Consejos para crear juegos
Consejos breves y prácticos sacados de los juegos de esta sección. Cada uno explica el porqué, con las pocas líneas de código que lo hacen en Pyzed.
Multiplica siempre por dt
Sección titulada «Multiplica siempre por dt»pz.frames(self.tick) llama a tick(dt) una vez por fotograma mostrado, y dt son los segundos desde el anterior.
Las velocidades son por segundo y cada cambio es velocidad × dt, así el juego va igual en un portátil de 60 Hz
que en un monitor de 144 Hz. Los temporizadores también bajan con dt:
self.x += self.vx * dtself.cooldown = max(0.0, self.cooldown - dt)Limita dt
Sección titulada «Limita dt»Tras una pausa (arrastrar la ventana, un punto de interrupción, un fotograma lento) dt puede ser grande, y un paso
grande deja que las cosas atraviesen paredes. Pyzed ya lo limita a 0,25 s; los juegos con mucha física lo limitan más:
def tick(self, dt): dt = min(dt, 1 / 30)Un paso de tiempo fijo, cuando lo necesites
Sección titulada «Un paso de tiempo fijo, cuando lo necesites»Algunos juegos necesitan que todos los pasos tengan el mismo tamaño: física que no debe depender de los fotogramas por segundo, repeticiones, juego en red, o una simulación que debe dar exactamente el mismo resultado dos veces. Acumula tiempo y avanza en trozos fijos:
STEP = 1 / 120
def tick(self, dt): self.acc += dt while self.acc >= STEP: self.simulate(STEP) # the game's logic, always with the same dt self.acc -= STEP self.blend = self.acc / STEP # 0..1: how far between two steps, to interpolate drawingPara juegos por turnos o en cuadrícula, usa pz.every(segundos, callback) en lugar de un bucle por fotograma, como
hace Snake.
Para el bucle cuando nada se mueve
Sección titulada «Para el bucle cuando nada se mueve»subscriptions() se evalúa tras cada actualización. Devuelve [] en pausa, en un menú sin nada animado o entre
movimientos de un puzle, y el juego no usa CPU:
def subscriptions(self): return [pz.frames(self.tick)] if self.animating else []Rendimiento
Sección titulada «Rendimiento»- Que
ticksea pequeño. Números, listas y clases pequeñas son rápidos; evita construir estructuras grandes en cada fotograma, y no leas archivos ni la red dentro del bucle. - Agrupa formas.
draw.rects([...], fill=color)dibuja muchos rectángulos de un color como una sola forma; así se mantienen baratosgame.spritey los tiles del juego de plataformas. - Dibuja solo lo que está en pantalla, y colisiona solo con lo que está cerca (mira el juego de plataformas).
- Limita las partículas con
game.Particles(limit=...), y quita las balas y los enemigos que salen de la pantalla. - Las imágenes se guardan en caché.
draw.image("ship.png", ...)decodifica un archivo una vez, así que dibujarlo cientos de veces por fotograma está bien. - El HUD es barato, pero
pz.memopuede guardar un widget caro de construir que cambia poco. - Mide. Muestra un contador de FPS (mira Orb Rush) y lee la guía de Rendimiento.
Entrada
Sección titulada «Entrada»-
Mantener vs pulsar. Usa
game.axis,game.vectorypz.is_pressedentickpara lo que pasa mientras una tecla está pulsada (moverse, disparo automático), yon_keypara lo que pasa una vez por pulsación (saltar, pausar). -
Guarda las pulsaciones un momento. Recuerda una pulsación un rato corto para que no se pierda: un salto pulsado justo antes de aterrizar, o dos giros rápidos en Snake. Basta un temporizador pequeño o una cola:
def on_key(self, combo):if combo == "space":self.jump_buffer = 0.12 # seconds; tick counts it down and jumps when possible -
Sé indulgente. El coyote time, cajas de colisión del jugador un poco más pequeñas que su sprite y un poco más grandes para los objetos hacen que un juego parezca justo.
-
Deja elegir. Acepta flechas y WASD (
game.vectorlo hace por defecto), y el ratón donde tenga sentido.
El “juice” es la respuesta que hace que una acción se sienta bien: el mismo golpe, con y sin ella:

| Efecto | Cómo |
|---|---|
| Temblor de pantalla | shake = game.Shake(), shake.kick(8) en un golpe, shake.update(dt) en cada fotograma, y empieza el canvas con *shake.shapes() |
| Partículas | game.Particles(...), burst(x, y, ...) en los eventos, update(dt) y shapes(glow=True) |
| Pausa de impacto (hit-stop) | no actualices el mundo durante 50–100 ms tras un golpe fuerte (deja moverse a las partículas) |
| Destello | dibuja en blanco lo golpeado durante uno o dos fotogramas |
| Squash and stretch | draw.scale(sx, sy) alrededor de los pies al aterrizar, o en la dirección del movimiento |
| Easing | tweens con curvas ease_out en vez de movimientos lineales (mira Sprites y animación) |
| Brillo | game.glow(x, y, r, color) en el canvas; bloom y glow en los widgets |
Organizar un juego
Sección titulada «Organizar un juego»- El estado en un sitio. Los atributos de la app son el estado del juego;
reset()onew_game()los fija todos, así que volver a empezar es una llamada. - Clases pequeñas para las cosas.
Enemy,Rock,Tile: unos pocos atributos cada una. Las listas de ellas son el mundo. - Reglas como funciones. El código que decide qué pasa (
slide_line,can_move, las colisiones) no debería dibujar; el código que dibuja no debería cambiar el estado. Así los dos son fáciles de cambiar y de probar. - Escenas. Un atributo
scene("title","play","pause"…) decide qué ejecutaticky qué muestraview(mira Menús, HUD y ajustes). - Constantes arriba. Velocidades, tamaños y tiempos como constantes con nombre son los mandos para afinar el juego.
- Azar con semilla.
self.random = random.Random(seed)hace un juego reproducible para pruebas e informes de errores; conseed=Nonees aleatorio como siempre.
Depurar
Sección titulada «Depurar»- Dibuja lo que ve el código: cajas de colisión, velocidades, caminos, el tile bajo el ratón. Actívalo con una tecla, como hace Jump Lab con H.
- Escribe los números en el canvas con
draw.text(..., font="mono")en vez de en la consola, para verlos en su contexto. - Cámara lenta: multiplica
dtpor 0,25 mientras mantienes una tecla para ver un momento rápido.
Probar juegos sin ventana
Sección titulada «Probar juegos sin ventana»Un juego de Pyzed es un objeto de Python normal: puedes crearlo, llamar a tick en un bucle y comprobar el resultado,
sin ventana. Los juegos de esta sección dejan que las pruebas fijen las teclas que leen con un atributo held:
from coin_chase import CoinChase, SPEED
def test_moves_right(): app = CoinChase(seed=1) app.held = {"right"} # instead of the real keyboard x = app.x for _ in range(30): app.tick(1 / 60) # half a second, frame by frame assert app.x == pytest.approx(x + SPEED * 0.5)
def test_draws(tmp_path): app = CoinChase(seed=1) app.screenshot(tmp_path / "frame.png", scale=1) # renders off-screen, no window neededgame.axis, game.vector y game.is_pressed aceptan held= para esto. App.screenshot (o pz.screenshot(app, path)) dibuja cualquier momento del juego en un PNG, que es también como se grabaron las animaciones de estas
páginas. Las pruebas de estos juegos están en
tests/test_game_tutorials.py.