Razón Artificial

La ciencia y el arte de crear videojuegos

Pygame V: Moviendo Sprite

Escrito por adrigm el 14 de febrero de 2010 en Desarrollo Videojuegos, Noticias, Programación | 23 Comentarios.

En el siguiente tutorial aprenderemos a mover nuestra pelota por la ventana y hacer que rebote contra los bordes, Usaremos física y matemáticas básicas para mover la pelota.

El método actualizar

Para mover la pelota crearemos un metodo llamado actualizar dentro de la clase Bola que controle el movimiento de la pelota y si choca contra los bordes de la ventana, el método es el siguiente:

def actualizar(self, time):
	self.rect.centerx += self.speed[0] * time
	self.rect.centery += self.speed[1] * time
	if self.rect.left <= 0 or self.rect.right >= WIDTH:
		self.speed[0] = -self.speed[0]
		self.rect.centerx += self.speed[0] * time
	if self.rect.top <= 0 or self.rect.bottom >= HEIGHT:
		self.speed[1] = -self.speed[1]
		self.rect.centery += self.speed[1] * time

La linea 1 define el método, recibe el parámetro self (como siempre) y el parámetro time que es el tiempo transcurrido, más adelante lo explicamos.

La línea 2 y 3 usa la física básica de espacio es igual a la velocidad por el tiempo (e = v*t), por tanto establecemos que el centro de nuestro rectangulo en x es el valor que tenía (self.rect.centerx) más (+=) la valocidad a la que se mueve en el eje x (self.speed[0]) por (*) el tiempo transcurrido (time). Lo mismo se aplica al eje y en la línea 3.

La linea 4, 5 y 6 establece que si la parte izquierda del rectángulo de la bola es menor o igual a 0 ó mayor o igual a el ancho de la pantalla (WIDTH), es decir,  que este en el extremo izquierdo o derecho, la velocidad de x (self.speed[0]) cambie de signo (-self.speed[0]) con esto conseguiremos que vaya hacia el otro lado.

Las líneas 7, 8 y 9 es lo mismo pero en el eje y como se puede ver.

Creando un reloj

Ahora vamos a crear un reloj que controle el tiempo del juego, esto es importante para el movimiento, pues sabemos cuanto tiempo a pasado desde la ultima actualización de la pelota y con ello poder situarla en el espacio.

clock = pygame.time.Clock()

Esta línea va justo antes de entrar en el bucle del juego y sirve para crear el reloj con el que gestionar el tiempo.

Ahora necesitamos saber cuanto tiempo pasa cada vez que se ejecuta una interección del bucle, para ello dentro del bucle ponemos como primera línea:

time = clock.tick(60)

El 60 que se le pasa como parámetro es el framerate, con él nos aseguramos de que el juego no va a más de la velocidad estipulada. Con ello conseguimos que el juego corra igual en todos los ordenadores. Aquí más inftomación.

Por último debemos actualizar la posición de la bola antes de actualizarla en la ventana, es decir antes de los screen.blit.

bola.actualizar(time)

Como ves le pasamos el parametro time que es el tiempo que ha pasado desde la última vez que se ejecuto.

El juego queda así:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Módulos
import sys, pygame
from pygame.locals import *

# Constantes
WIDTH = 640
HEIGHT = 480

# Clases
# ---------------------------------------------------------------------

class Bola(pygame.sprite.Sprite):
	def __init__(self):
		pygame.sprite.Sprite.__init__(self)
		self.image = load_image("images/ball.png", True)
		self.rect = self.image.get_rect()
		self.rect.centerx = WIDTH / 2
		self.rect.centery = HEIGHT / 2
		self.speed = [0.5, -0.5]

	def actualizar(self, time):
		self.rect.centerx += self.speed[0] * time
		self.rect.centery += self.speed[1] * time
		if self.rect.left <= 0 or self.rect.right >= WIDTH:
			self.speed[0] = -self.speed[0]
			self.rect.centerx += self.speed[0] * time
		if self.rect.top <= 0 or self.rect.bottom >= HEIGHT:
			self.speed[1] = -self.speed[1]
			self.rect.centery += self.speed[1] * time

# ---------------------------------------------------------------------

# Funciones
# ---------------------------------------------------------------------

def load_image(filename, transparent=False):
        try: image = pygame.image.load(filename)
        except pygame.error, message:
                raise SystemExit, message
        image = image.convert()
        if transparent:
                color = image.get_at((0,0))
                image.set_colorkey(color, RLEACCEL)
        return image

# ---------------------------------------------------------------------

def main():
	screen = pygame.display.set_mode((WIDTH, HEIGHT))
	pygame.display.set_caption("Pruebas Pygame")

	background_image = load_image('images/fondo_pong.png')
	bola = Bola()

	clock = pygame.time.Clock()

	while True:
		time = clock.tick(60)
		for eventos in pygame.event.get():
			if eventos.type == QUIT:
				sys.exit(0)

		bola.actualizar(time)
		screen.blit(background_image, (0, 0))
		screen.blit(bola.image, bola.rect)
		pygame.display.flip()
	return 0

if __name__ == '__main__':
	pygame.init()
	main()

Ya nuestra pelota se mueve y rebota contra las paredes. En el próximo tutorial crearemos el Sprite pala y aprenderemos a moverlo con el teclado.

23 Comentarios en "Pygame V: Moviendo Sprite"

  1. Miguel dice:

    Primero de todo felicitarte por el gran trabajo. Yo ya soy un “veterano” de la informática y creo recordar que en los tiempos del ZXspectrum, cuando se trabajaba con Sprites, en lugar de dibujar el fondo (sería muy lento) y a continuación el “personaje”, lo que se hacía era:
    1- se guardaba la zona del fondo donde se va a “machacar” encima el personaje. Por ejemplo en la variable “temp”.
    2- se colocaba el personaje. Si este se movía:
    3- se colocaba “temp” en su lugar, borrando el personaje y dejando el fondo como estaba. Y se volvía a guardar en “temp” la siguiente zona donde se iba a colocar el personaje.
    4- se colocaba el personaje y así repetidamente.
    ¿Se puede hacer esto mismo en pygame o sería más lento?

  2. admin dice:

    Miguel, en esencia ahora es lo mismo, tu tienes el fondo guardado en un array y metes el personaje encima en una posición, luego si mueves el personaje, vuelves a recolocar el fondo y pintas al personaje encima.

    De hecho estoy haciendo ahora un motor de un rpg en pygame con ese estilo, pronto publicaré algo del tema en el blog.

  3. Kike Alonso dice:

    Muy agradecido por tu excelente tutorial. He aprendido en horas lo que no pude en días. Gracias por tu esfuerzo.
    Kike

  4. Heres grande !! xD
    bueno dejando las bromas se agradece mucho el esfuerzo, la verdad tengo unos pocos dias que empece a programar con python (pyton para los cuates xD) y ya medio lo voy entendiendo.

    por sierto, yo quiero trabajar tambien en un RPG pero tactico.
    Saludos

  5. PD: olvide mecionar, ya tengo unos añitos programando con C#, visual Net, Gambas y quiero aprender C o C++ para luego ahi hacer mis Engines

    Saludos

  6. Lucas dice:

    Exelente trabajo el de tu blog, dejame decirte que te sigo a diario, lamentablemente tuve un problema con esta parte del tutorial…cuando cargo el pygame ( que esta exactamente igual al que dejast en la pagina ) me salta el error invalid rect assigment en la linea self.rect.centery += self.speed[1] * time…supongo que es por se paso del limite ya que cuando cargo la pelota se va para abajo pero nose como corregirlo..el if de invertir la velocidad ta ahi

  7. Lucas dice:

    no me hagas caso, de boludo me deje una linea de codigo :P

  8. Andrés dice:

    Está muy interesante el tutorial, pero encontré que establecer la velocidad en X y en Y no es lo más apropiado, yo prefiero ajustar la velocidad solo en X y calcular Y con la ecuación de la recta:

    y = mx + n
    pero como la posición inicial de y es dada, hay que despejar n cada vez que se altera un valor ya sea m o la velocidad hay que recalcular n:
    n = y – mx

    el algoritmo me quedó así:

    class Bola(pygame.sprite.Sprite):
            def __init__(self):
                    pygame.sprite.Sprite.__init__(self)
                    self.image = load_image("images/ball.png", True)
                    self.rect = self.image.get_rect()
                    self.rect.centerx = WIDTH / 2
                    self.rect.centery = HEIGHT / 2
                    self.speed = 0.5
                    self.m = 1
                    self.n = self.rect.centery - self.rect.centerx * self.m
                    print self.n
    
            def actualizar(self, time):
                    self.rect.centerx += self.speed * time
                    self.rect.centery = self.rect.centerx * self.m + self.n
                    if self.rect.left <= 0 or self.rect.right >= WIDTH:
                            self.speed = -self.speed
                            self.m = -self.m
                            self.n = self.rect.centery - self.rect.centerx * self.m
                            self.rect.centerx += self.speed * time
                    if self.rect.top <= 0 or self.rect.bottom >= HEIGHT:
                            self.m = -self.m
                            self.n = self.rect.centery - self.rect.centerx * self.m
                            self.rect.centery = self.rect.centerx * self.m + 4
  9. adrigm dice:

    Andrés y mejor que eso es aplicarle física mas realista con aceleración, razamientos, puntos de aplicación de la fuerza, que hiciera que pudiera dársele efecto, etc.

    Pero, el tutorial trata de enseñar a aprender a usar la biblioteca Pygame y no tenía sentido.

    Tengo pensado escribir algunos artículos de física aplicada a videojuegos.

    De todas maneras, buen trabajo siempre es bueno que la gente tenga iniciativa y haga sus propias modificaciones.

  10. Andrés dice:

    Claro a nivel básico mi idea es que con conocimientos básicos de gráfica cartesiana puedas hacer cosas como modificar el ángulo de la trayectoria sabiendo que m es igual a la tangente de dicho ángulo en grados.

    Mi propósito es crear un juego estilo Arkanoid que solo modifica el ángulo de la trayectoria de la bola dependiendo del punto en el que se intercepta con la pala.

    Con un poco más de conocimeinto sobre el sistema de Descartes puedes conocer distintas funciones y gráficas, como la del seno, que haría que la bolita ande como en zig-zag, la de la circunferencia, la elipse o la parábola, con lo que puedes definir trayectorias interesantes como para usar en videojuegos estilo Mario.

    Lamentablemente me decanté por el área humanista, así que mis conocimientos más avanzados en la materia terminan en la trigonometría, que es lo que pasan en el colegio.

    Te felicito por los tutoriales.

  11. Aradenatorix dice:

    Hola:

    Muy bueno el tutorial, de hecho ya logré hacer un pong, pero si quisiera saber más de como aplicarle física a los videojuegos. Sin embargo algo que me intriga de momento, es como en el modo de juego darle algo de variedad.

    Estoy en un proyecto en el que estamos, con ayuda de pygames desde luego haciendo videojuegos que ayuden a rehabilitar a pacientes de al tercera edad para lo que hay que desarrollar opciones de juego que permitan al paciente hacer el juego asequible a sus capacidades motrices y habilidades propias sin insultarlo.

    Concretamente lo que necesito es saber como después de x puntos o x errores alargar (aumentar en la longitud) la paleta del pong para facilitarle el juego a la persona, ¿no se si me di a entender? Entiendo que eso tiene que ser dentro de las propiedades asignadas a la clase paleta o raqueta, pero exactamente no ´se como hacerlo.

    Saludos y felicidades, buen tutorial.

  12. adrigm dice:

    Aradenatorix, no es muy difícil de lograr, simplemente crea una pala mas grande y recarga el gráfico y obtén de nuevo el rect y listo.

  13. Aradenatorix dice:

    Mmm si pero bueno apenas estoy empezando en pygame así que no estoy muy seguro de como lograr eso, yo tenia mas bien la idea de poder usar algún atributo de tamaño en la clase pala para ello y que de acuerdo a la puntuación pudiera variar el tamaño del elemento. Por eso te pregunto, saludos.

  14. Raziel dice:

    Adrian, tengo la siguiente pregunta: en las líneas donde el objeto colisiona con los bordes, como en:

    self.speed[0] = -self.speed[0]

    no es mejor hacerlo:

    self.speed[0] *= -1

    o el que se haga como en el código tiene algún fin?

  15. adrigm dice:

    De las dos formas es válida, la cosa es cambiar la dirección de la velocidad, tu en realidad lo que estas haciendo es:

    self.speed[0] = self.speed[0] * -1
    

    y yo lo que estoy haciendo al fin y al cabo es:

    self.speed[0] = -1 * (self.speed[0])
    

    Así que al final es lo mismo, da igual como lo escribas.

  16. Carlos dice:

    Cuando lo ejecuto me anda lento =S es normal??

  17. Carlos dice:

    Estoy que uso eclipse como IDE y estoy q trabajo en una core i3 con 4 de ram

  18. Juan Quiroga dice:

    ya esta entendi!!!… Disculpa por la pregunta tan estupida, lo que pasa que habia cambiado las variables WIDTH como “alto” en mi codigo, y no me encuadraba la logica, eso me hacia interpretar que cuando el movimiento de derecha izquiera era mayor al alto cambiava el signo, pero era culpa mia jaja… solo fue un error, ya la cambie a “ancho” y la verdad entendi bien… jaja locuras mias y cosas que suceden… saludos muy bueno seguiré por los otros

  19. Piolin dice:

    mi consulta es .,., si es posible agregar lineas de codigo.,.,., para lograr el movimiento de mas de una pelotita que esten chokando con los bordes de la ventana.,.,¿cuales serian los cambios del programa?????.,.,., gracias Salud

  20. KazE dice:

    Genial los tutoriales.

    En la parte de Framerate dice “infotmacion”

    Saludos

  21. Gabriela Sofía dice:

    Y si necesito hacer que un juego se detenga porque el jugador ha perdido, y en su lugar mostrar una tabla de score…. ¿cómo puedo ayudarme?

  22. manuel dice:

    como hago para desplazarme de una plataforma a otra como mario que uno avanza a la derecha y se desplaza la pantalla con el personaje

Responder a KazE Cancelar respuesta