Rotar imágenes con pygame

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

Rotar imágenes con pygame

Notapor algarafa » Mar Jul 14, 2009 6:10 pm

Bueno, no se si se acuerdan de estos dos hilos que había creado hace tiempo:
http://www.losersjuegos.com.ar/foro/vie ... highlight=
http://www.losersjuegos.com.ar/foro/vie ... highlight=
Pues ahora estoy pasando este "minijuego" a POO para practicarla y asentar un poco los conocimientos que tengo, ponerle sonidos para ver que tal va y eso.
El caso es que tengo un meteorito rondando por la pantalla, que se supone que sera un obstáculo para la nave, y que si esta choca con el pues pierdes. El choque lo gestionaré con colliderect, que es más que suficiente para este programa. Lo de las colisiones está por hacer, aunque es un momento. Lo que ya tengo hehco es que el meteorito, cada vez que inicias el juego, aparezca en un lugar distinto y con una velocidad deistinta, pero le falta un detalle:
Que gire sobre si mismo. Ya había visto eeste efecto en el ejemplo de asteroids que hizo Hugo, pero es que no entendí nada de como hacerlo rotar.
¡Muchas gracias y les paso el código fuente que llevo!
Código: Seleccionar todo
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):
      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):
   
   def __init__(self, rock):
      pygame.sprite.Sprite.__init__(self)
      self.image, self.rect = load_image(rock)
      self.rect.centerx = random.randrange(0, SCREEN_WIDTH)
      self.rect.centery = random.randrange(0, SCREEN_HEIGHT)
      self.speed = [random.randrange(1, 3), random.randrange(1, 3)]
      
   def update(self):
      if self.rect.top <1>= SCREEN_HEIGHT + self.rect.height + 1:
         self.rect.top = -self.rect.height
      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]))
   
# ----------------------------------------------------------------------
   
def main():
   pygame.init()
   screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
   pygame.display.set_caption('Aterriza la nave')
   icon, icon_rect = load_image('icon.gif')
   pygame.display.set_icon(icon)
   
   background, background_rect = load_image('background.jpg')
   screen.blit(background, (0,0))
   
   nave = Nave()
   meteorito1 = Meteorito('big_rock1.gif')
   todos_sprites = pygame.sprite.RenderPlain((nave, meteorito1))
   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
      
      meteorito1.update()
      nave.mueve_nave()
      nave.gravedad(0.1)
      nave.update()
      
      screen.blit(background, (0,0))
      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

Notapor hugoruscitti » Mar Jul 14, 2009 11:00 pm

Bueno, para rotar los meteoritos necesitas dos cosas. Transformar
la imagen del meteorito para hacerla girar y luego corregir la
coordenada del meteorito porque sino girará sobre el punto
(0, 0), osea, la esquina superior izquierda...

La función que te permite transformar una imagen para rotarla
está documentada en la traducción de pygame:

http://www.losersjuegos.com.ar/traducci ... orm#rotate

solo ten en cuenta que la función no debería aplicarse sobre
las superficies resultantes una y otra vez, porque la
"rotación" suele hacer que se vean mal.


Ah, y para centrar la imagen tienes que cambiar el valor
del atributo "rect" del sprite, generalmente es útil tratarlo
usando "centerx" y "centery" como estás haciendo.

Saludos, y felicitaciones por el código.
Avatar de Usuario
hugoruscitti
Site Admin
 
Mensajes: 1242
Registrado: Dom Jul 30, 2006 3:57 am
Ubicación: Buenos Aires, Argentina

Meteorito amorfo

Notapor algarafa » Jue Jul 16, 2009 2:13 pm

He intentado hacerlo, pero el meteorito se ve amorfo, como decías, y además cuando sale de la pantalla, no vuelve a entrar y empieza a ir muy lento el programa. Te dejo la clase meteorito:
Código: Seleccionar todo
class Meteorito(pygame.sprite.Sprite):
   
   def __init__(self, rock):
      pygame.sprite.Sprite.__init__(self)
      self.image, self.rect = load_image(rock)
      self.rect.centerx = random.randrange(0, SCREEN_WIDTH)
      self.rect.centery = random.randrange(0, SCREEN_HEIGHT)
      self.speed = [random.randrange(-3, 3), random.randrange(-3, 3)]
      
   def rotar_meteorito(self, angle):
      self.image = pygame.transform.rotate(self.image, angle)
      self.rect = self.image.get_rect()
      
   def update(self):
      if self.rect.top <1>= SCREEN_HEIGHT + self.rect.height + 1:
         self.rect.top = -self.rect.height
      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]))

Y como llamo al método rotar_meteorito en el cuerpo de la función main:
Código: Seleccionar todo
....
nave = Nave()
   meteorito1 = Meteorito('big_rock1.gif')
   todos_sprites = pygame.sprite.RenderPlain((nave, meteorito1))
   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
      
      meteorito1.rotar_meteorito(-2)
      meteorito1.update()
      nave.mueve_nave()
      nave.gravedad(0.1)
      nave.update()
      
      screen.blit(background, (0,0))
      todos_sprites.draw(screen)
      pygame.display.flip()
...

¡Muchas gracias por la ayuda!
http://liberatumundo.wordpress.com/
Avatar de Usuario
algarafa
 
Mensajes: 135
Registrado: Lun Oct 27, 2008 6:12 pm
Ubicación: España

Notapor Juanxo » Jue Jul 16, 2009 2:57 pm

buenas rafa:Yo tampoco consigo que se quede rotando en el sitio, pero he conseguido acotarlo:

Lo que he hecho es:

1. Para que no se vea amorfo, no puedes rotar siempre sobre la misma imagen, como te dijo hugo, sino que en rotar_meteorito tendriías que hacer o siguiente:

Código: Seleccionar todo
def rotar_meteorito(self, angle):
      self.image = pygame.transform.rotate(self.base_image, angle)
      self.rect = self.image_rotada.get_rect()


Es decir, rotar siempre sobre la misma imagen original para que los desperfectos sean mínimos.

Para poder hacer esto, además, tendrías que ir cambiando el parametro angle de la función de rotación, porque si no siempre rotaría -2 grados ( o 358, como quieran) sobre la imagen original.

Para ello podrías crear un atributo que fuera self.rotacion en la clase, inicializado a 0, y cada llamada a rotar_meteorito, incrementar o decrementar una cantidad fija.

Código: Seleccionar todo
def rotar_meteorito(self):
      self.rotacion -= 2 %360
      self.image = pygame.transform.rotate(self.base_image, self.rotacion)
      self.rect = self.image.get_rect()


Este es un ejemplo sencillo que me he hecho para ver si conseguía rotar la imagen en el sitio(al final lo he conseguido, aunque un poco chapucero para mi gusto)

Código: Seleccionar todo
def main():

    pygame.display.init()
    screen = pygame.display.set_mode((640,480),DOUBLEBUF,32)

    image, rect = load_image("nave.png",True)
    rotation = 0
    image2 = image
    rect.move_ip(100,100)
   
    #guardamos el centro de la imagen original
    #para ajustar las posiciones de las imagenes rotadas
    centerx = rect.centerx
    centery = rect.centery

    while True:

        for event in pygame.event.get():
            if event.type == QUIT:
                exit()
            if event.type == KEYDOWN:
                #si hay algun evento, actualizamos el centro,
                #puesto que la imagen se va a mover
                centerx = rect.centerx
                centery = rect.centery
               
                if event.key == K_r:
                    rotation += 3
                    image2 = transform.rotate(image,rotation%360)
                    rect = image2.get_rect()

                    rect.centerx = centerx
                    rect.centery = centery
                if event.key == K_RIGHT:
                    rect.move_ip(3,0)
                elif event.key == K_LEFT:
                    rect.move_ip(-3,0)

                if event.key == K_UP:
                    rect.move_ip(0,-3)
                elif event.key == K_DOWN:
                    rect.move_ip(0,3)

        screen.fill((255, 255, 255))
        screen.blit(image2, rect)
        pygame.draw.rect(screen, (0,0,0),rect,1)     

        pygame.display.flip()

if __name__ == '__main__':
    main()


Espero que te haya servido de ayuda algo.
Un saludo
Juanxo
Avatar de Usuario
Juanxo
 
Mensajes: 437
Registrado: Sab Ene 31, 2009 2:34 am
Ubicación: Madrid(España)

Notapor Meldron » Jue Jul 16, 2009 9:38 pm

rafahack95 estuve viendo el codigo y solucioné el problema de que cuando salía el meteorito este no volvía a entrar(para la rotación usé lo que dijo Juanxo). Basicamente uso el rectángulo de la imagen base y voy actualizando el centro del mismo, y luego le paso los valores al rectangulo de la imagen rotada. Porque si usas el rect.center de la imagen que rota cada vez que lo rotas te lo manda al (0,0).( Esto último no lo sabía pero lo comprobé en el ejemplo)
Después verifico si rect.centerx es menor a cero en cuyo caso el meteorito debería aparecer del otro lado de la pantalla (siguiendo la idea del asteroids) y lo mismo para cuando rect.centerx es mayor al ancho de la pantalla. Luego, la misma idea para rect.centery.

Bueno aquí va el código (lo probé y funcionó):
Código: Seleccionar todo
# -*- encoding: utf-8 -*-
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', True)
      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):
      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):
   
   def __init__(self, rock):
      pygame.sprite.Sprite.__init__(self)
      self.image, self.rect = load_image(rock, True)
      self.base_image, self.rect1 = load_image(rock, True)
      self.rect1.centerx = random.randrange(0, SCREEN_WIDTH)
      self.rect1.centery = random.randrange(0, SCREEN_HEIGHT)
      self.speed = [random.randrange(-3, 3), random.randrange(-3, 3)]
      self.rotacion = 0
     
# Esto haría rebotar el meteorito por la pantalla...   
#   def update(self):
#      if self.rect1.top <0> SCREEN_HEIGHT:
#         self.speed[1] = -self.speed[1]
#      if self.rect1.left <0> SCREEN_WIDTH:
#         self.speed[0] = -self.speed[0]
#      self.rect1.move_ip((self.speed[0], self.speed[1]))
#      self.rect.center = self.rect1.center

   def update(self):
      if self.rect1.centerx <0> SCREEN_WIDTH:
         self.rect1.centerx = 0
      if self.rect1.centery > SCREEN_HEIGHT:
         self.rect1.centery = 0
      if self.rect1.centery < 0:
         self.rect1.centery = SCREEN_HEIGHT
      self.rect1.move_ip((self.speed[0], self.speed[1]))
      self.rect.center = self.rect1.center
   
   def rotar_meteorito(self):
      self.rotacion -= 2 %360
      self.image = pygame.transform.rotate(self.base_image, self.rotacion)
      self.rect = self.image.get_rect()
   
# ----------------------------------------------------------------------
   
def main():
   pygame.init()
   screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
   pygame.display.set_caption('Aterriza la nave')
   icon, icon_rect = load_image('icon.gif')
   pygame.display.set_icon(icon)
   
   background, background_rect = load_image('background.jpg')
   screen.blit(background, (0,0))
   
   nave = Nave()
   meteorito1 = Meteorito('big_rock1.gif')
   todos_sprites = pygame.sprite.RenderPlain((nave, meteorito1))
   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
     
      meteorito1.rotar_meteorito()
      meteorito1.update()
      nave.mueve_nave()
      nave.gravedad(0.1)
      nave.update()
     
      screen.blit(background, (0,0))
      todos_sprites.draw(screen)
      pygame.display.flip()
   
if __name__ == '__main__':   main()


La misma idea de desplazamiento se puede usar para la nave.
Ah una cosa que no entiendo es lo que utilizas en el programa como "gravedad". Si mal no recuerdo, en el asteroids, la nave se frenaba hasta quedarse quieta, siempre que no la estes moviendo. En tu caso se desacelera constantemente.
Espero haber ayudado en algo!
Saludos
PD:acabo de ver el titulo del programa y prestándole atención me di cuenta de que no es un asteriods, asi que ahora entiendo lo de la "gravedad". Perdón por el error jeje...
Avatar de Usuario
Meldron
 
Mensajes: 20
Registrado: Jue Jun 04, 2009 6:04 pm
Ubicación: Cap. Fed.- Argentina

Notapor Juanxo » Jue Jul 16, 2009 10:06 pm

buenas a todos:

Solo quería comentarte un par de detalles en cuanto al código rafahack, especialmente en cuanto a la organización.

Quizás no sean puntos fundamentales, pero te recomiendo que corrijas varias cosillas:

-En cuanto a la nave, fijate que en main haces:
Código: Seleccionar todo
nave.mueve_nave()
      nave.gravedad(0.1)
      nave.update()

¿Por qué no llamar a mueve_nave y gravedad dentro de update mejor?Así se cumpliría en mayor medida con la encapsulación, y te permitiría utilizar tu clase en otros juegos sin tener que mirar cuantas funciones tienes que llamar para la clase. Siempre llamarás a update y listo.

-En la función de carga de la imagen del icono, si no te hace falta el parametro rect, como creo yo, puedes obtener solo el primer parámetro de la siguiente manera:
Código: Seleccionar todo
icon_image = load_image('icon.gif',True) [0]


Y creo que eso es todo por el momento. Ya nos comentarás los progresos. Ánimo!
Avatar de Usuario
Juanxo
 
Mensajes: 437
Registrado: Sab Ene 31, 2009 2:34 am
Ubicación: Madrid(España)

¡Ya lo consegui!

Notapor algarafa » Vie Jul 17, 2009 7:29 pm

Ya consegui que rotara correctamente, gracias a los dos!!!!! :D
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 0 invitados

cron