Migración desde Scratch¶
Este tutorial comparará una implementación de Flappy Bird escrita en Scratch con una escrita en Pygame Zero. Los programas de Scratch y Pygame Zero son similares en gran medida.
La Versión de Pygame Zero se puede encontrar en el repositorio de Pygame Zero.
También puedes descargar la Versión de Scratch desde el repositorio.
La versión Pygame Zero incluye la lógica de puntuación, que se omite en el código en los ejemplos de código de esta página para hacer una comparación más cercana.
El código de Python que se muestra a continuación está reordenado para mayor claridad en los ejemplos.
El escenario¶
Así es como se presenta el escenario en nuestro programa de Scratch:
Sólo hay tres objetos, aparte del fondo: el pájaro, y las tuberías superior y la parte inferior.
Esto corresponde al código de Pygame Zero que establece estos objetos como
Actores
:
bird = Actor('bird1', (75, 200))
pipe_top = Actor('top', anchor=('left', 'bottom'))
pipe_bottom = Actor('bottom', anchor=('left', 'top'))
En Pygame Zero también tenemos que asegurarnos de dibujar estos objetos. En principio esto da un poco más de flexibilidad sobre cómo dibujar la escena:
def draw():
screen.blit('background', (0, 0))
pipe_top.draw()
pipe_bottom.draw()
bird.draw()
Movimiento de la tubería¶
Las tuberías se mueven a un ritmo constante independientemente del pájaro. Cuando se mueven fuera de izquierda de la pantalla, hacen un bucle hacia la derecha, y su posición posición vertical se mueve al azar.
En Scratch esto se puede conseguir creando dos scripts diferentes para la tubería superior y el inferior.
Para resumir lo que está sucediendo aquí:
La condición
x position < -240
es verdadera cuando una tubería está fuera del lado izquierdo lado izquierdo de la pantalla, y este es el disparador para restablecer las tuberías.La variable
pipe_height
se utiliza para coordinar las dos tuberías. Debido a que el entre ellos debe ser la misma, no podemos elegir ambas alturas al azar. Por eso uno de los scripts tiene esta lógica y el otro no.El
set y position to pipe height +/- 230
establece que una de las tuberías esté por encima de y la otra por debajo depipe_height
.
Este código es mucho más sencillo en Pygame Zero. Podríamos escribir una única función que actualice ambas tuberías. De hecho lo he dividido de otra manera para que quede claro que las acciones de reinicio van juntas:
import random
WIDTH = 400
HEIGHT = 708
GAP = 130
SPEED = 3
def reset_pipes():
pipe_gap_y = random.randint(200, HEIGHT - 200)
pipe_top.pos = (WIDTH, pipe_gap_y - GAP // 2)
pipe_bottom.pos = (WIDTH, pipe_gap_y + GAP // 2)
def update_pipes():
pipe_top.left -= SPEED
pipe_bottom.left -= SPEED
if pipe_top.right < 0:
reset_pipes()
Una pequeña diferencia aquí es que puedo extraer valores que quiero reutilizar como
«constantes», escritas en MAYÚSCULAS. Esto me permite cambiarlos en un solo lugar cuando
quiero ajustar el juego. Por ejemplo, en el código anterior, podría ampliar o reducir
el espacio entre las dos tuberías simplemente cambiando GAP
.
La mayor diferencia es que no hay un bucle forever
en el código de Python.
de Python. Esta es la gran diferencia entre Scratch y la mayoría de los lenguajes de
basados en texto: debes actualizar el juego en un paso de animación y luego
regresar. El retorno le da a Pygame Zero la oportunidad de hacer cosas como procesar
entrada o redibujar la pantalla. Un bucle eterno y el juego se quedaría ahí,
por lo que cualquier bucle debe terminar rápidamente.
Pygame Zero llama a la función update()
cuando quiere actualizar la
animación en un paso, así que sólo necesitamos una llamada a update_walls()
:
def update():
update_walls()
El pájaro¶
Los patrones descritos anteriormente para la traducción de la lógica de Scratch al código de Python también se aplican a la lógica del pájaro. Veamos primero el código Python esta vez.
El código para actualizar el pájaro está organizado en una función llamada
actualizar_pájaro()
. Lo primero que contiene esta función es un código para mover
el pájaro según la gravedad:
GRAVEDAD = 0.3
# Estado inicial del pájaro
bird.dead = False
bird.vy = 0
def actualizar_pájaro():
uy = bird.vy
bird.vy += GRAVEDAD
bird.y += bird.vy
bird.x = 75
Esta es una simple fórmula de gravedad:
La gravedad significa una aceleración constante hacia abajo.
La aceleración es un cambio en la velocidad.
La velocidad es un cambio en la posición.
Para representar esto necesitamos seguir una variable pájaro.vy
, que es la velocidad del pájaro
en la dirección y
. Esta es una nueva variable que estamos definiendo,
no algo que Pygame Zero nos proporciona.
La gravedad significa una aceleración constante hacia abajo:
GRAVITY
es mayor que 0.La aceleración es un cambio en la velocidad:
GRAVITY
se añade abird.vy
.La velocidad es el cambio de posición:
pájaro.vy
se suma apájaro.y
.
Observa que el pájaro no se mueve horizontalmente. Su posición x
se mantiene en
75 durante todo el juego. Simulamos el movimiento moviendo las tuberías hacia
hacia él. Esto parece como si fuera una cámara en movimiento siguiendo al pájaro. Así que no hay
no hay necesidad de una variable vx
en este juego.
La siguiente sección hace que el pájaro agite sus alas:
if not bird.dead:
si bird.vy < -3
bird.image = 'bird2'
si no:
bird.image = 'bird1'
Esto comprueba si el pájaro se mueve hacia arriba o hacia abajo. Mostramos la imagen pájaro2
si se está moviendo rápidamente hacia arriba y la imagen pájaro1
.
si se mueve rápidamente hacia arriba y la imagen de pájaro1
en caso contrario. (-3 fue
(-3 fue elegido por ensayo y error para que esto parezca convincente).
La siguiente sección comprueba si el pájaro ha colisionado con una pared:
si bird.colliderect(pipe_top) o bird.colliderect(pipe_bottom)
bird.dead = True
bird.image = 'birddead'
Si es así establecemos pájaro.muerto
a Verdadero
. Este es un valor booleano que significa que
significa que es True
o False
. Podemos usar esto para comprobar fácilmente si el pájaro está
vivo. Si no está vivo, no responderá a la entrada del jugador.
Y la sección final comprueba si el pájaro se ha caído de la parte inferior (o superior) de la pantalla del juego. Si es así, reinicia el pájaro:
si no 0 < bird.y < 720:
bird.y = 200
bird.dead = False
bird.vy = 0
reset_pipes()
¿Qué hace reset_pipes()
ahí? Porque he organizado mi código de tuberías para
para que sea una función separada, puedo llamarla cada vez que quiera restablecer mis paredes.
En este caso, hace que el juego sea mejor porque da al jugador la oportunidad de
reaccionar cuando el pájaro se mueve de nuevo a su posición inicial.
De nuevo, esto necesita ser llamado en cada frame, así que lo añadimos a update()
:
def update():
update_walls()
update_bird()
La última parte de la lógica del pájaro es que tiene que responder al control del jugador.
Cuando pulsamos una tecla, el pájaro aletea hacia arriba. Pygame Zero llamará a
función on_key_down()
- si has definido una - siempre que se pulse una tecla
tecla:
FLAP_VELOCIDAD = -6.5
def on_key_down():
if not bird.dead:
bird.vy = FLAP_VELOCITY
Aquí, si el pájaro no está muerto, ponemos su vy
a un número negativo: en
Pygame Zero esto significa que empieza a moverse hacia arriba.
Deberías encontrar muchos paralelismos entre el código de Python y este Código de Scratch:
Las mayores diferencias entre Scratch y Pygame Zero son estas:
No se puede hacer un bucle eterno en Pygame Zero - sólo se actualiza durante un fotograma y luego regresa.
Las coordenadas son diferentes. En Pygame Zero, la parte superior izquierda de la pantalla es
x = 0, y = 0
. La direcciónx
va de izquierda a derecha como antes, pero ¡y
va hacia abajo en la pantalla! Por eso,GRAVITY
es un número positivo yFLAP_VELOCITY
es un número negativo en Python.bird.dead
es un bool, así que puedo escribir código comoif not bird.dead
en lugar dedead = 0
como en Scratch.
Resumen¶
Muchos de los conceptos disponibles en Scratch pueden ser traducidos directamente a Pygame Zero.
Aquí hay algunas comparaciones:
En Scratch |
En Pygame Zero |
---|---|
|
|
|
|
``set costume to <nombre>` |
|
|
|
|
|
|
|
|
Poner código en la función |
|
|
|
|
(0, 0) es el centro del escenario |
(0, 0) es la parte superior izquierda de la ventana |
En algunos casos, el código es más sencillo en Python porque puede organizarse de forma que tenga sentido al leerlo.
La potencia de los actores de Pygame Zero también facilita la manipulación de coordenadas. Utilizamos la posición de anchor
para posicionar las tuberías, y pudimos ver si una tubería estaba fuera de la pantalla comprobando pipe_top.right < 0
en lugar de if posición x < -240
.