Calificación:
  • 0 voto(s) - 0 Media
  • 1
  • 2
  • 3
  • 4
  • 5
¿Cómo calcular la profundidad de imagenes en pygame?
#1
Hola de nuevo Smile 

Tengo un pequeño código que simula una gravedad básica y muy simple. Al ejecutar el script nuestro personaje, un cuadrado azul, desciende hasta tocar el suelo. En este caso de color rojo. Cuando toca el suelo a veces aparece por detrás o por delante del mismo. Y pasa igual con la escalera, de color verde. Unas veces pasa por delante de la misma y otras por detrás. Diria que de forma aleatoria.

Al colisionar con la escalera podemos subir y bajar con las teclas del cursor. Si movemos a izquierda o derecha, caemos por la gravedad. Cuando tocamos el suelo no podemos subir si no tocamos la escalera. Algo lógico que he programado asi. Aunque, misteriosamente, nuestro personaje pega un saltito.

Las dos cosas que no me gustan es lo que comento:
- Que no hay una jerarquia clara de las profundidades de cada imagen.
- Que cuando colisionamos con el suelo, nuestro personaje, se queda hundido en el mismo. No sobre el suelo.

He usado las clases y los grupos de objetos. Pero por muchas combinaciones que hago, no resuelvo el problema de profundidad que cada imagen o superficie quiero que tenga.

He juntado las clases en el mismo script principal para que quede mas claro en el foro.


Código:
import pygame
import sys


#Inicializamos pygame
pygame.init()

# CLASES
class Personaje(pygame.sprite.Sprite):
   def __init__(self, x, y, ancho, alto, color):
       super().__init__()        
       self.image = pygame.Surface([ancho, alto])
       self.image.fill(color)
       self.rect = self.image.get_rect()
       self.rect.x = x
       self.rect.y = y
       self.velocidad = 8
       self.gravedad = 8
       self.parar = False    
   # Movimientos personaje
   def izquierda(self, pixels):
       self.rect.x -= self.velocidad    
   def derecha(self, pixels):
       self.rect.x += self.velocidad        
   def arriba(self, pixels):
       self.rect.y -= self.velocidad        
   def abajo(self, pixels):
       self.rect.y += self.velocidad        
   # Parar
   def stop(self):
       self.parar = True        
   # No parar
   def nonStop(self):
       self.parar = False              
   # Update
   def update(self):
       if self.parar:
           self.rect.y += 0
       else:
           self.rect.y += self.gravedad

class Decorado(pygame.sprite.Sprite):
   def __init__(self, x, y, ancho, alto, color):
       super().__init__()
       self.image = pygame.Surface((ancho, alto))
       self.image.fill(color)
       self.rect = self.image.get_rect()
       self.rect.x = x
       self.rect.y = y


NEGRO = (0, 0, 0)
BLANCO = (255, 255, 255)
VERDE = (0, 255, 0)
ROJO = (255, 0, 0)
AZUL = (0, 0, 255)
ventana = pygame.display.set_mode((1024, 720))
pygame.display.set_caption("Titulo de ventana")

# GRUPOS
objetosTotales = pygame.sprite.Group()
objetosSuelo = pygame.sprite.Group()
objetosEscalera = pygame.sprite.Group()

# OBJETOS
suelo = Decorado(0, 640, 1024, 32, ROJO)
escalera = Decorado(100, 300, 32, 400, VERDE)
jugador = Personaje(0, 0, 32, 32, AZUL)
monitor = Decorado(600, 10, 300, 50, BLANCO)

# GRUPOS DE OBJETOS
objetosSuelo.add(suelo)
objetosEscalera.add(escalera)
objetosTotales.add(suelo, escalera, jugador, monitor)

# TEXTO
fuente = pygame.font.SysFont('Consolas', 20)

#Bucle de "Juego"
reloj = pygame.time.Clock()
salir = False
cambio_y = 0
subir = False
while not salir:
   for event in pygame.event.get():
       if event.type == pygame.QUIT:
           salir = True    
   tecla = pygame.key.get_pressed()
   if tecla[pygame.K_LEFT]:
       jugador.izquierda(4)
   if tecla[pygame.K_RIGHT]:
       jugador.derecha(4)
   if tecla[pygame.K_UP] and subir == True:
       jugador.arriba(4)      
   if tecla[pygame.K_DOWN]:
       jugador.abajo(4)
           
   # LOGICA    
   colisiones =  pygame.sprite.spritecollide(jugador, objetosSuelo, False)
   for objeto in colisiones:              
       if jugador.rect.bottom >= objeto.rect.top:            
           jugador.rect.bottom = objeto.rect.top
           jugador.stop()    
   if pygame.sprite.spritecollideany(jugador, objetosEscalera):        
       subir = True
       jugador.stop()
   else:
       jugador.nonStop()      
   
   # UPDATE
   objetosTotales.update()    
   
   # DIBUJO DE PANTALLA
   ventana.fill(NEGRO)
   
   text1, text2 = 'jugador.rect.bottom: {}', 'suelo.rect.top: {}'
   coord_1 = fuente.render(text1.format(str(jugador.rect.bottom)), True, NEGRO)
   coord_2= fuente.render(text2.format(str(suelo.rect.top)), True, NEGRO)
   
   # DIBUJO DE SPRITES
   objetosTotales.draw(ventana)
   ventana.blit(coord_1, (600, 10))
   ventana.blit(coord_2, (600, 30))
       
   # ACTUALIZACION DE PANTALLA
   pygame.display.flip()
   reloj.tick(60)



pygame.quit()
sys.exit()

Un saludo y gracias!
Responder
#2
Hola, bienvenido.

Los sprites en PyGame ─y en la programación de videojuegos en general─ se superponen unos con otros según el orden en el que son dibujados; los últimos tapando a los primeros. Entonces, tu personaje debería ser lo último en dibujarse para que no aparezca por detrás de la escalera o de cualquier otro objeto. Para ello deberías usar un grupo de sprites ordenados (pygame.sprite.OrderedUpdates).

Saludos!
Responder
#3
(23-11-2018, 12:47 AM)Francisco escribió: Hola, bienvenido.

Los sprites en PyGame ─y en la programación de videojuegos en general─ se superponen unos con otros según el orden en el que son dibujados; los últimos tapando a los primeros. Entonces, tu personaje debería ser lo último en dibujarse para que no aparezca por detrás de la escalera o de cualquier otro objeto. Para ello deberías usar un grupo de sprites ordenados (pygame.sprite.OrderedUpdates).

Saludos!

Muchas gracias. Me ha funcionado muy bien. Me tocara repasar las funciones de esta libreria.

Por otra parte, pensaba que objetosTotales.update() se ponia despues de la logica del juego. O al menos asi lo veo en muchos sitios. Pero si lo hago de esta manera el cuadrado se queda por debajo del suelo 8 pixels (que es la gravedad del personaje). En cambio, si objetosTotales.update() se pone encima del codigo de la logica funciona bien. Es decir, el cuadrado se queda sobre el suelo.

De esta forma:

Código:
   objetosTotales.update()
   # LOGICA    
   colisiones =  pygame.sprite.spritecollide(jugador, objetosSuelo, False)
   for objeto in colisiones:              
       if jugador.rect.bottom > objeto.rect.top:            
           jugador.rect.bottom = objeto.rect.top
           jugador.stop()                
   if pygame.sprite.spritecollideany(jugador, objetosEscalera):        
       subir = True
       jugador.stop()
       
   else:
       jugador.nonStop()

Aunque quizas no sea lo correcto.
Responder
#4
De nada. En realidad no hay una forma canónica de hacerlo. update() puede ir antes o después con arreglo a las necesidades de tu código. Si poniéndolo al principio te funciona bien, no hay problema.

Saludos
Responder


Salto de foro:


Usuarios navegando en este tema: 1 invitado(s)