Colisiones y explosiones (pygame)

Consulte acerca de programas, técnicas, algoritmos etc.

Colisiones y explosiones (pygame)

Notapor algarafa » Mar Jul 21, 2009 10:28 am

Siguiendo el ejemplo de invaders de Juanxo, he conseguido que la navecita estalle al chocar con un meteorito, pero e caso es que estalla aún sin tocarlo si quiera, y no solo desaparece la nave al chocar con los meteoritos, también desaparecen los meteoritos.
Les paso el código a ver si ven el error (tiene que estar en la funcion main(), lo digo para ahorraros trabajo).
Código: Seleccionar todo
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-
# ----------------------------------------------------------------------
# Nombre: Aterriza la nave
# Autor: Rafael Álvarez García (rafahack95@gmail.com)
# Descripción: Juego en el que hay que aterrizar una nave en una
# plataforma esquivando los meteoritos y con un combustible determinado.
# ----------------------------------------------------------------------

import pygame
from pygame.locals import *
import os
import random

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
DATOS_IMA = 'ima'

def load_image(name, colorkey = False):
   """ Carga una imagen, devuelve una superficie y su rectángulo"""
   
   fullname = os.path.join(DATOS_IMA, name)
   
   try: image = pygame.image.load(fullname)
   except pygame.error, message:
      print 'No se ha podido cargar la imagen', fullname
      raise SystemExit, message
      
   image = image.convert()
   if colorkey:
      colorkey = image.get_at((0,0))
      image.set_colorkey(colorkey, RLEACCEL)
   return image, image.get_rect()
   
class Nave(pygame.sprite.Sprite):
   """ Este objeto representa la nave que controla el jugador"""
   
   def __init__(self):
      pygame.sprite.Sprite.__init__(self)
      self.image, self.rect = load_image('nave.gif')
      self.rect.centerx = SCREEN_WIDTH / 2
      self.rect.centery = SCREEN_HEIGHT - 400
      self.speed = [0,0]
      
   def gravedad(self, gravedad):
      self.speed[1] = self.speed[1] + gravedad
      
   def mueve_nave(self):
      tecla = pygame.key.get_pressed()
      
      if tecla[K_UP]:
         self.speed[1] = self.speed[1] - 0.3
         
      if tecla[K_LEFT]:
         self.speed[0] = self.speed[0] - 0.3
         
      if tecla[K_RIGHT]:
         self.speed[0] = self.speed[0] + 0.3
      
   def update(self):
      # En este método, se llaman a todos los demás, para que en el
      # cuerpo del programa solo sea necesario llamar a update
      self.mueve_nave()
      self.gravedad(0.1)
      if self.rect.top <1>= SCREEN_HEIGHT:
         self.rect.bottom = SCREEN_HEIGHT
         self.speed[1] = 0
      if self.rect.left <1>= SCREEN_WIDTH + self.rect.width + 1:
         self.rect.left = -self.rect.width
      self.rect.move_ip((self.speed[0], self.speed[1]))
      
class Meteorito(pygame.sprite.Sprite):
   """ Este objeto, representa los meteoritos que el objeto Nave tendrá
      que esquivar"""
   
   def __init__(self, rock):
      pygame.sprite.Sprite.__init__(self)
      self.image_base, self.rect_base = load_image(rock)
      self.rect_base.centerx = random.randrange(0, SCREEN_WIDTH)
      self.rect_base.centery = random.randrange(0, SCREEN_HEIGHT)
      self.speed = [random.randrange(-3, 3), random.randrange(-3, 3)]
      self.image, self.rect = self.image_base, self.rect_base
      self.angle = 0
      self.angle_constante = random.randrange(-4, 4)
      
   def rotar_meteorito(self):
      self.angle = self.angle - self.angle_constante
      self.image = pygame.transform.rotate(self.image_base, self.angle)
      self.rect = self.image.get_rect()
      self.rect.centerx = self.rect_base.centerx
      self.rect.centery = self.rect_base.centery
      
   def update(self):
      # En este método, se llaman a todos los demás, para que en el
      # cuerpo del programa solo sea necesario llamar a update
      self.rotar_meteorito()
      if self.rect_base.top <1>= SCREEN_HEIGHT + self.rect_base.height + 1:
         self.rect_base.top = -self.rect_base.height
      if self.rect_base.left <1>= SCREEN_WIDTH + self.rect_base.width + 1:
         self.rect_base.left = -self.rect_base.width
      self.rect_base.move_ip((self.speed[0], self.speed[1]))
      
class Boom(pygame.sprite.Sprite):
    """Representa una explosion de alguna nave."""

    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.load_images()
        self.step = 0
        self.delay = 2
        (self.image, self.rect) = load_image('boom/1.png', True)
        self.rect.center = (x, y)

    def load_images(self):
        """Carga la lista 'self.frames' con todos los cuadros de animacion"""

        self.frames = []

        for n in range(1, 8):
            path = 'boom/%d.png'
            new_image = load_image(path % n, True)[0]
            self.frames.append(new_image)

    def update(self):
        self.image = self.frames[self.step]

        if self.delay <0> 6:
                self.kill()
        else:
            self.delay -= 1
   
# ----------------------------------------------------------------------
   
def main():
   pygame.init()
   screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
   pygame.display.set_caption('Aterriza la nave')
   icon = load_image('icon.gif') [0]
   pygame.display.set_icon(icon)
   
   background, background_rect = load_image('background.jpg')
   screen.blit(background, (0,0))
   
   nave = Nave()
   meteorito1 = Meteorito('big_rock1.gif')
   meteorito2 = Meteorito('medium_rock1.gif')
   todos_sprites = pygame.sprite.RenderClear((nave, meteorito1, meteorito2))
   meteoritos = pygame.sprite.Group(meteorito1, meteorito2)
   clock = pygame.time.Clock()
   
   while True:
      clock.tick(60)
      
      for event in pygame.event.get():
         if event.type == pygame.QUIT:
            raise SystemExit
         elif event.type == pygame.KEYDOWN:
            if event.key == K_ESCAPE:
               raise SystemExit
               
      # Gestionamos la explosión de la nave
      if pygame.sprite.spritecollide(nave, meteoritos, 1):
         (x, y) = nave.rect.center
         todos_sprites.add(Boom(x, y))
         nave.kill()
      
      todos_sprites.update()
      todos_sprites.clear(screen, background)
      todos_sprites.draw(screen)
      pygame.display.flip()
   
if __name__ == '__main__':   main()

Saludos y gracias!!! :wink:
http://liberatumundo.wordpress.com/
Avatar de Usuario
algarafa
 
Mensajes: 135
Registrado: Lun Oct 27, 2008 6:12 pm
Ubicación: España

Notapor Juanxo » Mar Jul 21, 2009 10:44 am

En efecto, tenías razón el problema de las colisiones está en main:

Código: Seleccionar todo
if pygame.sprite.spritecollide(nave, meteoritos, 1):

El último parámetro(do_kill), hace que, en caso de ponerse a uno, todo sprite del grupo(en este caso meteoritos) que choque con nave desaparezca. Ponlo a 0 y dentro haz:
Código: Seleccionar todo
kill nave


El tema de que exploten sin tocarse es algo que creo que te comente en el post de la rotación del meteorito. Al rotar la imagen, el rect de la misma se tiene que ir ajustando( por que pygame no soporta rects rotados, por lo que el rect del meteorito aumenta, haciendo que choque antes de haber colisionado las imagenes.

Para evitar esto, podrías tratar las colisiones en vez de con rects, con circle, haciendo un circulo que rodee a la nave y otro que rodee al meteorito, los cuales no cambiarían nunca al rotar el meteorito(creo). Sería algo así:
Código: Seleccionar todo
for meteorito in grupo_meteoritos:
   if pygame.sprite.collide_circle(nave, meteorito):
       kill nave
       reiniciar(......)
       break


Para poder utilizar esta función, es obligatorio que el Sprite tenga un rect (self.rect) y opcional que tenga un radius(self.radius), que es el radio de colisión.
En caso de no tener un radius, se cogerá un circulo capaz de englobar al rect entero, por lo que estaríamos peor que antes.

Espero haberte ayudado en algo.
Avatar de Usuario
Juanxo
 
Mensajes: 437
Registrado: Sab Ene 31, 2009 2:34 am
Ubicación: Madrid(España)

Muchas gracias

Notapor algarafa » Dom Jul 26, 2009 6:55 pm

Muchas gracias por tu ayuda Juanxo, siento haber tardado en probarlo, pero es que me daba pereza :D Me tira un error:
Código: Seleccionar todo
rafael@rafael:~$ cd Desktop/Aterriza\ la\ nave
rafael@rafael:~/Desktop/Aterriza la nave$ ./Aterriza\ la\ nave.py
Traceback (most recent call last):
  File "./Aterriza la nave.py", line 188, in <module>
    if __name__ == '__main__':   main()
  File "./Aterriza la nave.py", line 178, in main
    if pygame.sprite.collide_circle(nave, meteorito):
AttributeError: 'module' object has no attribute 'collide_circle'
rafael@rafael:~/Desktop/Aterriza la nave$



Te copio el código por si acaso hice algo mal:
Código: Seleccionar todo
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-
# ----------------------------------------------------------------------
# Nombre: Aterriza la nave
# Autor: Rafael Álvarez García (rafahack95@gmail.com)
# Descripción: Juego en el que hay que aterrizar una nave en una
# plataforma esquivando los meteoritos y con un combustible determinado.
# ----------------------------------------------------------------------

import pygame
from pygame.locals import *
import os
import random

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
DATOS_IMA = 'ima'

def load_image(name, colorkey = False):
   """ Carga una imagen, devuelve una superficie y su rectángulo"""
   
   fullname = os.path.join(DATOS_IMA, name)
   
   try: image = pygame.image.load(fullname)
   except pygame.error, message:
      print 'No se ha podido cargar la imagen', fullname
      raise SystemExit, message
      
   image = image.convert()
   if colorkey:
      colorkey = image.get_at((0,0))
      image.set_colorkey(colorkey, RLEACCEL)
   return image, image.get_rect()
   
class Nave(pygame.sprite.Sprite):
   """ Este objeto representa la nave que controla el jugador"""
   
   def __init__(self):
      pygame.sprite.Sprite.__init__(self)
      self.image, self.rect = load_image('nave.gif')
      self.rect.centerx = SCREEN_WIDTH / 2
      self.rect.centery = SCREEN_HEIGHT - 400
      self.speed = [0,0]
      self.radius = self.rect.size[0]
      
   def gravedad(self, gravedad):
      self.speed[1] = self.speed[1] + gravedad
      
   def mueve_nave(self):
      tecla = pygame.key.get_pressed()
      
      if tecla[K_UP]:
         self.speed[1] = self.speed[1] - 0.3
         
      if tecla[K_LEFT]:
         self.speed[0] = self.speed[0] - 0.3
         
      if tecla[K_RIGHT]:
         self.speed[0] = self.speed[0] + 0.3
      
   def update(self):
      # En este método, se llaman a todos los demás, para que en el
      # cuerpo del programa solo sea necesario llamar a update
      self.mueve_nave()
      self.gravedad(0.1)
      if self.rect.top <1>= SCREEN_HEIGHT:
         self.rect.bottom = SCREEN_HEIGHT
         self.speed[1] = 0
      if self.rect.left <1>= SCREEN_WIDTH + self.rect.width + 1:
         self.rect.left = -self.rect.width
      self.rect.move_ip((self.speed[0], self.speed[1]))
      
class Meteorito(pygame.sprite.Sprite):
   """ Este objeto, representa los meteoritos que el objeto Nave tendrá
      que esquivar"""
   
   def __init__(self, rock):
      pygame.sprite.Sprite.__init__(self)
      self.image_base, self.rect_base = load_image(rock)
      self.rect_base.centerx = random.randrange(0, SCREEN_WIDTH)
      self.rect_base.centery = random.randrange(0, SCREEN_HEIGHT)
      self.speed = [random.randrange(-3, 3), random.randrange(-3, 3)]
      self.image, self.rect = self.image_base, self.rect_base
      self.angle = 0
      self.angle_constante = random.randrange(-4, 4)
      self.radius = self.rect.size[0]
      
   def rotar_meteorito(self):
      self.angle = self.angle - self.angle_constante
      self.image = pygame.transform.rotate(self.image_base, self.angle)
      self.rect = self.image.get_rect()
      self.rect.centerx = self.rect_base.centerx
      self.rect.centery = self.rect_base.centery
      
   def update(self):
      # En este método, se llaman a todos los demás, para que en el
      # cuerpo del programa solo sea necesario llamar a update
      self.rotar_meteorito()
      if self.rect_base.top <1>= SCREEN_HEIGHT + self.rect_base.height + 1:
         self.rect_base.top = -self.rect_base.height
      if self.rect_base.left <1>= SCREEN_WIDTH + self.rect_base.width + 1:
         self.rect_base.left = -self.rect_base.width
      self.rect_base.move_ip((self.speed[0], self.speed[1]))
      
class Boom(pygame.sprite.Sprite):
    """Representa una explosion de alguna nave."""

    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.load_images()
        self.step = 0
        self.delay = 2
        (self.image, self.rect) = load_image('boom/1.png', True)
        self.rect.center = (x, y)

    def load_images(self):
        """Carga la lista 'self.frames' con todos los cuadros de animacion"""

        self.frames = []

        for n in range(1, 8):
            path = 'boom/%d.png'
            new_image = load_image(path % n, True)[0]
            self.frames.append(new_image)

    def update(self):
        self.image = self.frames[self.step]

        if self.delay <0> 6:
                self.kill()
        else:
            self.delay -= 1
   
# ----------------------------------------------------------------------
   
def main():
   pygame.init()
   screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
   pygame.display.set_caption('Aterriza la nave')
   icon = load_image('icon.gif') [0]
   pygame.display.set_icon(icon)
   
   background, background_rect = load_image('background.jpg')
   screen.blit(background, (0,0))
   
   nave = Nave()
   meteorito1 = Meteorito('big_rock1.gif')
   meteorito2 = Meteorito('medium_rock1.gif')
   todos_sprites = pygame.sprite.RenderClear((nave, meteorito1, meteorito2))
   meteoritos = pygame.sprite.Group(meteorito1, meteorito2)
   clock = pygame.time.Clock()
   
   while True:
      clock.tick(60)
      
      for event in pygame.event.get():
         if event.type == pygame.QUIT:
            raise SystemExit
         elif event.type == pygame.KEYDOWN:
            if event.key == K_ESCAPE:
               raise SystemExit
               
      # Gestionamos la explosión de la nave
      for meteorito in meteoritos:
         if pygame.sprite.collide_circle(nave, meteorito):
            (x, y) = nave.rect.center
            todos_sprites.add(Boom(x, y))
            nave.kill()
      
      todos_sprites.update()
      todos_sprites.clear(screen, background)
      todos_sprites.draw(screen)
      pygame.display.flip()
   
if __name__ == '__main__':   main()
http://liberatumundo.wordpress.com/
Avatar de Usuario
algarafa
 
Mensajes: 135
Registrado: Lun Oct 27, 2008 6:12 pm
Ubicación: España

Re: Muchas gracias

Notapor endaramiz » Mar Jul 28, 2009 4:31 pm

rafahack95 escribió:Me tira un error:
Código: Seleccionar todo
    if pygame.sprite.collide_circle(nave, meteorito):
AttributeError: 'module' object has no attribute 'collide_circle'

¿Que versión tienes? Esa función fue añadida a partir de la versión 1.8.1. Puedes saber tu versión de Pygame poniendo en la consola:
Código: Seleccionar todo
python
import pygame
pygame.version.ver


Saludos.
Avatar de Usuario
endaramiz
 
Mensajes: 283
Registrado: Vie Ago 31, 2007 9:25 am
Ubicación: Barcelona

Versión

Notapor algarafa » Vie Jul 31, 2009 9:35 am

Código: Seleccionar todo
>>> pygame.version.ver
'1.7.1release'

Pues tenía la de los repos testing de Debian, toca actualizar, gracias.
http://liberatumundo.wordpress.com/
Avatar de Usuario
algarafa
 
Mensajes: 135
Registrado: Lun Oct 27, 2008 6:12 pm
Ubicación: España

Me sigue explotando antes de tiempo

Notapor algarafa » Mar Ago 25, 2009 9:57 am

Hola, perón por tardar en postear, pero es que estuve de vacaciones en alemania. El caso es que he hecho todo lo que se me ha dicho en el post, y sigue funcionando mal. Para no hacer el post eterno, mejor dejo un link de descarga ya con imágenes y código fuente.
http://www.gigasize.com/get.php?d=1rx3ljn58pf
¡Saludos!
http://liberatumundo.wordpress.com/
Avatar de Usuario
algarafa
 
Mensajes: 135
Registrado: Lun Oct 27, 2008 6:12 pm
Ubicación: España


Volver a General

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado

cron