Pygame 4 , Sprites, Muy importante.

Hola a tod@s, como os prometí, voy a seguir escribiendo sobre Pygame, esta vez vamos a ver una clase muy pero que muy importante llamada sprite.



Hasta ahora todos los objetos los habíamos creado simplemente cargando la imagen y posicionándola en pantalla, tanto el fondo como el pingüino, pero, evidente mente, el pingüino y el fondo son elementos jerárquicos muy diferentes, ya que el pingüino en algún momento del juego puede interactuar con el medio: colisionar, saltar, morir... mientras que el fondo es una imagen fija y sin vida que siempre va a ser así (al menos en la mayoría de los juegos).




IMPORTANTE: Es imprescindible haber leído con anterioridad los tutoriales sobre la POO para poder entender este artículo:




Así que el pingüino lo crearemos como un sprite, un sprite es un objeto capaz de interactuar con el medio, ya que nos proporciona métodos de movimiento, colisión...

Para ir refrescando la memoria, así teníamos el código en el anterior post:

#!/usr/bin/python


#importamos modulos
import sys
import pygame
from pygame.locals import *

#definimos constantes

altura=640
anchura=480



def main():

 #variables locales

 x=550
 y=220
 speed=1

 #iniciamos pygame
 pygame.init()
 #creamos una ventana
 ventana=pygame.display.set_mode((altura, anchura))
 #definimos un titulo para la ventana
 pygame.display.set_caption("probando pygame...")

 #cargamos imagenes
 fondo=pygame.image.load("fondo.jpg").convert()
 tux=pygame.image.load("tux.png").convert_alpha()
 
 
 #loop del juego
 while True:
  teclado=pygame.key.get_pressed()
  if teclado[K_RIGHT]:
   x+=speed

  if teclado[K_LEFT]:
   x-=speed
  if teclado[K_UP]:
   y-=speed
  if teclado[K_DOWN]:
   y+=speed
  
  ventana.blit(fondo, (0, 0))
  ventana.blit(tux, (x, y))
  pygame.display.flip()
  #posibles entradas del teclado
  for eventos in pygame.event.get():
   if eventos.type==pygame.QUIT:
    sys.exit()




if __name__=="__main__":
 main()


Para crear un sprite, se usa la clase sprite.Sprite(), pero no se usa como anteriormente hemos usado todos los métodos en python, sino que aquí esta clase deberá de ser heredada por otra que nosotros creemos anteriormente, para facilitaros el trabajo, ya lo he hecho yo por vosotr@s:




class objeto(pygame.sprite.Sprite):
  def __init__(self, foto):
   pygame.sprite.Sprite.__init__(self)
          self.image = pygame.image.load(foto).convert_alpha()
          self.rect = self.image.get_rect()
          self.speed = [velo, velo]
  def update(self):
   teclado=pygame.key.get_pressed()

   if teclado[K_RIGHT]:
    pinguino.rect.x+=velo
   if teclado[K_LEFT]:
    pinguino.rect.x-=velo
   if teclado[K_UP]:
    pinguino.rect.y-=velo
   if teclado[K_DOWN]:
    pinguino.rect.y+=velo
   ventana.blit(self.image, (self.rect.x, self.rect.y))

Como veis creamos una clase cualquiera pero que debe de heredar de la clase "pygame.sprite.Sprite()", después en el constructor de nuestra clase, debemos de llamar a el constructor de la clase sprite con:


pygame.sprite.Sprite.__init__(self)

Así obtenemos todos los métodos que nos brinda la clase Sprite, también en la clase que yo he creado; los valores del sprite también se especifican en el constructor siento estos:

self.image: La imagen a cargar

self.rect: Obtiene el rectángulo invisible que rodea la foto, para después poder gestionar colisiones...

self.speed: La velocidad de movimiento del sprite


Después para crear un sprite a través de esta clase, simplemente:


pingüino=objeto("tux.png")


Ahora pasamos a analizar el método update de la clase objeto, anteriormente, toda la lógica del pingüino se encontraba en el loop principal, pero claro, si tenemos 100 objetos, imaginaros como se nos quedaría el loop de largo  complejo con todas las condiciones que almacenaría, por lo cual en los sprites la inteligencia de estos se crean en el método especial update que aparte de la lógica que nosotros definamos también se encarga de gestionar el sprite, este método update se debe de ejecutar en el loop del programa con un simple:

pinguino.update()


Práctica mente el código que está dentro del update no cambia respecto al código del anterior post donde gestionamos las pulsaciones del teclado, solo que aquí en vez de jugar directa mente con las posiciones de la imagen "cruda", jugamos con las posiciones del rectángulo que lo contiene a través de los métodos "rect.x" y "rect.y".

Así que si adaptamos el código del anterior post con estas nuevas modificaciones, se nos quedaría algo así:

#!/usr/bin/python


#importamos modulos
import sys
import pygame
from pygame.locals import *

#definimos constantes

altura=640
anchura=480



def main():

 #variables locales

 x=550
 y=220
 velo=3

 #iniciamos pygame
 pygame.init()
 #creamos una ventana
 ventana=pygame.display.set_mode((altura, anchura))
 #definimos un titulo para la ventana
 pygame.display.set_caption("probando pygame...")
 #cargar imagenes
 fondo=pygame.image.load("fondo.jpg")
 

 class objeto(pygame.sprite.Sprite):
  def __init__(self, foto):
   pygame.sprite.Sprite.__init__(self)
          self.image = pygame.image.load(foto).convert_alpha()
          self.rect = self.image.get_rect()
          self.speed = [velo, velo]
  def update(self):
   teclado=pygame.key.get_pressed()

   if teclado[K_RIGHT]:
    pinguino.rect.x+=velo
   if teclado[K_LEFT]:
    pinguino.rect.x-=velo
   if teclado[K_UP]:
    pinguino.rect.y-=velo
   if teclado[K_DOWN]:
    pinguino.rect.y+=velo
   ventana.blit(self.image, (self.rect.x, self.rect.y))



 pinguino=objeto("tux.png")
#loop del juego
 while True:
  
  pygame.display.flip()
  ventana.blit(fondo, (0,0))
  pinguino.update()
  

  #posibles entradas del teclado
  for eventos in pygame.event.get():
   if eventos.type==pygame.QUIT:
    sys.exit()




if __name__=="__main__":
 main()


Si ejecutáis este último código obtendréis los mismos resultados que en el anterior post, pero en este ejemplo, el pingüino es un sprite no una foto normal y corriente, permitiendo que este interactue con el medio mediante métodos de la sprite.Sprite() como las colisiones.

Al principio puede resultar un poco complejo, pero si se lee bien esto y todos los anteriores posts sobre orientación a objetos, resulta llevadero.

Un abrazo a tod@s y hasta el siguiente artículo.

PD: Si el código os da error, copiarlo a mano ya que si no os puede tirar la excepción: "IndentationError: unexpected indent"

11 comentarios:

Anónimo dijo...

Espero que difundas todo tu esfuerzo ! Y en el futuro poder ver un blog lleno de vida.

Un abrazo!

Francisco Dominguez Lerma dijo...

Gracias, y si, ya me gustaría que aquí hubiera mucha actividad, pero de momento nada.

Un abrazo.

Dan dijo...

yo esto no lo voy a usar
para hacer un juego sino para
otras cosas,
echale un vistazo a raspberry pi.

Dan dijo...

Francisco , como puedo hacer un
programa en Tkinter y ejecutarlo en un archivo .py o lo que sea.
GRACIAS

Francisco Dominguez Lerma dijo...

Dan:

Pues es muy sencillo, colocando al final lo siguiente:

el_nombre_de_la_ventana.mainloop()

por ejemplo

ventana.mainloop()

Un Abrazo.

Dan dijo...

Muchisimas gracias.

Dan dijo...

Francisco tengo un programa que no va:
¿podrias decirme el fallo?:
Codigo:


from Tkinter import*
ventana=Tk()
titulo = Label(ventana, text="Text Editor 1.0", fg="brown")
titulo.pack()

def a():
exit()

def b():
file=open("a.txt", "a")
file.write(texto)
file.write(texto2)
file.write(texto3)
file.write(texto4)
file.write(texto5)
file.write(texto6)
file.write(texto7)
file.write(texto8)
file.write(texto9)
file.write(texto10)
file.write(texto11)
file.write(texto12)
file.write(texto13)
file.write(texto14)
file.write(texto15)
#para cerrar el archivo
file.close()

f1=Frame(ventana)
b1=Button (f1, text="Guardar", bg="blue", command=b)
b2=Button (f1, text="Salir", bg="red", command=a)

b1.pack(side="left")
b2.pack(side="left")

f1.pack()
f2=Frame(ventana)
texto=Entry(f2, bg="white")
texto.pack()
f2.pack()
ayuda = Label(f2, text="Cada cuadro representa una linea", fg="purple")
texto2 = Entry(f2, bg="white")
texto2.pack()
texto3 = Entry(f2, bg="white")
texto3.pack()
texto4 = Entry(f2, bg="white")
texto4.pack()
texto5 = Entry(f2, bg="white")
texto5.pack()
texto6 = Entry(f2, bg="white")
texto6.pack()
texto7 = Entry(f2, bg="white")
texto7.pack()
texto8 = Entry(f2, bg="white")
texto8.pack()
texto9 = Entry(f2, bg="white")
texto9.pack()
texto10 = Entry(f2, bg="white")
texto10.pack()
texto11 = Entry(f2, bg="white")
texto11.pack()
texto12 = Entry(f2, bg="white")
texto12.pack()
texto13 = Entry(f2, bg="white")
texto13.pack()
texto14 = Entry(f2, bg="white")
texto14.pack()
texto15 = Entry(f2, bg="white")
texto15.pack()
ayuda.pack()
dire = Label(f2, text="Write the file directory:", fg="brown")
dire.pack()
ventana.mainloop()

Francisco Dominguez Lerma dijo...

Dan, antes de nada, cuando tengas alguna duda con algún Script que no te funcione, lo primero que debes aportar es la excepción (el error) que te lanza el intérprete de Python, ya que suelen ser muy precisos y de gran ayuda.

Un saludo.

Dan dijo...

Francisco, como puedo hacer que un
programa redirija a otro,
es decir que al pulsar un boton se
ejecute una función que ejecute
otro archivo .py
GRACIAS

Francisco Dominguez Lerma dijo...

Daniel, la filosofía de Python no es que un archivo python pueda ejecutar otro, sino que el archivo .py "Principal" aproveche funciones de otros scripts, por ejemplo si el otro archivo se llama "operaciones.py" y tiene dos funciones, un "suma" y otra "resta"

import operaciones
operaciones.suma()

¿Entiendes lo que quiero decir? No sería buena práctica intentar abrir un archivo .py desde otro archivo .py, iría en contra de la filosofía de python.

Un saludo.

Dan dijo...

Esto es a lo que me referia.
GRACIAS