Programacion en 2D

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 314

Tema 2.

2
Programación en 2D

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Conocimientos básicos necesarios para programar videojuegos:


○ Conocimientos de matemáticas.

■ En principio no son muchos y dependerán básicamente del tipo de juego, pero suelen ser esenciales conocimientos básicos de trigonometría y geometría.

○ Conocimientos de física.

■ Como las matemáticas, depende del tipo de juego, para juegos de plataforma con conocimientos básicos de cinemática es suficiente.

○ Conocimientos de programación.

■ Se debe saber programar y conocer bien un lenguaje de programación

■ Del lenguaje elegido para programar lo más importante es que sea popular con una amplia comunidad y colección de bibliotecas.

● Consulta

● Recursos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Si se poseen estos conocimientos lo siguiente es escoger un lenguaje y


una biblioteca para el desarrollo de videojuegos
Lenguaje Biblioteca
● ¿Por qué python?
C SDL
○ La sintaxis de Python es extremadamente sencilla
C++ SFML
○ Python es un lenguaje interpretado. C# XNA / MonoGame
■ Los lenguajes interpretados permiten ciclos de desarrollo breves (edición y ejecución). Python PyGame
■ Python dispone de un entorno de ejecución que ayuda a detectar los errores (incluyendo aquellos que sólo se Java libgdx, spiller
manifiestan en ejecución) señalándolos con mensajes muy informativos.
Ruby Gosu
■ Python ofrece, además, un entorno interactivo con el que es posible efectuar pequeñas pruebas o diseñar
Flash Flixel
incrementalmente las soluciones a los problemas.
Lua Love2D
■ La contrapartida de que se trate de un lenguaje interpretado es, obviamente, la menor velocidad de ejecución.

● No obstante, esta menor velocidad no resulta en absoluto importante para los videojuegos en 2D.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● ¿Por qué python?


○ Python es un lenguaje tipado dinámicamente.
Lenguaje Biblioteca
■ Cada dato es de un tipo determinado y sólo se puede operar con él de formas bien definidas.
C SDL
■ La ventaja es que no hay que declarar variables antes de su uso. C++ SFML
○ Python facilita la detección y gestión de errores mediante excepciones C# XNA / MonoGame
■ Las excepciones forman parte ya de los lenguajes de programación modernos Python PyGame
● Java las incorporó desde el principio Java libgdx, spiller

● C++ lo ha hecho posteriormente


Ruby Gosu
Flash Flixel
○ Python ofrece un conjunto de estructuras de datos flexibles.
Lua Love2D
■ Por ejemplo: El tipo lista de Python (un vector dinámico heterogéneo) permite introducir con
naturalidad el concepto de secuencia y presentar los algoritmos básicos de manejo de
secuencias.

● Que la indexación empiece siempre en 0 ayuda a dar el salto a C, C++ o Java.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● ¿Por qué python?


○ Python simplifica la gestión de memoria.

■ El modelo de memoria de Python es sencillo: todo valor reside en el heap y toda variable contiene una referencia a su
Lenguaje Biblioteca
valor.
C SDL
■ A ello se suma un sistema de recogida automática de basura (garbage collection) que evita los punteros colgantes
C++ SFML
(dangling pointers), las fugas de memoria, las violaciones de segmento, etc.

● Estos errores de ejecución, habituales en lenguajes como C o C++, son difíciles de detectar y pueden convertir el C# XNA / MonoGame
desarrollo de programas en una actividad tediosa y frustrante
Python PyGame
○ Python ofrece una amplia colección de módulos (bibliotecas)
Java libgdx, spiller
■ Hay módulos para cualquier actividad imaginable:
Ruby Gosu
● Escritura de CGI, gestión de correo electrónico, desarrollo de interfaces gráficas de usuario, análisis de documentos
HTML o XML, acceso a bases de datos, trabajo con expresiones regulares, etc. Flash Flixel
○ El mecanismo de paso de parámetros es único. Lua Love2D
■ Los parámetros se pasan a funciones y métodos por referencia a objeto.

● En la práctica, se comporta de forma similar al paso de parámetros de Java:

○ El paso de objetos básicos (de tipo escalar) se realiza con efectos similares al paso por valor

○ El de objetos más elaborados (vectores, diccionarios, instancias de clase, etc.) por referencia.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● ¿Por qué python?


○ Python es orientado a objetos.
Lenguaje Biblioteca
■ A diferencia de Java, Python permite una programación puramente procedimental
C SDL
● La orientación a objetos, aunque perfectamente soportada, es opcional C++ SFML
■ El soporte a la programación orientada a objetos es similar al de lenguajes como Smalltalk: C# XNA / MonoGame
● La resolución de los nombres de método y atributos es dinámica. Python PyGame
○ Ello elimina la necesidad de complicadas jerarquías de herencia, clases virtuales e interfaces Java libgdx, spiller

○ De todas maneras, Python soporta la herencia múltiple


Ruby Gosu
Flash Flixel
■ Cuestiones como el diseño de contenedores genéricos está también resuelta, pues el sistema de tipos
dinámico ofrece la flexibilidad suficiente. Lua Love2D

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Python + Pygame
○ Ventajas

■ Libres

■ Multiplataforma

■ Lenguaje y Biblioteca muy sencilla

● Muy recomendado para iniciarse en el mundo de desarrollo de videojuegos

○ Desventajas

■ El soporte de gráficos por hardware es limitado

● Usa mucho el procesador

■ Python es un lenguaje interpretado

● Para juegos que requieran grandes cálculos eso se nota

■ Soporte solo para 2D

● Aunque se puede combinar con otras bibliotecas, como PyOpenGL

■ La gestión del sonido de Pygame es deficiente

● Es recomendable utilizar bibliotecas adicionales

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Pygame es una biblioteca multimedia (que trabaja sobre las librerías SDL) que permiten la creación de
videojuegos en dos dimensiones de una manera sencilla.
○ Las SDL (Simple DirectMedia Layer) son un conjunto de bibliotecas que proporcionan funciones básicas para realizar operaciones de dibujado 2D, gestión de efectos de sonido,
música, carga y gestión de imágenes.

■ Pygame puede ser usado también para la realización de aplicaciones multimedia e interfaces gráficas.

● Básicamente, pygame es una librería similar a OpenGL o DirectX (sólo en la parte 2D), diseñada para ser
más fácil de usar que estas otras
○ Además, OpenGL solo se encarga de la parte gráfica, en cambio SDL incluye otras cosas como son los sonidos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● El desarrollo de PyGame comenzó en el 2000 de la mano de Pete Shinners que acababa de conocer
Python y SDL (Simple Directmedia Layer), así como un pequeño proyecto denominado PySDL que
pretendía fusionar ambos
○ La desparición de PySDL motivó la decisión de asumir el proyecto de realizar un motor de juego basado en Python y SDL.

● Algunos juegos programados con pygame: Frets on Fire, SolarWolf o Pydance.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Pygame se encarga de manejar la parte más complicada dentro de la programación de un videojuego de


una forma sencilla:
○ Cargar y mostrar las imágenes (en formatos como PNG, BMP, PCX, TGA,…), sonido (formatos OGG, MP3, MIDI,…), vídeos, las ventanas del juego y monitoriza los dispositivos
de entrada (como mouse, teclado y joystick) de una manera bastante sencilla.

■ Por tanto, básicamente uno se tiene que preocupar por la programación del juego en sí

■ PyGame permite la creación y desarrollo de videojuegos de una forma clara y sencilla, sin que esto nos impida obtener los resultados esperados puesto que puede
combinarse con otras librerías, como PyOpenGl, para incluir gráficos 3D.

● Otra de las características de Python es la existencia de una completa documentación para facilitar la
comprensión y utilización, así como una gran variedad de ejemplos y una amplia comunidad de usuarios

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● El bucle de juego
○ Bucle infinito que ejecuta las diferentes fases del juego y controla el gameplay

○ 3 fases:

■ Importación

■ Inicialización

■ Ejecución

● Rolling example: PONG

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Importación import pygame


from pygame.locals import *
import sys

El módulo sys es importante para trabajar correctamente con el sistema Constantes y variables útiles
operativo. En particular, sys.exit()

● Inicialización pygame.init()
visor = pygame.display.set_mode((640, 480), 0, 32)

display.set_mode() crea la superficie de visualización Tamaño Profundidad de color

Opciones

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Inicialización
○ Opciones: Flags (separadas por | )
pygame.init()
■ FULLSCREEN: Pantalla completa visor = pygame.display.set_mode((640, 480), 0, 32)
■ RESIZABLE: Tamaño variable

■ NOFRAME: Sin bordes ni título

■ OPENGL: Para 3D

■ DOUBLEBUF: Doble buffer

■ HWSURFACE: Acelerada por hardware, sólo con FULLSCREEN

○ Profundidad de color (bits):

■ 8: 256 colores

■ 15: 32768 colores con transparencia

■ 16: 65536 colores

■ 24: 16.7 millones de colores

■ 32: 16.7 millones de colores con transparencia

○ Visor contiene un objeto Surface que puede ser una ventana o la pantalla completa

■ Aquí se mostrará el juego y las animaciones

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Cómo utilizar pygame


○ 3 fases:

■ Importación

■ Inicialización

■ Ejecución

● Mostrar imágenes y reproducir sonido

● Responder a las acciones del jugador

● Detectar las incidencias en el juego

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Dibujado en pantalla:
○ Para evitar artefactos se dibuja offscreen en un buffer

■ usando el método visor.blit()

● Dibujar imágenes

● Operación de blitting

○ …y luego se vuelca todo en pantalla

■ usando el método pygame.display.update()

○ Una vez por cada frame (fotograma)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● El blitting es una operación para transferir un bloque de bytes de un sector de la memoria a otra
● Este término viene del término inglés blit (Block Image Transfer, Transferencia de Imagen por Bloque) y es
una forma de dibujar imágenes con o sin transparencias sobre un fondo
○ Este fondo normalmente es un buffer que representa a la propia pantalla

● Esta técnica puede ser acelerada por hardware, lo cual ayuda a incrementar el rendimiento
○ De hecho es una de las operaciones más críticas en cualquier juego

● En una operación de blitting, se puede especificar en qué posición de la imagen de destino se quiere
copiar la imagen origen
○ Incluso qué parte de la superficie origen se quiere copiar.

○ De esta forma se puede colocar en cualquier posición, unas imágenes dentro de otras.

○ Gracias al blitting se puede montar una escena en un videojuego.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Normalmente, y para que todo vaya rápido, se monta la escena primero en un buffer mediante
esta operación, y se coge la imagen resultante y se manda a la pantalla
○ De esta manera, en pantalla sólo se dibuja una única vez por fotograma del videojuego

■ Lo cual es bastante más rápido que pintar varias veces en pantalla por cada fotograma

○ A esta técnica se le llama double buffering (o doble buffer)

● En Pygame, se tiene un buffer que es el buffer de pantalla, donde se dibuja toda la escena, y
posteriormente se manda a la pantalla este buffer utilizando esta técnica, mediante la función
anterior
○ ¿Qué ocurriría si se dibujase directamente en pantalla?

■ Si se montase la escena directamente en la pantalla

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Otros métodos para dibujar offscreen:


○ blit()

■ Imágenes

○ draw.line()

■ Líneas

○ draw.circle()

■ Círculos

○ draw.ellipse()

■ Elipses

○ draw.rect()

■ Rectángulos

○ etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Colores:
○ Tuplas con los componentes Rojo, Verde y Azul (RGB)

colorVerde = (0, 255, 0)

● Posiciones:
○ Tuplas con las coordenadas (x, y)

punto = (223, 327)

● Zonas:
○ Tuplas con la posición de arriba a la izquierda, largo y alto

zona = (0, 0, 300, 200)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

Zona (550, 200, 120, 80)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para dibujar texto:


○ Se define el tipo de letra (fuente):

tipoLetra = pygame.font.SysFont(‘arial’, 48)

○ Se genera el texto con esa fuente: Color de Color de


■ Será una imagen Suavizar texto fondo

texto = tipoLetra.render(‘Texto’, True, color1, color2)


○ Y se dibuja offscreen:

■ Se transfiere la imagen mediante una operación de blitting

visor.blit(texto, textRect)

Rectángulo que dicta la zona donde se va a dibujar

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● ¿Dónde se puede encontrar fuentes para el juego?


○ http://simplythebest.net/fonts/

■ Ejemplo:

tipoLetra = pygame.font.Font('Grandezza.ttf', 96)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example : PONG:


# Importar las librerías
import pygame, sys
from pygame.locals import *

# Inicializar la librería de pygame


pygame.init()

BLANCO = (255,255,255)

# Creamos la pantalla No se crea en pantalla completa


pantalla = pygame.display.set_mode((800,600))

# Rellenamos la pantalla de color negro


pantalla.fill((0,0,0))

# Dibujamos un círculo de color blanco en esa posición en el buffer


pygame.draw.circle(pantalla, BLANCO, (50,50),4,0)

# Actualizamos la pantalla
pygame.display.update()

# Finalizamos la librería y salimos al sistema


pygame.quit()
sys.exit()
1 - solo se crea la pantalla y sale.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ejecución ● Realizar esto una y otra


○ Mostrar imágenes y reproducir sonido vez
○ Responder a las acciones del jugador ○ Bucle de eventos

○ Detectar las incidencias en el juego ○ En un determinado número de frames


por segundo

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● En PyGame

while True:
...
for event in pygame.event.get():
...
if event.type == QUIT:
pygame.quit( ) Mirar la lista
sys.exit( ) de eventos

Responder al from pygame.locals import *


tipo de evento

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


while True:

# Para cada evento posible


for evento in pygame.event.get():

# Si el evento es la pulsación de la tecla Escape


if evento.type == KEYDOWN and evento.key == K_ESCAPE:
# Se sale del programa
pygame.quit()
sys.exit()

# Rellenamos la pantalla de color negro


pantalla.fill((0,0,0))

# Dibujamos un círculo de color blanco en esa posición en el buffer


pygame.draw.circle(pantalla, BLANCO, (50,50),4,0)

# Actualizamos la pantalla
pygame.display.update() 2 - con el bucle infinito.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


while True:

# Para cada evento posible


for evento in pygame.event.get():

# Si el evento es la pulsación de la tecla Escape


if evento.type == KEYDOWN and evento.key == K_ESCAPE:
# Se sale del programa
pygame.quit()
sys.exit()

# Rellenamos la pantalla de color negro


pantalla.fill((0,0,0))

# Dibujamos un círculo de color blanco en esa posición en el buffer


pygame.draw.circle(pantalla, BLANCO, (50,50),4,0)

# Actualizamos la pantalla
pygame.display.update()

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


pygame.init()
pantalla = pygame.display.set_mode((800,600))
BLANCO = (255,255,255)

# Posicion de la pelota
pelotaX = 50
pelotaY = 50

while True:

for evento in pygame.event.get():


if evento.type == KEYDOWN and evento.key == K_ESCAPE:
pygame.quit()
sys.exit()

# Actualizamos la posición de la pelota


pelotaX += 5
pelotaY += 5

pantalla.fill((0,0,0))
pygame.draw.circle(pantalla, BLANCO, (pelotaX,pelotaY),4,0)
pygame.display.update()
3 - la pelota se mueve (muy rapido).py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


pygame.init()
pantalla = pygame.display.set_mode((800,600))
BLANCO = (255,255,255)

# Posicion de la pelota
pelotaX = 50
pelotaY = 50

while True:

for evento in pygame.event.get():


if evento.type == KEYDOWN and evento.key == K_ESCAPE:
pygame.quit()
sys.exit()

# Actualizamos la posición de la pelota


pelotaX += 5
pelotaY += 5

pantalla.fill((0,0,0))
pygame.draw.circle(pantalla, BLANCO, (pelotaX,pelotaY),4,0)
pygame.display.update()

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Problema con este código:


○ La velocidad a la que se ejecuta el bucle es dependiente de la máquina que lo ejecute

■ En un ordenador más rápido irá más rápido y viceversa

● Hará más o menos iteraciones del bucle en el mismo tiempo

○ Consecuentemente, la velocidad de la pelota es dependiente de la velocidad del procesador

○ Necesario sincronizar el juego

● Hay dos formas de sincronización


○ Sincronización por Framerate o Frames per Second (FPS)

○ Sincronización por Tiempo

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sincronización por Framerate o Frames per Second (FPS):


○ Se refiere a la frecuencia (número de veces) con que se ejecuta el ciclo principal de un videojuego en un segundo

■ Mientras más alto, más fluidez

■ Se garantiza que cada iteración del ciclo (cada frame) se realiza cada cierto tiempo (milisegundos)

■ Este es uno de los métodos más extendidos

● Especialmente en juegos en 2D

○ ¿Cómo se hace?

■ Se obtiene el tiempo en un punto del ciclo

● Típicamente, al inicio

■ La próxima vez que se llega a ese punto se espera hasta que transcurre el tiempo correspondiente

● Así se consigue que el bucle se ejecute un número determinado de veces por segundo

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sincronización por Framerate o Frames per Second (FPS):


○ Ventaja:

■ El juego debería verse de la misma forma en cualquier equipo en donde se ejecute

● Si el equipo es muy potente, solo funcionará a los FPS especificados (pese a que puede ir mas rápido)

○ Inconveniente:

■ Si se usa este método en una máquina con un procesador mucho más antiguo, lo más probable es que vaya bastante lento

● No es capaz de alcanzar ese FPS

○ Menos iteraciones del bucle por segundo

○ No a saltos, sino lento

● Requerimientos mínimos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sincronización por Tiempo


○ Se sincroniza en base al tiempo

■ No importan los FPS

■ Cada ciclo del bucle durará un tiempo distinto

● No se espera para que las iteraciones duren lo mismo

● Máquinas distintas ejecutarán un número distinto de ciclos en el mismo tiempo

○ El movimiento depende del tiempo transcurrido

■ Lo que se hace es calcular la posición de un objeto en función del tiempo transcurrido desde el ciclo anterior del bucle

● El objeto tiene una velocidad

● A partir de esa velocidad y el tiempo transcurrido, se calcula su nueva posición

■ Se mueven de igual manera los objetos sin importar en qué equipo se ejecute el juego

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sincronización por Tiempo


○ Ventaja:
■ Dado que el movimiento de los objetos es en función del tiempo y no de los FPS que se alcancen, los objetos se mueven siempre a la misma
velocidad

● Por tanto, no hay que preocuparse de controlar el framerate


○ Inconveniente:
■ Pese a que los objetos se mueven siempre a la misma velocidad, en un computador más lento el desplazamiento no se verá fluidamente
● O en un ciclo en el que hay mucho que procesar
○ Por ejemplo: en un entorno 3D muy complejo
● Por ejemplo, en caso de que se demore el juego 1 segundo en cada ciclo, cada vez que se deba mover un objeto este se desplazara grandes
distancias

○ El tiempo entre actualizaciones/ciclos en donde se refresca la pantalla es grande


○ Se produce un salto muy grande
○ No se mueven lentamente, sino a saltos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Con el primer método (FPS) si se quiere mover un objeto 8 pixeles, se haría lo siguiente:
○ x=x+8

● En cambio, si se quiere hacer en base al tiempo:


○ x = x + (velocidad)*(tiempo)

○ Es decir, cada objeto tendrá una velocidad asociada.

○ Por ejemplo si se desplaza el objeto a una velocidad de 0.008 píxeles/seg. , y el ciclo demora 1 segundo en ejecutarse (1000ms), el nuevo incremento será de:

■ x = x + 0.008*1000

■ x=x+8

● Habitualmente se realizan ambas en 2D


● En videojuegos 3D, se usa la sincronización por tiempo ya que el framerate varía mucho en cada ciclo
■ Escenas sencillas (con pocos objetos) y complejas (con muchos)

■ En las escenas complejas es posible que una máquina no pueda visualizar la escena a un determinado FPS

● Va a saltos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


# Creamos el objeto reloj para sincronizar el juego
reloj = pygame.time.Clock()

# Bucle infinito
while True:

# Hacemos que el reloj espere a un determinado fps


reloj.tick(60) Sincronización por FPS

# Para cada evento posible


for evento in pygame.event.get():
if evento.type == KEYDOWN and evento.key == K_ESCAPE:
pygame.quit()
sys.exit()

pelotaX += 2
pelotaY += 2

pantalla.fill((0,0,0))
pygame.draw.circle(pantalla, BLANCO, (pelotaX,pelotaY),4,0)
pygame.display.update()

4 - con reloj.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Método clock.tick():
○ Si se usa sin argumentos (o sea, clock.tick()) devuelve el tiempo que ha pasado (en milisegundos) desde la ultima vez que se llamó

■ Es decir, funciona como un reloj

○ Si se usa con un argumento, que es el framerate, la función esperará el tiempo necesario para mantener al juego corriendo a la velocidad solicitada

■ Es decir, si se llama clock.tick(60), el juego nunca correrá a más de 60 frames por segundo

● Este método no es muy exacto, pero casi no usa CPU


○ Si se quiere algo más exacto hay que usar

■ clock.tick_busy_loop

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


# Creamos el objeto reloj para sincronizar el juego
reloj = pygame.time.Clock()

# Bucle infinito
while True:
Sincronización por FPS
# Hacemos que el reloj espere a un determinado fps Se espera hasta el siguiente
tiempo = reloj.tick(60) frame, devuelve el tiempo
transcurrido
# Para cada evento posible
for evento in pygame.event.get():
if evento.type == KEYDOWN and evento.key == K_ESCAPE:
pygame.quit()
sys.exit()
Sincronización por tiempo
pelotaX += tiempo * VELOCIDAD_PELOTA_X Se calcula la posición en función
pelotaY += tiempo * VELOCIDAD_PELOTA_Y
del tiempo transcurrido
pantalla.fill((0,0,0))
pygame.draw.circle(pantalla, BLANCO, (pelotaX,pelotaY),4,0)
pygame.display.update()

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


○ Otras operaciones a realizar:

■ Comprobar que la pelota no se salga

● Invertir la velocidad

# Para cada evento posible


for evento in pygame.event.get():

# Miramos a ver si la pelota está en el límite en el eje X


if pelotaX <= RadioPelota or pelotaX >= MaxX - RadioPelota:
# Invertimos la velocidad en el eje X
VelocidadPelotaX = -VelocidadPelotaX

# Miramos a ver si la pelota está en el límite en el eje Y


if pelotaY < RadioPelota or pelotaY > MaxY - RadioPelota:
# Invertimos la velocidad en el eje Y
VelocidadPelotaY = -VelocidadPelotaY

# Actualizamos la posición de la pelota


pelotaX += VelocidadPelotaX
pelotaY += VelocidadPelotaY
5 - la pelota rebota en los limites.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


○ Otras operaciones a realizar:

■ Dibujar las raquetas

# Actualizamos la posición de la pelota


pelotaX += VelocidadPelotaX
pelotaY += VelocidadPelotaY

# Rellenamos la pantalla de color negro


pantalla.fill((0,0,0))

# Dibujamos un círculo de color blanco en esa posición en el buffer


pygame.draw.circle(pantalla, BLANCO, (pelotaX,pelotaY),RadioPelota,0)

# Dibujamos como un rectángulo cada raqueta


pygame.draw.rect(pantalla, BLANCO,
(raqueta1X,raqueta1Y,tamanoRaquetaX,tamanoRaquetaY))
pygame.draw.rect(pantalla, BLANCO,
(raqueta2X,raqueta2Y,tamanoRaquetaX,tamanoRaquetaY))

# Actualizamos la pantalla
pygame.display.update()
6 - con las raquetas.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


○ Otras operaciones a realizar:

■ Controlar las raquetas con teclado

● Las pulsaciones de teclas son eventos que se producen en la aplicación

● Control del videojuego

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Eventos

■ Cualquier notificación que recibe la aplicación

● Por ejemplo, pulsación de teclas o movimiento de ratón

● Típicamente, acciones realizadas por el usuario

○ Pero también otros eventos como, por ejemplo:

■ Cuando un sonido termina de reproducirse

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Eventos

■ Los eventos se pueden producir en cualquier momento, esté haciendo lo que esté haciendo la aplicación

● Estos eventos se introducen en una cola de eventos

○ Si la cola se llena, se pierden eventos

● Se comprueban los eventos en la cola una vez cada ciclo del bucle

○ Una vez por frame

○ Típicamente, al principio del bucle

■ Después, actualizar los objetos en función de los eventos que hayan llegado (y de lo que ocurra
en el juego)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Eventos
○ Para recoger los eventos

■ pygame.event.get()

● Devuelve todos los eventos en la cola

■ pygame.event.wait()

● Para la ejecución hasta que se produce un evento

■ pygame.event.poll()

● Devuelve el primer evento de la cola, si hay, o NOEVENT

■ Si no se utilizan eventos, realizar llamadas a pygame.event.pump()

● Hace que pygame procese los eventos

○ Si no los procesa, el SO podría pensar que la aplicación se ha bloqueado

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Eventos
○ Además, se puede filtrar los eventos con:

■ pygame.event.set_allowed()

■ pygame.event.set_blocked()

○ Por defecto todos los eventos están permitidos

○ Cada evento es un objeto de la clase pygame.event.Event:

■ Cada evento tiene unos atributos que describen el evento

● type:

○ Lo tienen todos los eventos

○ Indica el tipo de evento: QUIT, KEYDOWN, etc.

○ Valor entero que varía entre NOEVENT y NUMEVENTS

○ El programador puede definir nuevos eventos, asignándoles el valor USEREVENT o superior

● key, pos, button, joy, etc.

○ Específicos de cada evento

■ Si se quiere crear nuevos eventos, extender esta clase

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

for evento in pygame.event.get():


# Si el evento es la pulsación de una tecla Se hace un bucle iterando
if evento.type == KEYDOWN: sobre cada evento de la
# Si la tecla es Escape cola
if evento.key == K_ESCAPE: Para saber el tipo de evento,
# Se sale del programa
pygame.quit()
se mira evento.type
sys.exit() En este caso, si es la
# si no, si es la tecla 'q' pulsación de una tecla
En este tipo de elif evento.key == K_q:
evento, se mira el # Se mueve la raqueta del jugador 1 arriba
atributo key para raqueta1Y -= velocidadRaquetaY
saber qué tecla se ha # si no, si es la tecla 'a'
elif evento.key == K_a:
pulsado, y se realiza
# Se mueve la raqueta del jugador 1 abajo
la acción raqueta1Y += velocidadRaquetaY
correspondiente # si no, si es la tecla 'o'
elif evento.key == K_o:
# Se mueve la raqueta del jugador 2 arriba
raqueta2Y -= velocidadRaquetaY
# si no, si es la tecla 'l'
elif evento.key == K_l:
# Se mueve la raqueta del jugador 2 abajo
raqueta2Y += velocidadRaquetaY
7 – las raquetas se mueven.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego:


○ Teclado

■ Existen 2 maneras de detectar la pulsación de una tecla:

● 1 - Controlar el evento de pulsación y levantado de una tecla

○ Eventos KEYDOWN y KEYUP

■ Ejemplos anteriores

■ Controlar los eventos:

● pygame.event.get() devuelve la lista de eventos

○ Cada evento, mirar su tipo (type)

○ Si son pulsaciones de tecla, mirar el atributo key para saber cuál es

for evento in pygame.event.get():


if evento.type == KEYDOWN and evento.key == K_ESCAPE:
pygame.quit()
sys.exit()

■ Se produce un solo evento cuando se pulsa, y otro cuando se suelta

● No se producen eventos al mantener pulsada la tecla

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego:


○ Teclado

■ Existen 2 maneras de detectar la pulsación de una tecla:

● 2 - Controlar si una tecla está pulsada o no en un determinado momento

○ Permite mantener una tecla pulsada

○ Función pygame.key.get_pressed()

■ Devuelve un array de booleanos

■ Un valor booleano por cada tecla

● Indica si está pulsada o no

● Para referenciar el valor que corresponde a cada tecla: constantes

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego:


○ Teclado

■ Con el primer método se produce un solo evento cuando se pulsa, y otro cuando se suelta

● No se producen eventos al mantener pulsada la tecla

■ ¿Cómo utilizar el primer método para generar múltiples eventos cuando se mantiene una tecla pulsada?

● pygame.key.set_repeat()

○ Se le puede indicar el tiempo (ms) que tiene que transcurrir con la tecla pulsada para que se genere otro evento

○ Si no se pasan parámetros, se deshabilita

■ Repetición inicialmente deshabilitada

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego:


○ Teclas: constantes

■ Comienzan con “K_”:

● Números: K_0 – K_9

● Constantes: K_f1, K_LEFT, K_RETURN, …

● Letras: K_a – K_z

○ No hay mayúsculas: resultado de combinar Shift + tecla

● Lista completa: http://www.pygame.org/docs/ref/key.html

■ Cuidado:

● Debido a limitaciones del hardware, algunas combinaciones de teclas no pueden ser detectadas

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
KeyASCII ASCII Common name KeyASCII ASCII Common name KeyASCII ASCII Common name
K_BACKSPACE \b backspace K_6 6 6 K_k k k
K_TAB \t tab K_7 7 7 K_l l l
K_CLEAR clear K_8 8 8 K_m m m
K_RETURN \r return K_9 9 9 K_n n n
K_PAUSE pause K_COLON : colon K_o o o
K_ESCAPE ^[ escape K_SEMICOLON ; semicolon K_p p p
K_SPACE space K_LESS < less-than sign K_q q q
K_EXCLAIM ! exclaim K_EQUALS = equals sign K_r r r
K_QUOTEDBL quotedbl K_GREATER > greater-than sign K_s s s
K_HASH # hash K_QUESTION ? question mark K_t t t
K_DOLLAR $ dollar K_AT @ at K_u u u
K_AMPERSAND & ampersand K_LEFTBRACKET [ left bracket K_v v v
K_QUOTE quote K_BACKSLASH \ backslash K_w w w
K_LEFTPAREN ( left parenthesis K_RIGHTBRACKE K_x x x
K_RIGHTPAREN ) right parenthesis T ] right bracket K_y y y
K_ASTERISK * asterisk K_CARET ^ caret K_z z z
K_PLUS + plus sign K_UNDERSCORE _ underscore K_DELETE delete
K_COMMA , comma K_BACKQUOTE ` grave K_KP0 keypad 0
K_MINUS - minus sign K_a a a K_KP1 keypad 1
K_PERIOD . period K_b b b K_KP2 keypad 2
K_SLASH / forward slash K_c c c K_KP3 keypad 3
K_0 0 0 K_d d d K_KP4 keypad 4
K_1 1 1 K_e e e K_KP5 keypad 5
K_2 2 2 K_f f f K_KP6 keypad 6
K_3 3 3 K_g g g K_KP7 keypad 7
K_4 4 4 K_h h h K_KP8 keypad 8
K_5 5 5 K_i i i K_KP9 keypad 9
K_j j j

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
KeyASCII ASCII Common name
KeyASCII ASCII Common name
K_KP_PERIOD . keypad period
K_F12 F12
K_KP_DIVIDE / keypad divide
K_F13 F13
K_KP_MULTIPLY * keypad multiply
K_F14 F14
K_KP_MINUS - keypad minus
K_F15 F15
K_KP_PLUS + keypad plus
K_NUMLOCK numlock
K_KP_ENTER \r keypad enter
K_CAPSLOCK capslock
K_KP_EQUALS = keypad equals
K_SCROLLOCK scrollock
K_UP up arrow
K_RSHIFT right shift
K_DOWN down arrow
K_LSHIFT left shift
K_RIGHT right arrow
K_RCTRL right ctrl
K_LEFT left arrow
K_LCTRL left ctrl
K_INSERT insert
K_RALT right alt
K_HOME home
K_LALT left alt
K_END end
K_RMETA right meta
K_PAGEUP page up
K_LMETA left meta
K_PAGEDOWN page down
K_LSUPER left windows key
K_F1 F1
K_RSUPER right windows key
K_F2 F2
K_MODE mode shift
K_F3 F3
K_HELP help
K_F4 F4
K_PRINT print screen
K_F5 F5
K_SYSREQ sysrq
K_F6 F6
K_BREAK break
K_F7 F7
K_MENU menu
K_F8 F8
K_POWER power
K_F9 F9
K_EURO euro
K_F10 F10

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


pygame.key.set_repeat(1, 25)

# Para cada evento posible


for evento in pygame.event.get():
# Si el evento es la pulsación de una tecla
if evento.type == KEYDOWN:
# Si la tecla es Escape
if evento.key == K_ESCAPE:
pygame.quit()
sys.exit()
# si no, si es la tecla 'q'
elif evento.key == K_q:
# Se mueve la raqueta del jugador 1 arriba
Para mirar qué raqueta1Y -= velocidadRaquetaY
# si no, si es la tecla 'a'
tecla se ha elif evento.key == K_a:
pulsado, se # Se mueve la raqueta del jugador 1 abajo
raqueta1Y += velocidadRaquetaY
mira el atributo # si no, si es la tecla 'o'
key del evento elif evento.key == K_o:
# Se mueve la raqueta del jugador 2 arriba
raqueta2Y -= velocidadRaquetaY
# si no, si es la tecla 'l'
elif evento.key == K_l:
# Se mueve la raqueta del jugador 2 abajo
raqueta2Y += velocidadRaquetaY
8 - teclas pulsadas y sin raton.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Control del videojuego:


○ Segunda forma de leer las teclas:

■ Función pygame.key.get_pressed()

● Devuelve una lista de booleanos, uno por cada tecla

● Por ejemplo:

teclas_pulsadas = pygame.key.get_pressed()
if teclas_pulsadas[K_SPACE]:
fire()

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego:

○ Otras funciones de pygame.key:

■ pygame.key.get_focused

● Devuelve un valor booleano que dice si la ventana tiene el foco, y puede recibir entrada de teclado

○ Si se ejecuta en pantalla completa, devuelve siempre True

■ pygame.key.name

● Recibe como parámetro una constante KEY_ y devuelve un string con el nombre de la tecla, como ‘a’, ‘b’, etc.

■ pygame.key.get_mods

● Devuelve un valor que dice cuál de las teclas modificadoras (Shift, Alt, Ctrl) están pulsadas

○ Ejemplo: para saber si Shift está pulsada: pygame.key.get_mods() & KMOD_LSHIFT

■ pygame.key.set_mods

● Para imitar el efecto de una tecla pulsada

○ Por ejemplo, para usar las teclas Shift y Alt: pygame.key.set_mods(KMOD_SHIFT | KMOD_ALT)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Ratón:

■ 2 formas similares de gestionar el ratón:

● Eventos:

○ pygame.MOUSEMOTION

○ pygame.MOUSEBUTTONDOWN

○ pygame.MOUSEBUTTONUP

■ Estos 2 eventos contienen dos atributos:

● pos: posición

● button: qué botón se ha pulsado

○ 4: rueda arriba

○ 5: rueda abajo

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Ratón:

■ 2 formas similares de gestionar el ratón:

● Tomar el estado mediante:

○ Posición:

■ pygame.mouse.get_pos()

● Devuelve la posición

● Además, pygame.mouse.set_pos()

○ Sitúa el ratón en un sitio determinado

■ pygame.mouse.get_rel()

● Devuelve la cantidad de movimiento en X e Y desde la última llamada a esta función

○ Botones pulsados

■ pygame.mouse.get_pressed

● Devuelve (button1, button2, button3) booleanos representando el estado de los 3 botones del ratón

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Ratón:

■ Además:

● pygame.mouse.set_visible(False)

○ Elimina de la pantalla el cursor del ratón

● pygame.event.set_grab(True)

○ Le da a pygame el control completo del ratón

● Con estos dos, el ratón entra en un modo de entrada virtual

○ Los movimientos relativos del ratón nunca se pararán por los bordes de la pantalla

■ Ejemplo: punto de vista de una cámara subjetiva en un videojuego en 3D

● pygame.mouse.set_cursor

○ Establece la imagen para el cursor

● pygame.mouse.get_cursor

○ Devuelve la imagen para el cursor

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Joystick

■ Cualquier tipo de controlador (joysticks, gamepads)

■ Funciones:

● pygame.joystick.init

○ Inicializa el módulo del joystick.

○ Se llama automáticamente desde pygame.init

■ No hace falta llamarlo

● pygame.joystick.quit

○ Finaliza el módulo de joystick

○ Se llama automáticamente, no se necesita llamarla

● pygame.joystick.get_init

○ Devuelve True si el módulo del joystick ha sido correctamente inicializado

○ Si devuelve False, el resto de funciones no funcionarán

○ Tampoco se suele llamar manualmente

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Joystick

■ Cualquier tipo de controlador (joysticks, gamepads)

■ Funciones:

● pygame.joystick.get_count

○ Devuelve el número de joysticks actualmente conectados al ordenador

● pygame.joystick.Joystick

○ Crea un nuevo objeto joystick que se usa para acceder a toda la información de un periférico determinado.

○ El constructor toma el ID de ese joystick

■ El primer joystick tiene ID 0, el siguiente ID 1, etc. hasta el número de joysticks disponibles.

○ Necesario inicializar el joystick

■ pygame.joystick.Joystick.init()

■ Si no, no envía eventos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Joystick

■ Ejemplo: Si hay algún periférico


conectado
joystick = None
if pygame.joystick.get_count() > 0:
joystick = pygame.joystick.Joystick(0)
joystick.init()

Se inicializa ese Se crea un objeto joystick


periférico con el primer periférico
conectado

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Joystick

■ Para gestionar el joystick (Eventos):

● JOYBUTTONDOWN, JOYBUTTONUP

○ Pulsación de botones

● JOYAXISMOTION

○ Movimiento de sticks

● JOYBALLMOTION

○ Movimiento de bolas

● JOYHATMOTION

○ Pulsación de crucetas

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Joystick

■ Para gestionar el joystick (Eventos):

● JOYBUTTONDOWN, JOYBUTTONUP

○ Contienen:

■ joy: id del joystick

■ button: índice del botón

● Los botones se empiezan a numerar desde 0

● Se puede saber el número de botones con el método get_numbuttons del objeto Joystick

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Joystick

■ Para gestionar el joystick (Eventos)

● JOYAXISMOTION

○ Sticks analógicos

○ Contiene:

■ joy: ID del joystick

■ axis: índice del eje

● 0: eje x

● 1: eje y

● Si hay más de un stick analógico, siguen con 2, 3, etc.

● get_numaxis en el objeto para saber el número de ejes

■ value: posición actual de ese eje

● Entre -1 (izquierda o abajo) y 1 (derecha o arriba)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Joystick

■ Para gestionar el joystick (Eventos)

● JOYAXISMOTION

○ Problema: Debido a la naturaleza mecánica y analógica de los sticks, se puede producir un flujo constante de eventos JOYAXISMOTION sin tocar nada

■ De valor muy bajo

■ Ignorar este evento, si su valor es muy bajo (<0.1)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Joystick

■ Para gestionar el joystick (Eventos)

● JOYBALLMOTION

○ Similar, si el joystick tiene bola

○ Contiene:

■ joy: ID

■ ball: número de bola

■ rel: movimiento relativo realizado desde la última vez

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Joystick
■ Para gestionar el joystick (Eventos)
● JOYHATMOTION
○ Cruceta de movimiento
■ d-pad o hat
○ Contiene:
■ joy: ID
■ hat: número de cruceta
■ value: qué dirección se ha pulsado
● Tupla en eje x y eje y:
○ Valores negativos: izquierda o abajo
○ Valores positivos: derecha o arriba
○ Cada valor solo puede ser -1, 0 o 1

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Joystick

■ Para gestionar el joystick (Eventos)

● JOYHATMOTION

○ No hay eventos distintos para cuando se pulse o libere un botón de la cruceta

■ En ambos casos (pulsar, soltar) se produce un evento JOYHATMOTION

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Joystick

■ Para gestionar el joystick (Obtener el estado del joystick: Mirar el objeto Joystick (1/3))

● pygame.joystick.Joystick.init

○ Inicializa el Joystick

● pygame.joystick.Joystick.quit

○ Finaliza el Joystick

● pygame.joystick.Joystick.get_init

○ Comprueba si un Joystick ha sido inicializado

● pygame.joystick.Joystick.get_id

○ Devuelve el ID del joystick

● pygame.joystick.Joystick.get_name

○ Devuelve el nombre del joystick en el sistema

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Joystick

■ Para gestionar el joystick (Obtener el estado del joystick: Mirar el objeto Joystick (2/3))

● pygame.joystick.Joystick.get_numaxes

○ Devuelve el número de ejes de un joystick

● pygame.joystick.Joystick.get_axis

○ Devuelve la posición actual de un eje

● pygame.joystick.Joystick.get_numballs

○ Devuelve el número de bolas en un joystick

● pygame.joystick.Joystick.get_ball

○ Devuelve la posición relativa de una bola

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Joystick

■ Para gestionar el joystick (Obtener el estado del joystick: Mirar el objeto Joystick (3/3))

● pygame.joystick.Joystick.get_numbuttons

○ Devuelve el número de botones de un joystick

● pygame.joystick.Joystick.get_button

○ Devuelve el estado actual de un botón de un joystick

■ Valor booleano

● pygame.joystick.Joystick.get_numhats

○ Devuelve el número de crucetas de un joystick

● pygame.joystick.Joystick.get_hat

○ Devuelve la posición de una cruceta de un joystick

■ Tupla (x, y), digital, cada valor sólo puede ser -1, 0 o 1

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Si un objeto ha de moverse con una velocidad en un ángulo, la velocidad ha de descomponerse en sus componentes x e y

■ Por ejemplo:

● Si se mueve con velocidad a la derecha o abajo, se moverá en esta dirección esa velocidad

● Pero si se mueve con un ángulo de 45 grados:

x
45
x = cos(45) = 0.707
1 y
y = sen(45) = 0.707

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Control del videojuego


○ Si un objeto ha de moverse con una velocidad en un ángulo, la velocidad ha de descomponerse en sus componentes x e y

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


○ Otras operaciones a realizar:

■ Comprobar que las raquetas no se salen de los bordes

# Para cada evento posible


for evento in pygame.event.get():
# Si el evento es la pulsación de una tecla
if evento.type == KEYDOWN:

# si no, si es la tecla 'q'


elif evento.key == K_q:
# Se mueve la raqueta del jugador 1 arriba
raqueta1Y -= velocidadRaquetaY
# Si se sale por arriba
if raqueta1Y < 0:
raqueta1Y = 0
# si no, si es la tecla 'a'
elif evento.key == K_a:
# Se mueve la raqueta del jugador 1 abajo
raqueta1Y += velocidadRaquetaY
# Si se sale por abajo
if raqueta1Y > MaxY - tamanoRaquetaY:
raqueta1Y = MaxY – tamanoRaquetaY

9 - las raquetas no se salen.py


Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Rolling example: PONG


○ Otras operaciones a realizar:

■ Comprobar que haya colisión entre la pelota y la raqueta

● En este caso se comprueban las coordenadas límite de las raquetas y la pelota

○ El rectángulo que ocupa cada una

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


○ Otras operaciones a realizar:

■ Comprobar que haya colisión entre la pelota y la raqueta

# Miramos a ver si hay colision con la raqueta del jugador 1


if pelotaX - RadioPelota < raqueta1X + tamanoRaquetaX and
pelotaX + RadioPelota > raqueta1X and
pelotaY - RadioPelota < raqueta1Y + tamanoRaquetaY and
pelotaY + RadioPelota > raqueta1Y:
# Invertimos la velocidad en el eje X
VelocidadPelotaX = -VelocidadPelotaX

# Miramos a ver si hay colision con la raqueta del jugador 2


if pelotaX - RadioPelota < raqueta2X + tamanoRaquetaX and
pelotaX + RadioPelota > raqueta2X and
pelotaY - RadioPelota < raqueta2Y + tamanoRaquetaY and
pelotaY + RadioPelota > raqueta2Y:
# Invertimos la velocidad en el eje X
VelocidadPelotaX = -VelocidadPelotaX 10 - colisiones con las raquetas.py

● Forma más sencilla: utilizando Sprites

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


○ Otras operaciones a realizar:

■ Detectar cuando un jugador falle, hacer una pausa y reiniciar: poner la pelota en el centro, hacia el otro jugador

# Miramos a ver si la pelota está en el límite en el eje X


if pelotaX <= RadioPelota or pelotaX >= MaxX - RadioPelota:
# Realizamos una pausa
time.sleep(1)
# Ponemos la pelota en el centro
pelotaX = MaxX / 2;
pelotaY = MaxY / 2;
# Invertimos la velocidad en el eje X (para que vaya contra el otro)
VelocidadPelotaX = -VelocidadPelotaX

# Miramos a ver si la pelota está en el límite en el eje Y


if pelotaY < RadioPelota or pelotaY > MaxY - RadioPelota:
# Invertimos la velocidad en el eje Y
VelocidadPelotaY = -VelocidadPelotaY

11 - pausa y reinicio cuando el jugador falla.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


○ Otras operaciones a realizar:

■ Contar los puntos de cada jugador

# Miramos a ver si la pelota está en el límite en el eje X


if pelotaX <= RadioPelota or pelotaX >= MaxX - RadioPelota:
# Sumamos los puntos al jugador correspondiente
if pelotaX <= RadioPelota:
puntos2 += 1
else:
puntos1 += 1
print 'Jugador 1: ', puntos1, ' - Jugador 2: ', puntos2

# Realizamos una pausa


time.sleep(1)
# Ponemos la pelota en el centro
pelotaX = MaxX / 2;
pelotaY = MaxY / 2;
# Invertimos la velocidad en el eje X (para que vaya contra el otro)
VelocidadPelotaX = -VelocidadPelotaX
12 - contando los puntos.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


○ Otras operaciones a realizar:

■ Mostrar unos marcadores

# Rellenamos la pantalla de color negro


pantalla.fill((0,0,0))
# Dibujamos un círculo de color blanco en esa posición en el buffer
pygame.draw.circle(pantalla, BLANCO, (pelotaX,pelotaY),RadioPelota,0)
# Dibujamos como un rectángulo cada raqueta
pygame.draw.rect(pantalla, BLANCO,
(raqueta1X, raqueta1Y, tamanoRaquetaX, tamanoRaquetaY))
pygame.draw.rect(pantalla, BLANCO,
(raqueta2X, raqueta2Y, tamanoRaquetaX, tamanoRaquetaY))

# Creamos los marcadores


marcador1 = tipoLetra.render(str(puntos1), True, BLANCO)
marcador2 = tipoLetra.render(str(puntos2), True, BLANCO)
# y los mostramos
pantalla.blit(marcador1, (MaxX/4, MaxY/8, 50, 50))
pantalla.blit(marcador2, (MaxX*3/4, MaxY/8, 50, 50))

# Actualizamos la pantalla
pygame.display.update()
13 - marcadores.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Rolling example: PONG


○ Otras operaciones a realizar:

■ Mostrar una “pantalla inicial” con un mensaje y una espera

# Se muestra el mensaje inicial


pantalla.blit(tipoLetra.render('PONG', True, BLANCO), (50, MaxY/4, 200, 100))
pantalla.blit(tipoLetra.render('Pulse cualquier tecla', True, BLANCO),
(20, MaxY/2, 200, 100))
pygame.display.update()

# Y se espera hasta que se pulse alguna tecla


esperar = True
while esperar:
for evento in pygame.event.get():
if evento.type == KEYDOWN:
esperar = False

14 - mensaje inicial y espera.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Uso del módulo pygame.mixer

■ Primera acción: inicializarlo

● pygame.mixer.init

■ Antes de eso, hay que indicar los parámetros con la función pygame.mixer.pre_init:

■ Por ejemplo:

pygame.mixer.pre_init(44100, 16, 2, 4096)


pygame.mixer.init()

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Uso del módulo pygame.mixer

■ Parámetros de configuración de pygame.mixer.pre_init:

● Frecuencia

● Tamaño

○ Tamaño en bits de cada muestra del audio (8, 16)

○ Calidad

● Estéreo

○ 1: mono, 2: estéreo

● Buffer

○ Número de muestras que se guardan en un buffer para realizar la reproducción

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Uso del módulo pygame.mixer

■ Parámetros de configuración de pygame.mixer.pre_init:

● Buffer

○ Número de muestras que se guardan en un buffer para realizar la reproducción

■ Valores bajos: menor latencia

● Pero pueden provocar cortes

○ El buffer se vacía más fácilmente

● Latencia: Tiempo entre que se le dice que reproduzca y se empieza a oír

■ Potencia de dos

■ Valor habitual: 4096

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Para cambiar los parámetros
■ Hay que realizar una llamada a pygame.mixer.quit
■ Posteriormente, otra a pygame.mixer.pre_init y después pygame.mixer.init

○ Para cargar un sonido:

■ Pygame.mixer.Sound

● Se le indica un archivo de sonido y devuelve un objeto de la clase Sound

● Constructor con dos parámetros opcionales:

○ loop: Valor numérico, que dice cuántas veces se repetirá:

■ Por ejemplo, si es 5, cuando se reproduzca se repetirá 5 veces (se escuchará 6 veces en total)

■ Si es -1, se repite continuamente, hasta que se llame al método stop

○ Maxtime: tiempo en ms tras el cual se parará la reproducción

■ Útil para sonidos que se reproducen en bucle

● Ejemplo:
sonido = Pygame.mixer.Sound(“sonido.ogg")

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Una vez cargado un sonido, se puede llamar al método play

■ Devuelve una instancia de la clase Channel, o None si la reproducción no se puede realizar

■ Ejemplo:

sonido = Pygame.mixer.Sound(“sonido.ogg")
canal = sonido.play()

■ La función play también acepta parámetros de repetición y tiempo máximo

● Por ejemplo, si se tiene un sonido de aplausos que dura 1.2 segundos y se quiere reproducir durante 5 segundos:

sonido_aplausos = Pygame.mixer.Sound(“aplausos.ogg")
canal = sonido_aplausos.play(-1, 5000)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Métodos de los objetos de sonido:

■ fadeout

● Reduce gradualmente el volumen del sonido en todos los canales

● Toma un único parámetro, que es la longitud de la reducción en milisegundos

■ get_length

● Devuelve la longitud del sonido, en segundos

■ get_num_channels

● Cuenta cuántas veces el sonido se está reproduciendo

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Métodos de los objetos de sonido:

■ play

● Reproduce el sonido

■ stop

● Inmediantamente para cualquier reproducción del sonido

■ get_volume

● Devuelve el volumen del sonido, como un número real entre 0 (silencio) y 1 (mayor volumen)

■ set_volume

● Establece el volumen (0: silencio, 1: máximo volumen)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Si la reproducción (llamada a play) se realiza, se devuelve una instancia de Channel

■ Un canal es una de varias fuentes de sonido que son mezcladas juntas por la tarjeta de sonido

■ Si todos los canales están ocupados, se devuelve None

■ Para saber el número de canales disponibles

● pygame.mixer.get_num_channels

■ Si no hay canales suficientes, se pueden crear más con

● pygame.mixer.set_num_channels

■ Si solamente se quiere reproducir un sonido, se puede ignorar el objeto Channel

● Pero se puede utilizar para realizar efectos

○ Por ejemplo: stereo panning

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Objetos Channel:

■ Permiten ajustar el volumen en el altavoz izquierdo y derecho independientemente

● Crea la ilusión de que un sonido viene de un punto particular de la ventana (stereo panning)

● Método set_volume acepta

dos parámetros: volumen del

altavoz izquierdo y derecho

(ambos entre 0 y 1)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Objetos Channel:

■ Implementan una cola de tal forma que se pueden poner sonidos para su reproducción cuando termine el anterior

● Método queue

■ Permiten la definición de eventos de fin de sonido

● El método set_endevent recibe como parámetro un número de evento que será enviado cuando termine la reproducción

● Este id del evento debería ser superior a USEREVENT para evitar conflictos con el resto de eventos de pygame

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Generalmente lo mejor es dejar a pygame la selección del canal, pero se puede forzar a un objeto Sound a ser reproducido en un
determinado canal mediante el método play de ese canal

■ A ese método se le pueden pasar los parámetros de número de veces que se repetirá y duración

■ De esta forma se pueden reservar canales para sonidos de alta prioridad

● Por ejemplo, que los efectos de sonido de un juego (por ejemplo, disparos) no se vean bloqueados por el sonido de fondo

○ Sonido de fondo (ambiental), no música

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Para reservar un canal: pygame.mixer.set_reserved

■ Se le indica el número de canales que se quiere reservar

■ Por ejemplo, para reservar los dos primeros canales, pygame.mixer.set_reserved(2)

■ Por ejemplo:

pygame.mixer.set_reserved(2)
canal_reservado_0 = pygame.mixer.Channel(0)
canal_reservado_1 = pygame.mixer.Channel(1)

● y para reproducir un sonido por uno de esos canales:

canal_reservado_1.play(gunfire_sound)

○ Se podría indicar repetición y tiempo máximo en la llamada a play

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Métodos de la clase Channel (1/4):

■ play

● Reproduce un sonido en este canal

● Toma como parámetros un objeto Sound y valores opcionales de repetición y tiempo máximo

■ stop

● Termina la reproducción instantáneamente de cualquier sonido en el canal

■ pause

● Pausa temporalmente cualquier sonido en este canal

■ unpause

● Reanuda la reproducción de un canal que ha sido pausado

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Métodos de la clase Channel (2/4):

■ set_volume

● Pone el volumen de este canal.

● Si se da un valor, se aplica a ambos altavoces. Si son dos, al izquierdo y al derecho independientemente

■ get_volume

● Devuelve el volumen actual del canal como un valor entre 0 y 1 (no toma en consideración el volumen estéreo puesto en
set_volume)

■ fadeout

● Reduce gradualmente el volumen en un número determinado de milisegundos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Métodos de la clase Channel (3/4):

■ get_busy

● Devuelve True si un sonido está siendo reproducido actualmente en este canal

■ queue

● Reproduce un sonido cuando el actual termine

● Pone el sonido en la cola de reproducción

■ get_queue

● Devuelve cualquier sonido que se haya puesto en la cola para reproducción, o None si no hay ninguno

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Métodos de la clase Channel (4/4):

■ set_endevent

● Envía el evento cuando el sonido actual ha terminado de reproducirse

● Toma el id del evento, que debería ser superior a USEREVENT

● Si no se indica parámetro, pygame dejará de mandar eventos

■ get_endevent

● Devuelve el evento que será enviado cuando un sonido se termine de reproducir, o NOEVENT si no se ha fijado un evento

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Métodos de pygame.mixer (1/4):

■ pygame.mixer.pre_init

● Establece los parámetros para cuando sea llamada pygame.init

■ pygame.mixer.init

● Inicializa el módulo mixer

■ pygame.mixer.get_init

● Devuelve True si el módulo mixer ha sido inicializado

■ pygame.mixer.quit

● Finaliza el módulo.

● Se llama automáticamente cuando se finaliza el script, pero es necesario llamarlo para reiniciarlo con parámetros diferentes

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Métodos de pygame.mixer (2/4):

■ pygame.mixer.get_num_channels

● Devuelve el número de canales disponibles

■ pygame.mixer.set_num_channels

● Establece el número de canales disponibles

■ pygame.mixer.Channel

● Crea un objeto Channel para un índice de canal dado

■ pygame.mixer.find_channel

● Encuentra un canal actualmente sin uso y devuelve su índice

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Métodos de pygame.mixer (3/4):

■ pygame.mixer.Sound

● Crea un objeto Sound, a partir de un nombre de archivo

■ pygame.mixer.get_busy

● Devuelve True si un sonido está siendo reproducido (en cualquier canal)

■ pygame.mixer.fadeout

● Reduce gradualmente el volumen de todos los canales a 0

● Toma como parámetro el tiempo

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Métodos de pygame.mixer (4/4):

■ pygame.mixer.pause

● Pausa temporalmente la reproducción en todos los canales

■ pygame.mixer.unpause

● Reanuda la reproducción del sonido pausado

■ pygame.mixer.stop

● Para la reproducción en todos los canales

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
sonidoRaqueta = pygame.mixer.Sound('Ping_Pong.wav');
sonidoAplausos = pygame.mixer.Sound('Aplausos.wav');

# Miramos a ver si hay colision con la raqueta del jugador 1


if pelotaX - RadioPelota < raqueta1X + tamanoRaquetaX and

pelotaX + RadioPelota > raqueta1X and pelotaY - RadioPelota <
raqueta1Y + tamanoRaquetaY and
pelotaY + RadioPelota > raqueta1Y:
# Invertimos la velocidad en el eje X
VelocidadPelotaX = -VelocidadPelotaX
# Reproducimos el sonido de la raqueta
sonidoRaqueta.play();

# Miramos a ver si hay colision con la raqueta del jugador 2


if pelotaX - RadioPelota < raqueta2X + tamanoRaquetaX and
pelotaX + RadioPelota > raqueta2X and
pelotaY - RadioPelota < raqueta2Y + tamanoRaquetaY and
pelotaY + RadioPelota > raqueta2Y:
# Invertimos la velocidad en el eje X
VelocidadPelotaX = -VelocidadPelotaX
# Reproducimos el sonido de la raqueta
sonidoRaqueta.play();
15 - con sonidos.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Música:

■ Las funciones anteriores están pensados para efectos de sonido

● Sonidos cortos, que ocupan poco

● Sin embargo, la música suele ocupar mucho más

○ Se suele reproducir por streaming

■ Ir tomando trozos de la música del archivo y reproducirlos

○ Si se hiciese con las funciones anteriores, obligaría a tener todo el archivo de música en memoria

■ Podría ralentizarse la ejecución en la carga / cola / reproducción

○ Submódulo pygame.mixer.music

○ Válido para cualquier archivo de sonido grande

■ Voz

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Música:

■ pygame.mixer.music permite reproducir mp3 y ogg

■ Para reproducir música:

● pygame.mixer.music.load

○ Se le indica el nombre del archivo

● pygame.mixer.music.play

● Ejemplo:
pygame.mixer.music.load(“musica.ogg")
pygame.mixer.music.play()

● No hay objeto “Music” ni “Channel”

○ Sólo se puede reproducir un archivo de música en cada momento

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Funciones del módulo pygame.mixer.music (1/5):

■ pygame.mixer.music.load

● Carga un archivo de música para su reproducción

■ pygame.muxer.music.queue

● Pone otro archivo para ser reproducido cuando el actual termine

● Toma un único parámetro, que el es nombre del archivo que se quiere poner en cola

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Funciones del módulo pygame.mixer.music (2/5):

■ pygame.mixer.music.play

● Comienza la reproducción de un archivo de música cargado

● Toma como parámetros:

○ El número de veces que se quiere repetir después de la primera reproducción

■ Si este valor es -1, se reproduce hasta que se llame a pygame.mixer.stop

○ El punto en el que se quiere comenzar la reproducción

■ pygame.mixer.music.stop

● Para la reproducción de la música

■ pygame.mixer.music.rewind

● Recomienza el archivo de música desde el principio

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Funciones del módulo pygame.mixer.music (3/5):

■ pygame.muxer.music.pause

● Pausa temporalmente la música

■ pygame.mixer.music.unpause

● Reanuda la reproducción de la música que ha sido pausada

■ pygame.mixer.get_busy

● Devuelve True si hay música reproduciéndose

■ pygame.mixer.music.get_pos

● Devuelve el tiempo que la música se ha reproducido, en ms

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Funciones del módulo pygame.mixer.music (4/5):

■ pygame.mixer.music.set_volume

● Establece el volumen de la música, entre 0 y 1

● Cuando se cargue nueva música, el volumen se reseteará a 1

■ pygame.mixer.music.get_volume

● Devuelve el volumen de la música

■ pygame.mixer.music.fadeout

● Reduce gradualmente el volumen en un período de tiempo, pasado como parámetro en ms

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sonidos
○ Funciones del módulo pygame.mixer.music (5/5):

■ pygame.mixer.music.get_endevent

● Devuelve el evento de fin que será mandado, o 0 si no hay evento

■ pygame.mixer.music.set_endevent

● Establece el evento que será enviado cuando la reproducción haya finalizado

● Toma como parámetro el identificador del evento, que debería de ser superior a USEREVENT

○ Si no se especifica parámetro, pygame deja de enviar eventos de fin

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● ¿Dónde se puede encontrar sonidos para el juego?


○ http://soungle.com/

■ Buscador de sonidos

○ http://spriters-resource.com/

■ Sprites, modelos, imágenes, sonidos de videojuegos famosos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Introduciendo una imagen de fondo


# Imagen de fondo
imagenFondo = pygame.image.load('pistaTenis.jpg').convert();

# Bucle infinito
while True:

# Ponemos la imagen de fondo
pantalla.blit( imagenFondo, (0, 0))
Posición donde se coloca la imagen
# Dibujamos un círculo de color blanco en esa posición en el buffer
pygame.draw.circle(pantalla, BLANCO, (pelotaX,pelotaY),RadioPelota,0)

# Dibujamos como un rectángulo cada raqueta


pygame.draw.rect(pantalla, BLANCO, (raqueta1X, raqueta1Y, tamanoRaquetaX, tamanoRaquetaY))
pygame.draw.rect(pantalla, BLANCO, (raqueta2X, raqueta2Y, tamanoRaquetaX, tamanoRaquetaY))

# Creamos los marcadores


marcador1 = tipoLetra.render(str(puntos1), True, BLANCO)
marcador2 = tipoLetra.render(str(puntos2), True, BLANCO)
# y los mostramos
pantalla.blit(marcador1, (MaxX/4, MaxY/8, 50, 50))
pantalla.blit(marcador2, (MaxX*3/4, MaxY/8, 50, 50))

# Actualizamos la pantalla
pygame.display.update() 16 - con imagen de fondo.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Orientación a objetos

Raqueta Pelota

controlaY() update()
colision() dibuja()
dibuja()

○ Método update

■ Para cada objeto, actualizar su estado

# Comprobamos que ninguna de las dos raquetas se hayan ido por arriba o abajo
jugador1.controlaY()
jugador2.controlaY()

# Actualizamos el comportamiento de la pelota


pelota.update(jugador1, jugador2)

17 - orientacion a objetos.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Orientación a objetos
○ Los objetos que se muestran por pantalla son subclases de Sprite

■ Son Sprites

■ Un Sprite es cualquier objeto que aparece en el videojuego, aunque normalmente suele relacionarse a todo
personaje o animación dentro del mismo.

■ Normalmente tienen una serie de atributos, entre los más básicos están las imágenes que lo componen y su
posición.

● Imágenes: atributo image


Sprite
● Posición: atributo rect

Raqueta Pelota

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
La pelota es una
● Sprites subclase de Sprite
class Pelota(pygame.sprite.Sprite):
"La pelota y su comportamiento" El Sprite tiene un
atributo de imagen
def __init__(self, sonidoRaqueta, sonidoPunto):
# Primero invocamos al constructor de la clase padre que se carga de
pygame.sprite.Sprite.__init__(self); disco
# Cargamos la imagen El Sprite tiene un atributo
self.imagen = pygame.image.load('pelota.png'); rectángulo (zona) donde
# El rectangulo donde estara la imagen
self.rect = self.imagen.get_rect() está situado
self.rect.centerx = MaxX/2;
self.rect.centery = MaxY/2; Se coloca ese
# El resto de atributos rectángulo inicialmente
self.velocidad = [2, 2];
en medio de la pantalla
self.sonidoRaqueta = sonidoRaqueta
self.sonidoPunto = sonidoPunto
Para dibujar el sprite, se
# Dibuja la pelota dibuja su imagen en la
def dibuja(self, pantalla):
pantalla.blit(self.imagen, self.rect);
posición del rectángulo

18 - con sprites.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Sprites
class Raqueta(pygame.sprite.Sprite):
"Las raquetas de ambos jugadores"

def __init__(self, posicion, posicionMarcador):


# Primero invocamos al constructor de la clase padre
pygame.sprite.Sprite.__init__(self);
# Cargamos la imagen
self.imagen = pygame.image.load("raqueta.png");
# El rectangulo donde estara la imagen
self.rect = self.imagen.get_rect()
self.rect.centerx = posicion[0];
self.rect.centery = posicion[1];
# El resto de atributos
self.puntos = 0
self.posicionMarcador = posicionMarcador
self.tipoLetra = pygame.font.SysFont('arial', 96)

18 - con sprites.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ventajas de extender la clase Sprite: Heredar atributos y métodos ya implementados.


○ Por ejemplo: Atributo rect:

■ Rectángulo que ocupa el Sprite

■ Para calcular si dos sprites están en colisión

● Para calcular si sus rectángulos se solapan, se puede utilizar el método de la clase Rect: colliderect

self.rect.colliderect(pelota.rect)
18 - con sprites.py

○ La colisión se comprueba entre los rectángulos que ocupan los Sprites en pantalla

■ Atributo rect

■ Para pygame los Sprites son rectángulos

● Aunque las imágenes tengan partes con transparencia

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ventajas de extender la clase Sprite: Heredar atributos y métodos ya implementados.


○ Por ejemplo: Atributo rect:

■ Sin embargo, puede ser útil definir varios atributos Rect para poder definir nuevos rectángulos de colisión además del principal

● Por ejemplo:

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ventajas de extender la clase Sprite: Heredar atributos y métodos ya implementados.


○ Por ejemplo: Atributo mask:

■ Contiene una máscara del Sprite

● Opcional

○ Si no se calcula, la calcula pygame en el momento en que la necesita

● Útil para calcular colisiones más precisas

○ Que sigan el contorno del dibujo del Sprite

○ Ver más adelante

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Además, al extender la clase Sprite, implementar el método update:


○ Actualiza el estado del Sprite en cada frame, realizando tareas como:

■ Mueve el Sprite

● Comprueba si ese movimiento es correcto, y lo

● realiza si lo es

○ Por ejemplo, que no choque con alguna pared

■ Cambia la imagen que se muestra del Sprite

■ Disminuye/aumenta algún nivel de energía del

■ Sprite

■ etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sprites
○ Las imágenes de los Sprites suelen tener una parte que es transparente, por ello, se marca
mediante el canal llamado: Canal Alfa, que indica qué partes de la imagen son transparentes y
cuáles opacas

■ Grado de transparencia/opacidad de cada píxel

■ Imágenes en RGBA

● RGB + Alpha

● Canal alfa
○ Normalmente los programas de dibujo y tratamiento de imagen suelen asociar el canal alfa
mostrando un fondo de cuadros y la imagen sobre este fondo, indicando que la imagen en esa
parte es transparente o translúcida.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Canal alfa
○ Sin embargo, el canal alfa no solamente contiene información para cada píxel de transparente/opaco

■ Cada píxel tiene un valor que indica el grado de transparencia

Valores altos (próximos


a blanco): más
intensidad de color
En canal alfa: mayor
opacidad

Valores bajos (próximos


a negro): menos
intensidad de color
En canal alfa: mayor
transparencia
ROJO VERDE AZUL

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Formatos de imágenes
○ JPEG (Joint Photographic Expert Group): ○ PNG (Portable Network Graphics):
■ Formato diseñado para fotografías ■ Formato de imágenes muy versátil
■ Compresión que reduce el tamaño de la ■ Comprime muy bien el tamaño sin pérdida
imagen
de calidad
● Pero reduce también la calidad de la
imagen ■ Soporta canales alfa

● Tiende a difuminar imágenes ■ Indicado para el desarrollo de videojuegos


○ Bordes ○ Otros formatos: GIF, BMP, PCX, TGA, TIF,
○ No es apropiada para Sprites LBM, PBM, XPM.

○ Regla general:

■ Usar JPEG sólo para imágenes grandes

● Por ejemplo, decorado

■ En el resto, usar PNGs

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Sprites
○ Se puede tener dos tipos de Sprite en un juego

■ Dinámicos: se referirán a todos los personajes y animaciones

● En general, todos los Sprites que tengan algún tipo de comportamiento asociado

○ Aunque no se muevan

○ Por ejemplo: personajes, zonas en las que se pierde vida, plataformas que no se mueven pero cambian su imagen
conforme pasa el tiempo, etc.

■ Estáticos: se referirán a todos los elementos del juego que no sean el fondo de la pantalla y que no se muevan a lo largo del
juego

● Los que permanecen sin ser alterados

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para dibujar un Sprite estático, basta con dibujar (blit) su imagen en una determinada posición
pantalla.blit(miSprite.image, miSprite.rect);

Image: Posición: Rectángulo -


Atributo del objeto Atributo del objeto
○ Al dibujarlo, en aquellos píxeles con canal alfa bajo (0) se verá lo que hubiera en esa posición del buffer

■ Nivel de transparencia
● Sin embargo, un Sprite dinámico debe de mostrar una animación
○ Por ejemplo, el caminar de un personaje

○ Esa animación se carga de un fichero

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para animar un juego 2D, cada Sprite tiene asociado un array de imágenes
○ Estas imágenes se muestran secuencialmente cada cierto tiempo para dar sensación de animación.

○ Cada una de estas imágenes se suele denominar frame o fotograma.


● Normalmente, todas las animaciones se pintan sobre una misma imagen llamada: hoja de Sprites (o Sprite
sheet).

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Estas hojas suelen montarse para no estar trabajando con cientos (o, a veces, miles) de imágenes.
● Para coger cada una de las imágenes que formarán la animación se puede utilizar la siguiente función:

# Coge una parte de la imagen


def divide_imagen(image, rect):
subimage = image.subsurface(rect)
return subimage, rect # Devuelve la subimagen y su tamaño

● Con esto no se crea una nueva imagen, si no que se referencia sobre la anterior, siendo más eficiente (ya
que no se ocupa tanta memoria) y más óptimo.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Generalmente, las hojas de Sprites no tienen canal alfa


○ Necesario establecer qué píxeles tienen transparencia

○ La forma de hacer esto mismo, es utilizar un color key, o color base

■ Se utiliza para marcar la transparencia

● Los píxeles con ese color base (RGB) serán transparentes

■ Por tanto, estos Sprites suelen tener fondos chillones ya que es poco probable que se encuentre ese color en la imagen.

● Generalmente se toma como color base el color del píxel en la posición (0, 0) de la imagen

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Esto se puede hacer con la siguiente función

# El colorkey es es color que indicara la transparencia


# Si no se usa, no habra transparencia
# Si se especifica -1, el color de transparencia sera el del pixel (0,0)
# Si se especifica un color, ese sera la transparencia
def load_image(name, colorkey=None):
fullname = os.path.join('imagenes', name)
try:
image = pygame.image.load(fullname)
except pygame.error, message:
print 'Cannot load image:', fullname
raise SystemExit, message
image = image.convert()
if colorkey is not None:
if colorkey is -1:
colorkey = image.get_at((0,0))
image.set_colorkey(colorkey, RLEACCEL)
return image
1 – toda la imagen como Sprite.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Si se utilizase una imagen JPEG


como hoja de Sprites
○ Las imágenes JPEG tienen a difuminar

■ Especialmente crítico en los bordes de la imagen con el fondo

● Parte del fondo (cercana al objeto) no será del mismo color que el
fondo

● Al aplicar el color key, no se reconocerán iguales al color en (0,0)

○ No se reconocerán

como transparentes

○ Sprite con un «halo»

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ejemplo de hoja de Sprites

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para mostrar una animación


○ El Sprite contiene una referencia a la hoja de Sprites

○ En cada ciclo del bucle:

■ Método update:
● Actualiza el estado general del Sprite
○ Qué subimagen de la hoja se va a mostrar (entre otras cosas)

■ ¡¡¡Necesario saber las coordenadas de cada subimagen!!!


■ Método para dibujar
● Toma esa subimagen y la muestra en la posición determinada

● El método blit de pygame permite especificar:


○ Imagen
○ Región de esa imagen a mostrar (subimagen)

○ Posición donde se dibuja

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Es necesario, por tanto, conocer las coordenadas en la hoja de Sprite donde está cada frame
○ Por ejemplo:

■ (5, 25, 34, 40)

(0, 0)

(5, 25) 34

40

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Es necesario, por tanto, conocer las coordenadas en la hoja de Sprite donde está cada frame
○ Por ejemplo:

■ (5, 25, 34, 40)

■ (42, 25, 34, 40)

(0, 0)
34
(42, 25)
40

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Habitualmente alineada inferiormente


● Muchas veces, los frames están equiespaciados
○ Por ejemplo:

● Estas dos condiciones facilitan el proceso de saber las coordenadas de cada frame
■ Una multiplicación sencilla

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
while True:
reloj.tick(60)

for event in pygame.event.get():


if event.type == QUIT:
pygame.quit()
sys.exit()
teclasPulsadas = pygame.key.get_pressed()

# Si la tecla es Escape
if teclasPulsadas[K_ESCAPE]:
pygame.quit()
sys.exit()
# Indicamos la acción a realizar segun la tecla pulsada
elif teclasPulsadas[K_LEFT]:
jugador.mover(IZQUIERDA)
elif teclasPulsadas[K_RIGHT]:
jugador.mover(DERECHA)

# Actualizamos el jugador
jugador.update()

# Dibujar el fondo de color


pantalla.fill((133,133,133))
# Dibujar el Sprite
jugador.dibujar(pantalla)
# Actualizar la pantalla
pygame.display.update() 2 – sprite animado.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
class Jugador(pygame.sprite.Sprite):
def __init__(self):
# Primero invocamos al constructor de la clase padre
pygame.sprite.Sprite.__init__(self);
# Se carga la hoja El Sprite contiene una referencia a la hoja
self.hoja = load_image('Jugador.png',-1)
self.hoja = self.hoja.convert_alpha()
# El rectangulo y la posicion que tendra El Sprite contiene atributos como su posición
self.rect = pygame.Rect((7,25), (30, 40)) (una esquina del rectángulo)
self.posicionx = 100
self.posiciony = 100
# El movimiento que esta realizando
self.movimiento = QUIETO
# Leemos las coordenadas de un archivo de texto

def mover(self, direccion):


self.movimiento = direccion

def dibujar(self, pantalla): Al método blit se le puede indicar:


# Parametros de blit: • Imagen que se dibuja (hoja de Sprites)
# Imagen
# Posicion en la pantalla
• Posición donde se dibuja
# Rectangulo dentro de la imagen • Parte de la imagen (hoja de Sprites) que se dibuja
pantalla.blit(self.hoja, (self.posicionx, self.posiciony),
self.coordenadasHoja[self.numPostura][self.numImagenPostura])

2 – sprite animado.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

def update(self):
# Si vamos a la izquierda
if self.movimiento == IZQUIERDA:
# Actualizamos la posicion
self.posicionx -= 2
# Actualizamos la imagen a mostrar
self.numImagenPostura += 1
if self.numImagenPostura >= len(self.coordenadasHoja[self.numPostura]):
self.numImagenPostura = 0;
# Su siguiente movimiento (si no se pulsa mas) sera estar quieto
self.movimiento = QUIETO
# Si vamos a la derecha En cada llamada al método update se
elif self.movimiento == DERECHA: calcula en qué postura está el Sprite:
# Actualizamos la posicion
self.posicionx += 2
Se muestra la postura siguiente (incorrecto)
# Actualizamos la imagen a mostrar
self.numImagenPostura -= 1
if self.numImagenPostura < 0:
self.numImagenPostura = len(self.coordenadasHoja[self.numPostura])-1
# Su siguiente movimiento (si no se pulsa mas) sera estar quieto
self.movimiento = QUIETO
2 – sprite animado.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Un problema que ocurre es que la posición del Sprite en pantalla se da sobre la esquina superior izquierda
○ La coordenada y crece de arriba a abajo

○ Si el personaje tiene una vista lateral y hay animaciones con imágenes más pequeñas que otras, las cuadra sobre esta esquina haciendo que el Sprite “vuele”

■ Por ejemplo, si el Sprite está en la posición (x, y), y se dibuja en esa misma posición:

■ Necesario alinear los Sprites abajo

(x, y) (x, y)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Un problema que ocurre es que la posición del Sprite en pantalla se da sobre la esquina superior
izquierda
○ Necesario recalcular la posición del Sprite en función de la altura que tiene:

■ La posición (x, y) del Sprite será la de la esquina inferior izq.

■ Si el frame tiene una altura h, se pinta en la posición (x, y-h)

(x, y-h) (x, y-h)

h
h

(x, y) (x, y)

● Problema solucionado más adelante en pygame

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Un problema que ocurre es que la posición del Sprite en pantalla se da sobre la esquina superior izquierda
○ Este problema podría darse, de forma similar, en el eje x, si es necesario alinear los sprites a la derecha

■ La coordenada x crece de izquierda a derecha

■ Si su coordenada x es la del lado izquierdo y se dibuja en esa coordenada x, se alinearán por la izquierda

○ Problema solucionado más adelante en pygame

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Otro problema con el código anterior:


○ Cada vez que se llama a update se cambia el frame que se va a mostrar

■ Si se tiene el juego a 60 FPS, se llama 60 veces en un segundo

● La animación (Sprite) se mueve demasiado rápido

■ Es necesario ralentizarla

● Indicarle que cambie el frame cuando transcurra un número determinado de llamadas a la función update

○ Sincronizar este cambio de frame con el desplazamiento que realice el Sprite

3 – sprite se mueve mas despacio.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Otro problema con el código anterior:


○ Cada vez que se llama a update se cambia el frame que se va a mostrar

■ Es necesario ralentizarla

● Si el Sprite tiene n imágenes (frames) consecutivas y la transición entre ellas tiene que durar t segundos (𝑛𝑛⁄𝑡𝑡 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖/𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠) cada imagen tiene que durar en
pantalla

○ 𝑡𝑡⁄
𝑛𝑛 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠/𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖

● Ejemplo: secuencia que tiene que durar 1 segundos:

○ 10 imágenes

○ 1/10 = 0.1 segundos/imagen

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Otro problema con el código anterior:


○ Cada vez que se llama a update se cambia el frame que se va a mostrar

■ Es necesario ralentizarla

● Si el juego está sincronizado para correr a FPS frames por segundo

○ Se llamará FPS veces a la función update por segundo (updates/segundo)

● En consecuencia, habrá que cambiar la imagen del Sprite cada

○ 𝐹𝐹𝐹𝐹𝐹𝐹 𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢
�𝑠𝑠𝑠𝑠𝑠𝑠 × 𝑡𝑡⁄𝑛𝑛 𝑠𝑠𝑠𝑠𝑠𝑠
�𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 =
(𝐹𝐹𝐹𝐹𝐹𝐹×𝑡𝑡)⁄ 𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢
𝑛𝑛 �𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖

● En el ejemplo anterior, si el juego va a 60 FPS:

(60×1) 𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢𝑢
○ �10 = 6 �𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖
● En consecuencia, habrá que cambiar la imagen cada 6 llamadas a la función update

○ Llevar un contador

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Además, las animaciones de los personajes suelen venir en la hoja dadas de un lado únicamente, debido
a que se pueden invertir (flip) respecto al eje que se desee.
● Para girar una imagen se puede usar un código similar a:

# Damos la vuelta a la imagen para que mire al otro lado


img = pygame.transform.flip(imagen[0], 1, 0)

○ Segundo y tercer parámetro

■ Inversión en los eje x e y (valores booleanos)

● Normalmente, también se suele soportar las rotaciones con un ángulo arbitrario


○ Si la imagen tiene una vista superior, una única imagen nos sirve para todos los ángulos en el juego.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

def dibujar(self, pantalla):

if self.mirando == IZQUIERDA:
pantalla.blit(
self.hoja.subsurface(
self.coordenadasHoja[self.numPostura][self.numImagenPostura]),
(self.posicionx, self.posiciony))

elif self.mirando == DERECHA:


pantalla.blit(
pygame.transform.flip(
self.hoja.subsurface(
self.coordenadasHoja[self.numPostura][self.numImagenPostura]),
1, 0),
(self.posicionx, self.posiciony))

4 - mirando hacia los dos lados.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Otras opciones sobre imágenes que se pueden realizar:


○ Módulo transform

○ Imágenes: Objetos Surface

■ pygame.transform.flip

● Invertir una imagen en el eje x y/o y

■ pygame.transform.scale

● Cambiar el tamaño a una nueva resolución

■ pygame.transform.rotate

● Rotar una imagen

■ pygame.transform.rotozoom

● Rotar y escalar de forma combinada

■ pygame.transform.scale2x

● Devuelve una imagen con el doble de tamaño

■ etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● ¿Cómo obtener las coordenadas de un frame en la hoja de Sprites y/o generar hojas de Sprites nuevas?
○ http://www.spritecow.com/

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Cada Sprite de un mismo tipo (jugadores, cada tipo de enemigo) deberá pertenecer a la misma clase.
○ Extender la clase Sprite

○ Incluso cuando el Sprite no haga nada, conviene extender esta clase y el método update.

● Cada Sprite puede pertenecer a un grupo, lo que facilita una división conceptual.
○ Por ejemplo:

■ Se puede tener un grupo de Sprites que sean Escenario, con Sprites tanto estáticos como dinámicos en él

■ También se puede tener otro grupo que fuera Enemigos, y otro llamado Personajes

○ Un mismo Sprite puede estar en varios grupos

■ Por ejemplo:

● Los Sprites en Personajes podrían estar en dos grupos: Jugadores y Enemigos

● Los Sprites en Enemigos, a su vez, podrían estar en distintos grupos según el tipo de enemigos que sean

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Los métodos de un Sprite son los siguientes:


○ update(*args)

■ Controla el comportamiento del Sprite

■ Necesario implementar este método al extender la clase Sprite

○ add(*groups)

■ Añade el Sprite a uno o varios grupos

○ remove(*groups)

■ Elimina el Sprite de uno o varios grupos

○ kill()

■ Elimina el Sprite de todos los grupos

○ alive()

■ Devuelve verdadero si el Sprite está en algún grupo

○ groups()

■ Devuelve la lista de grupos donde está el Sprite

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Los métodos de la clase Group son los siguientes (1/2):


○ sprites()

■ Una lista de todos los Sprites de este grupo

○ copy()

■ Duplica el grupo

○ add(*sprites)

■ Añade Sprites del grupo

○ remove(*sprites)

■ Elimina Sprites del grupo

○ has(*sprites)

■ Comprueba que existan todos los Sprites en el grupo

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Los métodos de la clase Group son los siguientes (2/2):


○ draw(surface)

■ Dibuja todos los Sprites

○ update(*args)

■ Llama al método update de todos los Sprites

○ clear(dest,bg)

■ Dibuja un fondo sobre los Sprites (los borra)

○ empty()

■ Elimina todos los Sprites

● Aunque también acepta los operadores estándar de Python:


○ in, len, bool, iter

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Se puede crear un grupo instanciando la clase Group


○ En el constructor se puede indicar qué Sprites están en el grupo

■ Posteriormente se pueden añadir más

# Creamos los jugadores


jugador1 = Jugador()
jugador2 = Jugador()

# Creamos el grupo de Sprites de jugadores


grupoJugadores = pygame.sprite.Group( jugador1, jugador2 )

5 – con grupos.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● La clase Group permite mantener referencias a los Sprites

○ Elimina la necesidad de crear un array u otra estructura para mantener referencias a los mismos

■ El objeto Group realiza esta tarea

● Una ventaja de utilizar grupos es que se pueden automatizar las llamadas a ciertas funciones

■ grupoJugadores.update()

● Se llama al método update de cada Sprite del grupo

○ ¡¡¡Necesario que cada Sprite haya definido el método!!!

■ Aunque no haga nada (esté vacío)

● Además, ese método puede recibir parámetros

○ Se pasan como parámetros a cada una de las llamadas a update de los miembros del grupo

# Actualizamos los jugadores actualizando el grupo


grupoJugadores.update()

# Dibujar el fondo de color


pantalla.fill((133,133,133))

# Dibujar el grupo de Sprites


grupoJugadores.draw(pantalla)
5 – con grupos.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D
● grupoJugadores.draw(pantalla)

○ Se dibuja cada Sprite del grupo

■ Para ello, es necesario que los Sprites tengan los atributos

● image -> imagen (o subimagen) que se mostrará en pantalla

● rect -> posición

○ Se elimina la necesidad de que cada Sprite implemente una función «dibujar»

■ La llamada a la función draw del grupo ya dibuja todos los Sprites

○ Orden de llamada: Montar la escena

■ Ejemplo:

● Llamar al método draw del grupo de las plataformas

● Después, llamar al método draw del grupo de los personajes

# Actualizamos los jugadores actualizando el grupo


grupoJugadores.update()

# Dibujar el fondo de color


pantalla.fill((133,133,133))

# Dibujar el grupo de Sprites


grupoJugadores.draw(pantalla)
5 – con grupos.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Comprobar si hay colisión entre un Sprite y un grupo de Sprites


○ Por ejemplo, un jugador con el grupo de proyectiles o items

○ Función spritecollideany(sprite, grupoSprites)

■ Dice si el sprite está en colisión con algún Sprite del grupo grupoSprites y devuelve con cuál

● Si está en colisión con varios, devuelve sólo uno

■ Devuelve None si no hay colisión entre el Sprite y ninguno del grupo

○ Función spritecollide(sprite, grupo, dokill)

■ Devuelve la lista de Sprites del grupo con los que está en colisión

● Vacía si no colisiona con ninguno

■ dokill es un valor booleano que dice si los Sprites con los que está en colisión deben de ser eliminados del grupo

○ Función groupcollide(grupoSprites1, grupoSprites2, dokill1, dokill2)

■ Devuelve la lista de Sprites que colisionan

■ dokill1, dokill2 dicen si los Sprites en colisión se deben borrar de los grupos 1 y 2 respectivamente

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Un ejemplo de pasar un parámetro a la función update podría ser el siguiente, pasando el tiempo:
○ Sincronización por tiempo

def update(self,tiempo):
# Si vamos a la izquierda
posicionx y posiciony contienen
if self.movimiento == IZQUIERDA: los valores reales de la posición
# La postura actual sera estar caminando del Sprite (en punto flotante)
self.numPostura = ANDANDO
# Esta mirando a la izquierda
self.mirando = IZQUIERDA
# Actualizamos la posicion Sin embargo, el Sprite se dibujará
self.posicionx -= VELOCIDAD_JUGADOR * tiempo donde esté el atributo rect, así
self.rect.left = self.posicionx
# Actualizamos la imagen a mostrar que se sitúa rect en esa posición
self.actualizarPostura()
# Si vamos a la derecha En este caso, posicionx indica la
… coordenada del lado izquierdo del
rectángulo

6 - con postura de estar quieto y movimiento medido.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Además de rect.left, el rectángulo se podría situar de forma relativa a su margen derecho (rect.right) o
izquierdo (rect.left) en el eje x, y con respecto al superior (rect.top) o inferior (rect.bottom) en el eje y
○ Alinearlo con respecto a izq/dcha o arriba/abajo

○ Si se utiliza rect.bottom para situar el rectángulo, se soluciona el problema anterior de tener frames de distinta altura

■ Ejemplo: saltos

○ Solución de pygame

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
def update(self, tiempo):
# Si vamos a la izquierda

# Si estamos en el aire
if self.numPostura == SPRITE_SALTANDO:
# Actualizamos la posicion
self.posiciony -= self.velocidady * tiempo
# Si llegamos a la posicion inferior, paramos de caer
if (self.posiciony>300):
self.numPostura = SPRITE_QUIETO
self.posiciony = 300
self.velovidady = 0
# Si no, aplicamos el efecto de la gravedad
else:
self.velocidady -= 0.004
# Nos ponemos en esa posicion en el eje y
self.rect.bottom = self.posiciony

# Actualizamos la imagen a mostrar


self.actualizarPostura()
return

7 - con saltos.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● ¿Dónde se puede encontrar Sprites e imágenes para el juego?


○ http://spritedatabase.net/

■ Una de las mayores colecciones de imágenes y hojas de Sprites

■ Utilidades en http://spritedatabase.net/download

○ http://spriters-resource.com/

■ Sprites, modelos, imágenes, sonidos de videojuegos famosos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Al inicializar un Sprite
○ Cargar hoja de Sprites

○ Si varios Sprites tienen las mismas imágenes

■ Instancias de la misma clase

● La misma hoja de Sprites

■ Se carga varias veces el mismo archivo

● ¡¡¡Las operaciones de lectura/escritura de disco son las más costosas en tiempo!!!

■ En lugar de ello, cargar la imagen una vez y reutilizarla

● Realizar este proceso a través de un gestor de recursos

○ Válido para cualquier archivo que se cargue de disco

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Gestores de recursos
○ En el código mostrado hasta ahora, cada vez que se crea un objeto se cargan sus datos (imagen, sonido, etc.) de disco

■ Ineficiente si es necesario crear distintos objetos durante el juego/animación

○ En lugar de ello, usar un gestor de recursos

■ Un objeto que se encarga de gestionar la carga desde archivo de los recursos necesarios:

● Archivos de configuración

● Imágenes

● Sonidos

● Música

● Fuentes tipográficas

● Texturas

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Gestores de recursos
○ Cuando se necesita cargar un recurso de disco, en lugar de cargarlo directamente, se hace una llamada al gestor

■ El gestor es el que lo carga y lo devuelve

■ Pero mantiene la referencia al recurso cargado

● Si vuelven a pedir que se cargue ese recurso, no lo carga de nuevo

○ Devuelve esa referencia

○ Ahorro de tiempo

■ De esta manera, todos los objetos que han cargado ese recurso tienen una referencia al mismo objeto

● Ahorro de memoria

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Gestores de recursos
○ El gestor de recursos, por tanto, debe implementar un almacenamiento

■ <nombre de recurso> - <recurso cargado>

○ Cuando se le pasa un nombre de archivo, lo compara con los almacenados

■ Comparación de strings

● Ineficiente

■ No almacenar el string del fichero sino el hash

● Comparar hash de ambos nombres

■ Implementación más sencilla en python:

● Tipo de datos Dictionary

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Gestores de recursos
○ Un gestor de recursos para un juego más complejo podría tener métodos especializados en cada tipo de recurso

■ Por ejemplo:

● CargarTextura()

● CargarSonido()

● CargarImagen()

● etc.

■ Cada método sabe en qué carpeta buscar

■ Mantener una lista de recursos distinta para cada tipo

● O la misma lista si el lenguaje de programación lo permite (python)

■ O extender esta clase para especializar el gestor

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Gestores de recursos
○ Un gestor de recursos genérico podría, además:

■ Implementar métodos para registrar las carpetas donde está cada recurso

■ Implementar métodos para “dar de alta” cada tipo de recurso

● Indicando en qué carpeta se encuentra

■ Implementar métodos que permitan mantener la integridad de los recursos

● Que detecte si algún objeto los ha modificado, y restituya su estado original

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Gestores de recursos
○ Además, en un videojuego más complejo un gestor de recursos podría gestionar otros recursos más complejos

■ Recursos que no necesariamente se corresponden cada uno con un archivo

■ Por ejemplo:

● Escenas

○ Ver más adelante

● Modelos 3D

● Scripts

● Personajes, Sprites, …

● etc.

■ Crear «plantillas»

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Gestores de recursos
○ Implementación de un gestor de recursos:

■ Patrón Singleton

■ Métodos de clase

● Ejemplo: Carga de una hoja de Sprites:

# Se carga la hoja de Sprites


self.hoja = GestorRecursos.CargarImagen('Jugador.png',-1)
3 – Juego/personajes.py

● Ejemplo de gestor de recursos:

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
class GestorRecursos(object):
recursos = {} Conjunto de recursos ya cargado
Si la imagen ha sido cargada
@classmethod anteriormente, se devuelve
def CargarImagen(cls, nombre, colorkey=None):
# Si el nombre de archivo está entre los recursos
if nombre in cls.recursos:
# Se devuelve ese recurso
return cls.recursos[nombre]
# Si no ha sido cargado anteriormente
else:
# Se carga la imagen indicando la carpeta
fullname = os.path.join('imagenes', nombre)

# Se almacena
Si no, se carga, cls.recursos[nombre] = imagen
se almacena y # Se devuelve
se devuelve
return imagen 3 – Juego/gestorRecursos.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
@classmethod
def CargarArchivoCoordenadas(cls, nombre):
# Si el nombre de archivo está entre los recursos
if nombre in cls.recursos:
# Se devuelve ese recurso
return cls.recursos[nombre]
# Si no ha sido cargado anteriormente
else:
# Se carga el recurso indicando la carpeta
fullname = os.path.join('imagenes', nombre)
pfile=open(fullname,'r')
datos=pfile.read() Se realiza el mismo
pfile.close() proceso para un
archivo de
# Se almacena
configuración
cls.recursos[nombre] = datos
# Se devuelve
return datos
3 – Juego/gestorRecursos.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Si se desea añadir interacción de los Sprites con el resto de los objetos del juego:
○ El resto de los objetos también son Sprites

■ Están en los grupos correspondientes

● Un Sprite puede estar en más de un grupo

■ Por ejemplo:

● Plataformas

● Enemigos

● Sprites dinámicos

○ Contiene todos los dinámicos: personajes, enemigos, proyectiles, etc.

● Items

● etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Si se desea añadir interacción de los Sprites con el resto de los objetos del juego:
○ ¿Quién mantiene referencia a esos Sprites?

■ Clase “Fase” o “Nivel”

● Son subclases de “Escena” (ver más adelante)

○ No confundir con “Pantalla”

■ Una escena puede tener muchas pantallas

● Ejemplo: juego de aventura gráfica

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Si se desea añadir interacción de los Sprites con el resto de los objetos del juego:
○ ¿Quién mantiene referencia a esos Sprites?

■ Clase “Fase” o “Nivel”

● Mantiene referencia a los Sprites por medio de grupos

○ Sólo los Sprites muy importantes mantienen referencia por medio de atributos

■ fase.jugador1, fase.jugador2

○ Posiblemente un Sprite esté en más de un grupo a la vez

○ Para eliminar un Sprite: método kill()

■ Lo elimina de todos los grupos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Si se desea añadir interacción de los Sprites con el resto de los objetos del juego:
○ ¿Quién mantiene referencia a esos Sprites?

■ Clase “Fase” o “Nivel”

● Ejemplos de grupos:

○ GrupoJugadores, GrupoEnemigos, GrupoPlataformas

○ GruposSpritesDinamicos

■ Todos los Sprites dinámicos (incluye jugadores, enemigos, etc.)

● Para actualizarlos todos a la vez (update)

○ GrupoSprites

■ Todos los Sprites

● Por si es necesario operar sobre todos a la vez

3 – Juego/fase.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
class Fase:


def __init__(self):

self.jugador1 = Jugador()
self.jugador2 = Jugador()
self.grupoJugadores = pygame.sprite.Group( self.jugador1, self.jugador2 )

self.jugador1.establecerPosicion((200, 551)) Se crean los Sprites de los jugadores y se


self.jugador2.establecerPosicion((400, 551)) meten en un grupo
plataformaSuelo = Plataforma(pygame.Rect(0, 550, 1200, 15)) Grupo con las
plataformaCasa = Plataforma(pygame.Rect(870, 417, 200, 10))
plataformas
self.grupoPlataformas = pygame.sprite.Group( plataformaSuelo, plataformaCasa)

enemigo1 = Sniper() Grupo con los


enemigo1.establecerPosicion((1000, 418)) enemigos
# Creamos un grupo con los enemigos Grupos con los Sprites dinámicos y con
self.grupoEnemigos = pygame.sprite.Group( enemigo1 ) todos los Sprites
# Creamos un grupo con los Sprites que se mueven
# En este caso, solo los personajes, pero podría haber más (proyectiles, etc.)
self.grupoSpritesDinamicos = pygame.sprite.Group( self.jugador1, self.jugador2, enemigo1 )
# Creamos otro grupo con todos los Sprites
self.grupoSprites = pygame.sprite.Group( self.jugador1, self.jugador2, enemigo1,
plataformaSuelo, plataformaCasa )

3 – Juego/fase.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Clase “Fase” o “Nivel”


○ Objetos de la clase Jugador (Sprites):

■ En este caso, la clase Fase los crea en el constructor

● Instanciador de atributos

■ Podría recibirlos como parámetros y asignarlos a los atributos en lugar de crearlos

● Cuando los jugadores tienen atributos que van a conservar su valor de una fase a otra: número de vidas, energía, etc.

● Se crearían antes de crear el objeto Fase, en el código principal, y se pasarían de una fase a otra

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Clase “Fase” o “Nivel”


○ Objetos de la clase Jugador (Sprites):

■ Solución más habitual:

● Objetos de la clase Jugador (no Sprites)

○ Contienen los atributos de vida, energía, etc.

○ Cada uno contiene una referencia al Sprite correspondiente

■ Podría ser distinto de una fase a otra

○ El objeto Fase recibe los objetos Jugador, crea los Sprites y los asigna

■ Podrían tener asignados Sprites de una fase anterior

● Se descartarían o no

○ En cualquiera de los dos casos, se resetea el Sprite

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Clase “Fase” o “Nivel”


○ Además, contiene la lógica asociada a esa fase determinada:

■ Posiciones de las plataformas, items, enemigos, etc.

● Referencias a los mismos en forma de grupo

■ Referencia al decorado (y efectos de fondo)

■ Orden de actualización

● Llamada a update

■ Orden de dibujo:

● Primero el fondo, luego los Sprites

■ Eventos que ocurren

■ etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Clase “Fase” o “Nivel”


○ El código principal (bucle de eventos) no llama al método update ni draw de los Sprites ni gestiona los eventos

■ En su lugar, redirige estas funciones a esos métodos en Fase

● Es la que conoce la lógica de la fase

● La Fase llamará al método update de cada grupo

○ El método update de un grupo llamará a update de cada Sprite de ese grupo

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Clase “Fase” o “Nivel”


○ El código principal (bucle de eventos) no llama al método update ni draw de los Sprites ni gestiona los eventos

■ Además, para dibujar la fase

● Desde el bucle se llamará al método “dibujar” de Fase

○ Desde este método se dibujará el fondo y se llamará al método draw de los grupos

■ Conoce el orden de dibujo (orden de llamadas)

● Sin embargo, el método “dibujar” de Fase no hace el volcado (double buffering)

○ Lo realiza el código principal, en el bucle de eventos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

escena.procesarEventos(eventos
)

escena.update(tiempo)

escena.dibujar(pantalla)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
# El bucle de eventos
while True:
Se pasa la lista de
# Sincronizar el juego a 60 fps eventos al objeto Fase
tiempo_pasado = reloj.tick(60)

# Coge la lista de eventos y se la pasa a la escena


# Devuelve si se debe parar o no el juego
if (fase.eventos(pygame.event.get())):
pygame.quit()
sys.exit()

# Actualiza la escena
# Devuelve si se debe parar o no el juego Se llama al método
if (fase.update(tiempo_pasado)): update del objeto Fase
pygame.quit()
sys.exit()
Se llama al método dibujar
# Se dibuja en pantalla
del objeto Fase
fase.dibujar(pantalla)
pygame.display.flip() El volcado de la pantalla se hace en el bucle

3 – Juego/main.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Clase “Fase” o “Nivel”


○ Método dibujar de Fase:

■ El volcado no se realiza en este método, sino en el bucle

● Transparencia anterior

■ Orden de pintado:

def dibujar(self, pantalla):


# Ponemos primero el fondo
self.fondo.dibujar(pantalla)
# Después el decorado
self.decorado.dibujar(pantalla)
# Luego los Sprites
self.grupoSprites.draw(pantalla)
3 – Juego/fase.py
■ No se especifica orden de dibujado de los Sprites

● Podría hacerse

○ Pintar los grupos por separado, en orden

■ Para, por ejemplo, pintar los jugadores por encima del resto de Sprites

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Clase “Fase” o “Nivel”


○ Eventos en Fase:

■ El código principal redirige los eventos a Fase

● Redirige los eventos a los Sprites correspondientes

● Devuelve si el usuario pidió terminar la aplicación

● En el ejemplo: a los Sprites de los jugadores:

def eventos(self, lista_eventos):


# Miramos a ver si hay algun evento de salir del programa
for evento in lista_eventos:
# Si se sale del programa
if evento.type == pygame.QUIT:
return True

# Indicamos la acción a realizar segun la tecla pulsada para cada jugador


teclasPulsadas = pygame.key.get_pressed()
self.jugador1.mover(teclasPulsadas, K_UP, K_DOWN, K_LEFT, K_RIGHT)
self.jugador2.mover(teclasPulsadas, K_w, K_s, K_a, K_d)
# No se sale del programa
return False
3 – Juego/fase.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Clase “Fase” o “Nivel”


○ Método update en Fase:

■ El código principal llama al método update de Fase

● Este método llamará a update de los Sprites en el orden correcto

● Además, realiza otras acciones:

○ Comprueba si se debe finalizar la fase

■ Por ejemplo, si un personaje ha muerto

● Devuelve si se debe parar la ejecución del juego

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Clase “Fase” o “Nivel”


○ Por ejemplo, si hay un jugador con una barra de vida:

■ Método update en Fase:

1. Indicar las acciones a realizar por las IA

2. Llamar a update de todos los Sprites

3. Comprobar el scroll, si los jugadores se han salido de la pantalla

4. Mirar si hay que terminar la fase

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Clase “Fase” o “Nivel”


○ Por ejemplo, si hay un jugador con una barra de vida:

■ Método update en Fase:

1. Indicar las acciones a realizar por las IA

■ Llamar a los métodos correspondientes de cada personaje no jugador

● No sólo los personajes enemigos, cualquier Sprite con comportamiento

■ Las acciones a realizar por los jugadores ya fueron indicadas en los eventos

2. Llamar a update de todos los Sprites

3. Comprobar el scroll, si los jugadores se han salido de la pantalla

4. Mirar si hay que terminar la fase

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Clase “Fase” o “Nivel”


○ Por ejemplo, si hay un jugador con una barra de vida:

■ Método update en Fase:

1. Indicar las acciones a realizar por las IA

2. Llamar a update de todos los Sprites

■ El método update comprueba que los movimientos que se quiere hacer son correctos (no intersecan con paredes,
por ejemplo), y los realiza

■ El método update de Jugador comprobará si hay colisión con algún enemigo, y bajará el nivel de vida

■ Actualiza el estado general del Sprite

■ Se simula así que todos los Sprites “se actualizan a la vez”

3. Comprobar el scroll, si los jugadores se han salido de la pantalla

4. Mirar si hay que terminar la fase

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Clase “Fase” o “Nivel”


○ Por ejemplo, si hay un jugador con una barra de vida:

■ Método update en Fase:

1. Indicar las acciones a realizar por las IA

2. Llamar a update de todos los Sprites

3. Comprobar el scroll, si los jugadores se han salido de la pantalla

4. Mirar si hay que terminar la fase

■ En este caso, un jugador colisiona con un enemigo

■ O si su vida llega a 0

● La vida la disminuye en el método update de Jugador…

■ el método update de Jugador es el que actualiza su estado

● …pero es el objeto Fase el que comprueba si este ha llegado a 0

■ O cualquier otra condición para terminar la fase

● Si termina con éxito la fase

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
Indicar las acciones a realizar por los Sprites con
● Clase “Fase” o “Nivel” algún tipo de comportamiento (aplicable a cualquier
Sprite que tenga algún tipo de IA)
○ Método update en Fase:

def update(self, tiempo): Se actualizan todos los


Sprites “a la vez”.
for enemigo in iter(self.grupoEnemigos): Cada update comprueba
enemigo.mover_cpu(self.jugador1, self.jugador2) que la acción que se va a
self.grupoSpritesDinamicos.update(self.grupoPlataformas,
realizar (desplazamiento)
tiempo) es correcta
if pygame.sprite.groupcollide(self.grupoJugadores, Se comprueba si hay que
self.grupoEnemigos, False, False)!={}: terminar: colisión entre
return True
algún jugador y algún
self.actualizarScroll(self.jugador1, self.jugador2) enemigo

self.fondo.update(tiempo) Se actualiza el scroll


Otras acciones: actualizar el
return False Devuelve False: no fondo (cielo y sol)
se termina la escena 3 – Juego/fase.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Diagrama de clases, la clase Fase tiene instancias de Decorado y Cielo


○ Se crean al inicializar el objeto Fase

Cielo Fase Decorado

class Fase:
def __init__(self):

# Creamos el decorado y el fondo


self.decorado = Decorado()
self.fondo = Cielo()
3 – Juego/main.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Crear una clase con el decorado


○ En el constructor (instanciador de atributos) cargar imagen como decorado

■ Tiene (o se crea) canal alfa

● Permite que haya un fondo detrás

■ Más grande que la pantalla

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D Se carga la imagen mediante
el gestor de recursos y se
class Decorado: escala
def __init__(self):
self.imagen = GestorRecursos.CargarImagen('decorado.png', -1)
self.imagen = pygame.transform.scale(self.imagen, (1200, 300))

self.rect = self.imagen.get_rect()
self.rect.bottom = ALTO_PANTALLA Se guarda el nivel en el que está
el scroll horizontal (inicialmente a
# La subimagen que estamos viendo 0)
self.rectSubimagen = pygame.Rect(0, 0, ANCHO_PANTALLA, ALTO_PANTALLA)
self.rectSubimagen.left = 0
# El scroll horizontal empieza en la posicion 0 por defecto

def update(self, scrollx): La función update simplemente


self.rectSubimagen.left = scrollx modifica el scroll horizontal
def dibujar(self, pantalla):
pantalla.blit(self.imagen, self.rect, self.rectSubimagen)

La función de dibujar solamente pone por


pantalla la parte que se va a ver
3 – Juego/fase.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Además, en este código hay otra clase similar para el fondo:


class Cielo:
def __init__(self):
self.sol = GestorRecursos.CargarImagen('sol.png', -1)
self.sol = pygame.transform.scale(self.sol, (300, 200))
self.rect = self.sol.get_rect()
self.posicionx = 0
# El lado izquierdo de la subimagen que se esta visualizando
self.update(0)

def update(self, tiempo):


self.posicionx += VELOCIDAD_SOL * tiempo
if (self.posicionx - self.rect.width >= ANCHO_PANTALLA):
self.posicionx = 0
self.rect.right = self.posicionx
# Calculamos el color del cielo
if self.posicionx >= ((self.rect.width + ANCHO_PANTALLA) / 2):
ratio = 2 * ((self.rect.width + ANCHO_PANTALLA) - self.posicionx) /
(self.rect.width + ANCHO_PANTALLA)
else:
ratio = 2 * self.posicionx / (self.rect.width + ANCHO_PANTALLA)
self.colorCielo = (100*ratio, 200*ratio, 255)

def dibujar(self,pantalla):
# Dibujamos el color del cielo
pantalla.fill(self.colorCielo)
# Y ponemos el sol
pantalla.blit(self.sol, self.rect) 3 – Juego/fase.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Para actualizar el scroll:


○ Función update de la clase Fase:
■ Mover todos los Sprites dinámicos “a la vez”
● Llamar a update de cada Sprite dinámico

■ Una vez movidos los Sprites, y según la posición de los jugadores, puede haber que cambiar
el scroll
● Si hay que cambiarlo, actualizar la posición de todos los Sprites

○ Estáticos y dinámicos

● Actualizar también la posición del decorado

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll:


○ Ordenar ambos jugadores según el eje x
■ “Jugador de la izquierda” y “jugador de la derecha”

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll:


○ Existen unos límites en los que se pueden mover los jugadores
Parte que se ve en
pantalla
Espacio entre el que se pueden
mover

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll:


○ Si ambos jugadores se han salido de los límites por lados distintos
■ Se sitúan en el lado del límite correspondiente

■ No se actualiza el scroll

○ Si ambos jugadores están entre esos límites, no hacer nada


■ No se actualiza el scroll

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll:


○ En otro caso, un jugador se ha salido del límite
○ Si el jugador de la izquierda se ha salido del límite izquierdo
■ Se calcula la diferencia entre la posición izquierda del límite izq. decorado y la del jugador de la izquierda:
desplazamiento
● Cuánto hay que mover el scroll

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll:


○ Si el jugador de la izquierda se ha salido del límite izquierdo
■ En este caso, se pueden dar una de estas tres situaciones:
1. El scroll ya está a la izquierda de todo
○ Se sitúa el jugador izquierdo en el límite izquierdo

○ No se actualiza el scroll

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll:


2. El scroll no está a la izquierda de todo pero el jugador derecho impide desplazarlo a la izquierda

● Habría que desplazar el jugador izquierdo, el decorado y todos los sprites desplazamiento
pixeles a la derecha

● Pero puede ocurrir que el jugador de la derecha se encuentre a una distancia del borde derecho
menor que desplazamiento píxeles

○ Si se desplazase todo, el jugador derecho saldría del borde

● En este caso, se sitúa el jugador izquierdo al límite izquierdo

● No se actualiza el scroll

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll:


3. El scroll no está a la izquierda de todo y se puede desplazar
● Se actualiza el scroll desplazamiento pixeles a la derecha

○ Se colocan en pantalla todos los Sprites según el nuevo valor de scroll

■ Estáticos y dinámicos

○ Se coloca el decorado a la derecha según el nuevo valor de scroll

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll:


○ Realizar un proceso similar para el jugador de la derecha
■ Si el jugador de la derecha se ha salido por el lado derecho

○ Proceso fácilmente generalizable para más de dos jugadores

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
Se ordenan los jugadores y se
● Para actualizar el scroll: mira si hay que cambiar el scroll

def actualizarScroll(self, jugador1, jugador2):


# Se ordenan los jugadores según el eje x, y se mira si hay que
actualizar el scroll
if (jugador1.posicion[0]<jugador2.posicion[0]):
cambioScroll = self.actualizarScrollOrdenados(jugador1, jugador2)
else:
cambioScroll = self.actualizarScrollOrdenados(jugador2, jugador1)

# Si se cambio el scroll, se desplazan todos los Sprites y el decorado


if cambioScroll:
# Actualizamos la posición en pantalla de todos los Sprites
for sprite in iter(self.grupoSprites):
sprite.establecerPosicionPantalla((self.scrollx, 0))

# Ademas, actualizamos el decorado Si se cambia el scroll, se


self.decorado.update(self.scrollx) desplazan todos los Sprites

Si se cambia el scroll, se desplaza también el decorado


3 – Juego/fase.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll:


○ Una vez ordenados los jugadores, para saber si se actualiza el scroll:

def actualizarScrollOrdenados(self, jugadorIzq, jugadorDcha):

# Si ambos jugadores se han ido por ambos lados de los dos bordes
if (jugadorIzq.rect.left<MINIMO_X_JUGADOR) and
Si ambos jugadores se han
(jugadorDcha.rect.right>MAXIMO_X_JUGADOR):
ido por ambos lados, se
coloca uno en cada lado.
# Colocamos al jugador que esté a la izq a la izq de todo
Devuelve False: no se ha
jugadorIzq.establecerPosicion((self.scrollx+MINIMO_X_JUGADOR,
actualizado
jugadorIzq.posicion[1]))
# Colocamos al jugador que esté a la derecha a la derecha de todo
jugadorDcha.establecerPosicion((self.scrollx+MAXIMO_X_JUGADOR-
jugadorDcha.rect.width, jugadorDcha.posicion[1]))

return False; # No se ha actualizado el scroll

… 3 – Juego/fase.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

# Si el jugador de la izquierda se encuentra más allá del borde izquierdo
if (jugadorIzq.rect.left<MINIMO_X_JUGADOR):
desplazamiento = MINIMO_X_JUGADOR - jugadorIzq.rect.left

# Si el escenario ya está a la izquierda del todo, no lo movemos mas


if self.scrollx <= 0:
self.scrollx = 0
# En su lugar, colocamos al jugador que esté más a la izq a la izquierda de todo
jugadorIzq.establecerPosicion((MINIMO_X_JUGADOR, jugadorIzq.posicion[1]))
return False; # No se ha actualizado el scroll

# Si no, es posible que el jugador de la derecha no se pueda desplazar


# tantos pixeles a la derecha por estar muy cerca del borde derecho
elif ((MAXIMO_X_JUGADOR-jugadorDcha.rect.right)<desplazamiento):

# En este caso, ponemos el jugador de la izquierda en el lado izquierdo


jugadorIzq.establecerPosicion((jugadorIzq.posicion[0]+desplazamiento,
jugadorIzq.posicion[1]))
return False; # No se ha actualizado el scroll

# Si se puede hacer scroll a la izquierda


else:
# Calculamos el nivel de scroll actual: el anterior - desplazamiento
# (desplazamos a la izquierda)
self.scrollx = self.scrollx - desplazamiento;

return True; # Se ha actualizado el scroll


… 3 – Juego/fase.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

# Si el jugador de la derecha se encuentra más allá del borde derecho
if (jugadorDcha.rect.right>MAXIMO_X_JUGADOR):
desplazamiento = jugadorDcha.rect.right - MAXIMO_X_JUGADOR

# Si el escenario ya está a la derecha del todo, no lo movemos mas


if self.scrollx + ANCHO_PANTALLA >= self.decorado.rect.right:
self.scrollx = self.decorado.rect.right - ANCHO_PANTALLA
# En su lugar, colocamos al jugador que esté más a la derecha a la derecha de todo
jugadorDcha.establecerPosicion(
(self.scrollx+MAXIMO_X_JUGADOR-jugadorDcha.rect.width, jugadorDcha.posicion[1]))
return False; # No se ha actualizado el scroll

# Si no, es posible que el jugador de la izquierda no se pueda desplazar


# tantos pixeles a la izquierda por estar muy cerca del borde izquierdo
elif ((jugadorIzq.rect.left-MINIMO_X_JUGADOR)<desplazamiento):

# En este caso, ponemos el jugador de la derecha en el lado derecho


jugadorDcha.establecerPosicion((jugadorDcha.posicion[0]-desplazamiento,
jugadorDcha.posicion[1]))
return False; # No se ha actualizado el scroll

# Si se puede hacer scroll a la derecha


else:
# Calculamos el scroll actual: el anterior + desplazamiento (desplazamos a la dcha)
self.scrollx = self.scrollx + desplazamiento;
return True; # Se ha actualizado el scroll

# Si ambos jugadores están entre los dos límites de la pantalla, no se hace nada
return False;
3 – Juego/fase.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Para actualizar el scroll del decorado:


○ Implementación:
■ El decorado tiene un valor de scroll: lo que se ve en pantalla
● Límite izquierdo: scrollx
● Límite inferior/superior: scrolly
○ Si scroll vertical
■ Ejemplo:
● scrollx: 80

Parte que se ve en
pantalla

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll del decorado:


○ Implementación:
■ Cada objeto (Sprite) tiene dos atributos de
posiciones:

● Posición en el entorno: sin tener en cuenta


qué parte del decorado se está pintando

● Posición del sprite en pantalla

○ pygame: con el atributo Sprite.rect

○ Utilizado para dibujar el Sprite con el


método draw del grupo

■ Ejemplo: posición: (190, 300) relativa al


entorno global, no a la pantalla

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll del decorado:


○ Implementación:
■ Para implementar que un Sprite tiene estas dos posiciones, lo más sencillo es extender la clase Sprite:

Sprite

MiSprite

■ Contiene los atributos y métodos para situar los Sprites de forma global (en el entorno) o local (en pantalla)

■ Todos los Sprites que se usen serán de la clase MiSprite

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll del decorado:


○ Implementación:
■ La clase MiSprite contiene atributos:
● Posición global en el entorno

● Posición local en la pantalla

○ pygame.Rect

● Scroll actual

○ Almacena el valor de scroll para, cuando se modifique su posición global, también se modifique su posición en la pantalla

■ Importante:
● Implementar esta clase sólo si se va a realizar scroll

○ Si el juego es de pantallas sencillas, la posición global y local serán la misma

■ Usar para pygame.Rect para almacenar la posición

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll del decorado:


○ Implementación:
■ Implementa métodos:

● establecerPosicion( nuevaPosicionGlobal )
○ Recibe una nueva posición global y cambia su posición de forma global
■ También, gracias al valor de scroll que tiene almacenado, su posición en pantalla

■ Un cambio de posición global puede implicar un cambio de posición local

● establecerPosicionPantalla( nuevoScroll )
○ Recibe un valor de scroll y cambia su valor en pantalla
■ Pero no su posición global en el entorno

■ Un cambio de posición local NO implica un cambio de posición global

■ Se llama desde la clase Fase cuando se actualiza el scroll

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll del decorado:


○ Implementación:
■ Además, la clase MiSprite se puede ampliar con

● Un atributo de velocidad

○ Velocidad en cada eje (x, y)

● Implementación de un método update

○ Recibe el tiempo

○ Con el atributo de velocidad, calcula la distancia recorrida y la actualiza con el método establecerPosicion() anterior

■ De esta manera, mover un Sprite sólo implica establecer sus valores de velocidad y llamar a update

● Si se extiende MiSprite y se implementa update, se puede llamar a update de MiSprite después de establecer la velocidad

■ Esto permite, además, implementar de una forma sencilla distintos efectos físicos como

● Inercia

● Aceleración / deceleración

● Gravedad

● etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll del decorado: Almacena la posición, la


velocidad y una copia del
○ Implementación: Clase MiSprite: nivel de scroll
class MiSprite(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self) Método para
self.posicion = (0, 0)
cambiar la
self.velocidad = (0, 0)
self.scroll = (0, 0) posición global:
cambia también
def establecerPosicion(self, posicion): la posición en
self.posicion = posicion pantalla gracias a
self.rect.left = self.posicion[0] - self.scroll[0] la copia del scroll
self.rect.bottom = self.posicion[1] - self.scroll[1]
Cambiar la posición
def establecerPosicionPantalla(self, scrollDecorado): en pantalla: recibe el
self.scroll = scrollDecorado;
nuevo nivel de scroll y
(scrollx, scrolly) = self.scroll;
(posx, posy) = self.posicion; actualiza su posición
self.rect.left = posx - scrollx; en pantalla
self.rect.bottom = posy - scrolly;
3 – Juego/personaje.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Para actualizar el scroll del decorado:


○ Implementación: Clase MiSprite:

def incrementarPosicion(self, incremento):


(posx, posy) = self.posicion
(incrementox, incrementoy) = incremento
self.establecerPosicion((posx+incrementox, posy+incrementoy))

def update(self, tiempo):


incrementox = self.velocidad[0]*tiempo
incrementoy = self.velocidad[1]*tiempo
self.incrementarPosicion((incrementox, incrementoy))

3 – Juego/personaje.py

Método update: recibe el tiempo, y, con la velocidad en cada eje, actualiza su


posición.
En consecuencia, también se actualiza su posición en pantalla.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll del decorado:


○ Implementación:

■ Si no se modifica el scroll y se cambia la posición de algún jugador

● Se llama al método establecerPosicion(nuevaPosicionGlobal)

○ Se cambia la posición global (y la local)

■ Cuando se actualiza el scroll en función de las posiciones de los jugadores, se modifica la posición de todos los Sprites

● Pero no las posiciones globales

○ Desde el punto de vista del entorno, siguen en el mismo sitio

● Se llama al método establecerPosicionPantalla(scroll)

● Este proceso para todos los objetos que haya:

○ Jugadores

○ Enemigos

○ Disparos

○ Items

○ Plataformas

○ etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll del decorado:


○ Implementación: método actualizarScroll() de Fase
def actualizarScroll(self, jugador1, jugador2):
# Se ordenan los jugadores según el eje x, y se mira si cambia el scroll
if (jugador1.posicion[0]<jugador2.posicion[0]):
cambioScroll = self.actualizarScrollOrdenados(jugador1, jugador2)
else:
cambioScroll = self.actualizarScrollOrdenados(jugador2, jugador1) Cuando se cambia el scroll,
se llama al método
# Si se cambio el scroll, se desplazan todos los Sprites y el decorado
if cambioScroll: establecerPosicionPantalla()
# Actualizamos la posición en pantalla de todos los Sprites de MiSprite: cambia la
for sprite in iter(self.grupoSprites): posición local pero no la
sprite.establecerPosicionPantalla((self.scrollx, 0)) global de los Sprites

# Ademas, actualizamos el decorado para que se muestre una parte


distinta
self.decorado.update(self.scrollx)
3 – Juego/fase.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Para actualizar el scroll del decorado:

○ Implementación:

■ Ejemplo anterior:

● posicion = (190, y)

● establecerPosicionPantalla(80, 0) → pygame: Sprite.rect.left = 190-80 = 110

80: scroll del decorado


190: posición del Sprite en el
entorno
■ Al final se vería en pantalla:
(límite izquierdo del Sprite)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para actualizar el scroll del decorado:


○ Scroll parallax:
■ Distintos niveles de scroll

● Se mueven dos o más planos de scroll

○ Las imágenes de fondo se mueven más despacio que las de delante

● Se da una cierta sensación de profundidad al videojuego

■ Ejemplo: un escenario formado por:

● Árboles en primer plano

● Casas más lejanas moviéndose más despacio que los árboles

● Al fondo unas montañas moviéndose aún más despacio

■ Implementación:

● Distintos objetos Decorado, cada uno con una línea distinta en update:

class Decorado:

def update(self, scrollx):
self.rectSubimagen.left = scrollx 3 – Juego/fase.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● ¿Y si se quiere ejecutar el juego con distintas resoluciones?


○ Se puede escalar cada imagen para que siempre tenga el mismo tamaño en pantalla

■ Problema: al escalar una imagen de poca resolución a más resolución, aparece pixelada

○ Solución más común:

■ Guardar distintas versiones de cada imagen (decorados, Sprites, etc.), una para cada resolución distinta

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● ¿Y si se quiere ejecutar el juego con distintas resoluciones?


○ Problema: el movimiento (velocidad) de cada Sprite se ha especificado en píxeles/seg

■ Irá a velocidades distintas según la configuración

○ Solución:

■ Especificar distancias y velocidades en unidades arbitrarias

● “metros” o “metros/seg”

■ Cuando se sitúe un Sprite en pantalla, hacer la conversión

● “metros” → píxeles

● Métodos establecerPosicion y establecerPosicionPantalla de MiSprite

● Conversión distinta para cada resolución

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● ¿Y si se quiere ejecutar el juego con distintas resoluciones?


○ Realizar esta conversión: clase Configuracion

■ A nivel de implementación, patrón Singleton

■ Contiene como atributos todas las características de la configuración:

● Resolución

● Volumen de sonido

● Controles de los jugadores

● Corrección gamma

● etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● ¿Y si se quiere ejecutar el juego con distintas resoluciones?


○ Clase Configuracion

■ Implementa métodos:

● Para cambiar de “metros” a píxeles según la resolución actual

● Cuando se quiere cargar un archivo de imagen (a través del gestor de recursos), completa el nombre del archivo

○ Ejemplo:

■ Configuracion.Instancia().nombreArchivoImagen(‘decorado’)

● Devuelve ‘decorado1280x800.jpeg’

● etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Introduciendo plataformas:
○ Clase plataforma: subclase de Sprite

■ Atributo rect: posición (argumento al constructor)

■ Atributo image: imagen (en este caso vacío, son “invisibles”)

■ No tiene métodos: No hay ningún comportamiento asociado

● Una
class plataforma sólo necesita existir
Plataforma(MiSprite):
def __init__(self,rectangulo):
# Primero invocamos al constructor de la clase padre
MiSprite.__init__(self)
# Rectangulo con las coordenadas en pantalla que ocupara
self.rect = rectangulo
# Y lo situamos de forma global en esas coordenadas En este caso, las
self.establecerPosicion((self.rect.left, self.rect.bottom)) plataformas no
# En el caso particular de este juego, las plataformas no se van a ver, tienen imagen
asi que no se carga ninguna imagen
self.image = pygame.Surface((0, 0))

3 – Juego/fase.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Introduciendo plataformas:
○ Las plataformas pertenecen al objeto Fase

■ La fase sabe en qué lugares están, las crea con sus posiciones

● Las crea indicándoles sus posiciones

● Las une en un grupo: grupoPlataformas

○ Mantiene referencia a ellas mediante el grupo

class Fase:
def __init__(self):

# Creamos las plataformas del decorado
# La plataforma que conforma todo el suelo
plataformaSuelo = Plataforma(pygame.Rect(0, 550, 1200, 15))
# La plataforma del techo del edificio
plataformaCasa = Plataforma(pygame.Rect(870, 417, 200, 10))
# y el grupo con las mismas
self.grupoPlataformas = pygame.sprite.Group( plataformaSuelo, plataformaCasa )
# Estas plataformas deberían de cargarse de un archivo, puesto que deberian
# de ser distintas para cada decorado
3 – Juego/fase.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Introduciendo plataformas:
○ En el ejemplo, la fase crea las plataformas en las siguientes posiciones:

■ Suelo

■ Techo del edificio

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Introduciendo plataformas:
○ Observaciones (1/5):

■ Aquí se ha creado una clase “Plataforma”

● En un caso real:

○ Crear una clase abstracta “Plataforma” (subclase de Sprite)

○ Extender esa clase a todos los tipos de plataformas

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Introduciendo plataformas:
○ Observaciones (2/5):

■ En esta implementación la fase ha creado las plataformas de forma “estática”, en el constructor

● Sólo hay una fase

● Para que haya varias fases con diferentes configuraciones:

○ 2 opciones (1/2):

■ La fase recibe en el constructor el nombre de un archivo de configuración

● que tendría, entre otras cosas:

○ El nombre del fichero con la imagen de fondo

○ Las posiciones de las plataformas, los enemigos

○ etc.

● Más cercano al concepto de Arquitectura dirigida por datos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Introduciendo plataformas:
○ Observaciones (3/5):

■ En esta implementación la fase ha creado las plataformas de forma “estática”, en el constructor

○ 2 opciones (2/2):

■ Crear esa clase abstracta y extenderla para cada fase

● Cada fase, en el constructor, instancia las plataformas, enemigos, etc. en las posiciones que corresponda y el decorado de esa fase

Fase

Fase1 Fase2

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Introduciendo plataformas:
○ Observaciones (4/5):

■ En esta implementación la fase ha creado las plataformas de forma “estática”, en el constructor

○ 2 opciones (2/2):

■ Crear esa clase abstracta y extenderla para cada fase

● Si existiesen distintas fases de distintas naturalezas

○ Por ejemplo, unas con scroll horizontal, otras en perspectiva aérea

Contiene la lógica propia de las Contiene la lógica de las fases de


fases de scroll horizontal
Fase vista aérea
(actualizar scroll, etc.)

FaseScrollHorizontal FaseAérea
En el constructor se indica las
características de cada fase
Fase1 Fase2

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Introduciendo plataformas:
○ Observaciones (5/5):

■ En este caso, las plataformas no tienen imagen

● Se aprovecha la imagen del decorado

● En pygame: atributo image es una imagen vacía

○ Surface(0,0)

○ Al llamar a draw del grupo, no se dibujará

■ Podrían coexistir plataformas con imagen y plataformas sin imagen

● Al llamar a draw del grupo, sólo se dibujarán las que tienen imagen

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Introduciendo plataformas:

○ Para comprobar cuándo un jugador “cae” en una plataforma, habrá que comprobar si hay colisión entre el jugador y las
plataformas del grupo

■ Función spritecollideany(sprite, grupoSprites)

● Dice si el sprite está en colisión con algún Sprite del grupo grupoSprites y devuelve con cuál

■ En el caso de este juego sólo nos interesan las colisiones que se producen cuando el personaje está en caída

● Andando o saltando se supone que puede atravesar las plataformas

○ Se podrían crear plataformas “atravesables” y otras que no

■ Ponerlas en distintos grupos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Introduciendo plataformas
○ Además, un personaje que no esté en el aire debe de estar en colisión con una plataforma

■ En caso contrario, en la próxima llamada al método update, este deberá detectarlo y poner al personaje cayendo

■ Cuando un personaje está cayendo y se detecta colisión con una plataforma, se detiene la caída y se sitúa al personaje encima
de la plataforma

● Pero con una línea de píxeles de colisión, para que en la próxima llamada se detecte que está por encima

○ Habría muchas otras formas de solucionar esto

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
class Personaje(MiSprite):
def update(self, grupoPlataformas, tiempo):

# Además, si estamos en el aire
if self.numPostura == SPRITE_SALTANDO:

# Miramos a ver si hay que parar de caer: si hemos llegado a una plataforma
# Para ello, miramos si hay colision con alguna plataforma del grupo
plataforma = pygame.sprite.spritecollideany(self, grupoPlataformas)
# Ademas, esa colision solo nos interesa cuando estamos cayendo
# y solo es efectiva cuando caemos encima, no de lado, es decir,
# cuando la posicion inferior esta por encima de la parte de abajo de la plat.
if (plataforma != None) and (velocidady>0) and
(plataforma.rect.bottom>self.rect.bottom):
# Lo situamos con la parte de abajo un pixel colisionando con la plataforma
# para poder detectar cuando se cae de ella
self.establecerPosicion(
(self.posicion[0], plataforma.posicion[1]-plataforma.rect.height+1))
# Lo ponemos como quieto
self.numPostura = SPRITE_QUIETO
# Y estará quieto en el eje y
velocidady = 0

# Si no caemos en una plataforma, aplicamos el efecto de la gravedad


else:
velocidady += GRAVEDAD * tiempo 3 – Juego/personajes.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Si se desea añadir interacción de los Sprites con el resto de los objetos del juego:
○ El resto de los objetos también son Sprites

■ Están en los grupos correspondientes


Sprite
● Un Sprite puede estar en más de un grupo

■ Por ejemplo:

● Plataformas MiSprite
● Enemigos

● Items
Personaje

Jugador NoJugador

(patrón plantilla) Sniper


Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Introduciendo enemigos:
○ Clase “MiSprite”

■ Subclase de Sprite

■ Contiene los atributos con la localización de un Sprite en el entorno del videojuego y en pantalla

■ Contiene los métodos para operar con esos atributos

● Cambiar de posición

● Cambiar de posición en pantalla

○ Se modifica el scroll

■ Usar solamente cuando el juego tenga scroll

■ Contiene atributos de velocidad que permiten modificar posiciones de una manera más sencilla

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Introduciendo enemigos:
○ Clase “Personaje”

■ Subclase de MiSprite

■ Constructor:

● Recibe las características: hoja de sprites, velocidad, etc.

■ Contiene toda la lógica descrita anteriormente de comportamiento de un personaje

● Movimiento

○ Métodos mover, update

● Colisiones con plataformas

● etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Introduciendo enemigos:
○ Clase “Jugador”

■ Subclase de “Personaje”

■ Constructor:

● Llama al constructor de Personaje con las características propias del jugador: hoja de sprites, velocidad, etc.

■ Método update

● Utiliza el de “Personaje”

■ Método mover

● Recibe la entrada de teclado y llama al método mover de Personaje

○ Recibe el array de teclas pulsadas y qué tecla (constante) es de cada movimiento en este objeto

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
class Jugador(Personaje):

def __init__(self):
# Invocamos al constructor de la clase padre con la configuracion
# de este personaje concreto
Personaje.__init__(self,'Jugador.png','coordJugador.txt', [6, 12, 6],
VELOCIDAD_JUGADOR, VELOCIDAD_SALTO_JUGADOR, RETARDO_ANIMACION_JUGADOR);
Recibe el array con las teclas
def mover(self, teclasPulsadas, arriba, abajo, izquierda, derecha): pulsadas y qué teclas
# Indicamos la acción a realizar segun la tecla pulsada para el jugador utilizará este objeto
if teclasPulsadas[arriba]:
Personaje.mover(self,ARRIBA)
elif teclasPulsadas[izquierda]:
Personaje.mover(self,IZQUIERDA)
elif teclasPulsadas[derecha]:
Personaje.mover(self,DERECHA)
else:
Personaje.mover(self,QUIETO)
3 – Juego/personajes.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para implementar controles distintos:


○ Clase Control

■ Almacena qué teclas/botones se corresponden a cada acción

■ Implementa métodos que devuelven valor booleano según las acciones del jugador

● “arriba(…)”, “abajo(…)”, “fuego(…)”, “salto(…)”, etc.

● Estos métodos reciben los eventos y/o la lista de teclas/botones pulsados y dicen si se quiso realizar una acción determinada

■ Implementa métodos que asignan a una tecla/botón una acción concreta

● “asignarArriba(…)”, “asignarAbajo(…)”, “asignarFuego(…)”, “asignarSalto(…)”, etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para implementar controles distintos:


○ Clase Control

■ Clase abstracta

● Métodos anteriores abstractos

● Extender esta clase:

○ ControlTeclado, ControlRaton, ControlJoystick

○ Implementar cada una los métodos anteriores

■ Un objeto Control por cada jugador

● El objeto Jugador contiene una referencia a su objeto Control

● La clase Configuracion mantiene referencias a todos los controles

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Para implementar controles distintos:


○ El método mover de Jugador recibe los eventos y/o teclas/botones pulsados

■ Realiza llamadas a cada método de acción de su objeto Control con esos datos como parámetros

● De esta forma, se sabe qué acciones se desea hacer

● Se posibilita que se realicen varias acciones a la vez

○ O una acción que requiera varias teclas pulsadas a la vez

○ Ejemplo: disparar mientras se camina

○ Implementación más sencilla y eficiente

■ Comprobación del estado de las teclas/botones pulsados en lugar de eventos

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Introduciendo enemigos:
○ Clase “NoJugador”

■ Subclase de “Personaje”

■ Clase abstracta para todos los posibles personajes que no son controlados por el jugador

■ Método mover_cpu

● Sin definir

● Cada una de las subclases deberá implementar la IA de cada tipo de personaje

○ En función de la IA, la función debería realizar una u otra determinada llamada a mover (superclase “Personaje”)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
El constructor recibe los parámetros necesarios para su caracterización (hoja de Sprites,
velocidad, etc.) y se la pasa al constructor de la clase Personaje para que se configure
correctamente

class NoJugador(Personaje):

def __init__(self, archivoImagen, archivoCoordenadas, numImagenes,


velocidad, velocidadSalto, retardoAnimacion):
# Primero invocamos al constructor de la clase padre con los parametros
Personaje.__init__(self, archivoImagen, archivoCoordenadas, numImagenes,
velocidad, velocidadSalto, retardoAnimacion);

# Aqui vendria la implementacion de la IA segun las posiciones de los


# jugadores
# La implementacion por defecto, este metodo deberia de ser implementado en
# las clases inferiores mostrando la personalidad de cada enemigo
def mover_cpu(self, jugador1, jugador2):
# Por defecto un enemigo no hace nada
# (se podria programar, por ejemplo, que disparase al jugador por
# defecto)
return
3 – Juego/personajes.py

IA por defecto: no hace nada (seInmersivos,


Contornos podría haber
Interactivos puesto este método como
y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Introduciendo enemigos:
○ Clase “Sniper”

■ Un tipo particular de enemigo

■ Constructor:

● Llama al constructor de Personaje con las características propias del jugador: hoja de sprites, velocidad, etc.

■ Método mover_cpu:

● IA rudimentaria:

○ Sigue al personaje que tenga más cerca

● Realiza llamadas a mover

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D
El constructor llama al constructor de la clase padre con los parámetros necesarios (hoja de
Sprites, velocidad, etc.) para que se configure correctamente

class Sniper(NoJugador):
"El enemigo 'Sniper'"
def __init__(self):
# Invocamos al constructor de la clase padre con la configuracion de este personaje
NoJugador.__init__(self,'Sniper.png','coordSniper.txt', [5, 10, 6],
VELOCIDAD_SNIPER, VELOCIDAD_SALTO_SNIPER, RETARDO_ANIMACION_SNIPER);

Implementación de la
IA de este personaje
# Aqui vendria la implementacion de la IA segun las posiciones de los jugadores concreto
# La implementacion de la inteligencia segun este personaje particular
def mover_cpu(self, jugador1, jugador2):

# Movemos solo a los enemigos que esten en la pantalla


if self.posicionx>0 and self.posicionx<ANCHO_PANTALLA and self.posiciony>0 and
self.posiciony<ALTO_PANTALLA:
… Sólo se mueve a los enemigos que están en pantalla (en este juego)
# Si este personaje no esta en pantalla, no hara nada
else:
Personaje.mover(self,QUIETO)
3 – Juego/personajes.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Además, el objeto de la clase Fase ha de comprobar si hay colisión entre los jugadores y los enemigos
○ Método update

○ Si lo hay, devuelve un valor al bucle para que termine el juego

def update(self, tiempo):


# Primero, se indican las acciones que van a hacer los enemigos
for enemigo in iter(self.grupoEnemigos):
enemigo.mover_cpu(self.jugador1, self.jugador2)
# Actualizamos los Sprites dinamicos
self.grupoSpritesDinamicos.update(self.grupoPlataformas, tiempo)

# Comprobamos si hay colision entre algun jugador y algun enemigo


if pygame.sprite.groupcollide(self.grupoJugadores, self.grupoEnemigos,
False, False)!={}:
return True

# Actualizamos el scroll
self.actualizarScroll(self.jugador1, self.jugador2)
# Actualizamos el fondo: la posicion del sol y el color del cielo
self.fondo.update(tiempo)
# No se debe parar la ejecucion
return False
3 – Juego/fase.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Creación de controles HUD:


○ Heads-Up Display

○ Crear clase para el control HUD

■ Tiene método dibujar() que pinta el control

● A partir de los valores de vida, energía, etc.

○ Cuando se llama al método dibujar() del objeto Fase:

■ Llama de forma ordenada a los elementos a dibujar:

● Fondo

● Decorado

● Plataformas

● Sprites

● Proyectiles

● Control HUD

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Creación de controles HUD:


○ Otra opción: unir estos elementos en capas

■ Clase Capa

● Cada capa tendrá todos los elementos que se dibujan “a la vez”

○ Capa con el Decorado

○ Capa con uno o varios grupos de Sprites

○ Capa con varios grupos de Sprites

○ Capa con el control HUD

■ El método dibujar() de Fase va llamando sucesivamente a los métodos dibujar() de cada objeto Capa en el orden adecuado

● Almacenarlos en una lista por el orden a ser llamados

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Creación de controles HUD:


○ Otra opción: unir estos elementos en capas

■ Método utilizado, sobre todo, si en el juego existen distintos niveles de profundidad en la pantalla

● Típicamente, 2 o 3 niveles: decorado, juego y control HUD

● Posible que haya más niveles de profundidad en la pantalla

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Mapeado:
○ Creación de mapas aquí descrita de forma “a bajo nivel”

■ Especificar posiciones de plataformas, etc.

○ Para un videojuego en perspectiva superior, con vista aérea

■ Necesario definir zonas del mapa por donde no se puede mover

● Usar una máscara

○ Matriz binaria de igual tamaño que una imagen

■ Un bit por píxel de la imagen

■ Cada bit dice si en ese píxel habría colisión (1) o no (0)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Mapeado:
○ Para crear una máscara a partir de una imagen:

■ pygame.mask.from_surface

● Se le aplica un umbral al canal alfa

■ pygame.mask.from_threshold

● Permite especificar un color y un umbral

○ Una vez se tienen dos máscaras (mapa y personaje), para comprobar que un personaje puede ir por cierta parte del mapa:

■ mascaraMapa.overlap_area(mascaraPersonaje, desplazamiento)

● Devuelve el número de píxeles que se solapan entre ambas máscaras

● desplazamiento se expresa como tupla (x, y)

● Si devuelve un valor de 0, es que no solapan con el escenario

■ Comprobación a realizar en el método update del personaje, antes de moverlo

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Mapeado:
○ Editores de mapas:

■ GLEED2D

● http://www.gleed2d.de/

■ D2D Map Editor

● http://www.dannylum.com/D2DProject/index.html

■ Mappy Tile Map Editor

● http://www.tilemap.co.uk/mappy.php

■ Tile Mapper

● http://tilemapeditor.com/

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Mapeado:
○ Editores de mapas:

■ La mayoría de ellos no están pensados para su uso con Python, pero para cargarlos con Python:

● Tiled TMX Map Loader for Pygame

○ http://silveiraneto.net/2009/12/19/tiled-tmx-map-loader-for-pygame/

● Mappy Exporter

○ Exporta un mapa hecho con Mappy a XML

○ http://trac2.assembla.com/trollshit/browser

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Otro uso de las máscaras:


○ Calcular colisiones entre Sprites con total precisión

■ Que las colisiones se adecúen al propio dibujo del Sprite

● Solamente con los píxeles visibles

■ Más precisión que usar los rectángulos de los Sprites

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Otro uso de las máscaras:


○ Calcular colisiones entre Sprites con total precisión

■ Usar el atributo mask de los Sprites:

● Crear la máscara con la función from_surface o from_threshold:

class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image=img_player
self.rect=self.image.get_rect()
self.mask=pygame.mask.from_surface(self.image)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Otro uso de las máscaras:


○ Calcular colisiones entre Sprites con total precisión

■ Función collide_mask(Sprite1, Sprite2)

● Devuelve el primer punto de la máscara donde ambas colisionaron

○ None si no hubo colisión

● Más precisión que usar los rectángulos de los Sprites

● Si ambos Sprites tienen atributo mask, se comprueba entre esas máscaras

○ Si alguno no la tiene, se crea a partir de la imagen del Sprite

■ Se crea cada vez que se calcula la colisión

○ Si se van a calcular muchas colisiones mediante esta función, calcular el atributo mask para no tener que calcular la
máscara para cada colisión

● Esta función no está pensada para ser usada sola, sino en combinación con alguna otra de colisión

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Otro uso de las máscaras:


○ Calcular colisiones entre Sprites con total precisión

● Otras opciones:
■ Las funciones spritecollide, groupcollide y
spritecollideany aceptan un parámetro opcional que es la ○ collide_rect
función de colisión
■ Comprueba con el rectángulo
● Si no se especifica, usa por defecto la función ○ collide_rect_ratio
collide_rect()
■ Comprueba con el rectángulo escalado
○ Comprueba colisión de dos Sprites según sus
atributos rect ○ collide_circle

■ Comprueba con un círculo

○ collide_circle_ratio

■ Comprueba con un círculo escalado

○ collide_mask

■ Comprueba con la máscara

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escenas:
○ Una escena se define como cada una de las etapas que posee el juego global

○ Un videojuego puede tener muchas escenas, pero sólo una está activa en un momento determinado

○ Por ejemplo:

■ La pantalla inicial

■ La pantalla del juego en sí en una fase determinada

■ La pantalla de puntuaciones más altas

■ La pantalla de opciones

■ Todas las demás pantallas que se necesiten

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escenas:
○ Por ejemplo, un juego con estas escenas:

■ Se puede definir cada una como aplicaciones más o menos separadas

■ Pero existe un hilo conductor que contiene la lógica que conecta las escenas

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escenas:
○ El componente que se ocupa de gestionar la ejecución de cada escena se denomina director

○ El director conoce qué escena está activa, y mantiene una pila de escenas para permitir acciones como “llamadas entre escenas”

■ Por ejemplo: pausando una escena, entrando en la escena de pausa, y más tarde, volviendo a la original

○ A nivel de implementación, el director es un objeto compartido (Singleton)

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escenas:
○ Cada escena conoce su lógica interna

■ Sabe, por ejemplo:

● Cómo se actualizan los elementos que tiene

● Cómo se dibujan los elementos que tiene

● etc.

■ Ejemplo: objeto Fase

■ Además, cada escena sabe cuándo tiene que terminar y empezar otra escena

● Le comunica al director que cambie de escena a otra

○ La crea si es necesario (la escena anterior, no el director)

○ O solicita al gestor de recursos que la cree, y se la pasa al director

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escenas:
○ No confundir escena con pantalla

■ Una escena podría ser una aplicación más o menos separada

■ Una escena puede constar de distintas pantallas

● Por ejemplo:

○ Aventura gráfica

○ Menú de inicio

● Por sí sola, cada pantalla no podría ser una aplicación

○ Existe una lógica que las enlaza

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escenas:
○ El director implementa el bucle de eventos

■ Recibe los eventos y los envía a la escena actual

● La escena sabe cómo procesarlos

■ Llama al método update de la escena actual

● La escena llamará a update de cada elemento que tenga

■ Llama al método de dibujar de la escena actual

● La escena dibujará cada uno de los elementos

● Pero es el director el que actualiza la pantalla

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Un director sencillo (1/4):


class Director():

def __init__(self):

# Inicializamos la pantalla y el modo grafico


self.screen = pygame.display.set_mode((ANCHO_PANTALLA, ALTO_PANTALLA))
pygame.display.set_caption("Juego con escenas")

# Pila de escenas
self.pila = []
Implementa una pila de escenas
# Flag que nos indica cuando quieren salir de la escena
self.salir_escena = False

# Reloj
self.reloj = pygame.time.Clock()

Flag para que una escena pueda indicar que se quiere terminar. Si se
El director posee el objeto reloj pone a True, la escena no terminará inmediatamente, sino cuando acabe
esa iteración del bucle de eventos
4 – Escenas/director.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D Elimina todos los eventos
que se hayan producido
antes de ejecutar la
● Un director sencillo (2/4): escena en sí
def bucle(self, escena):
self.salir_escena = False
Pone el flag de terminar la escena a false
# Eliminamos todos los eventos producidos antes de entrar en el bucle
pygame.event.clear()

# El bucle del juego, las acciones que se realicen se harán en cada escena
while not self.salir_escena:
El director
# Sincronizar el juego a 60 fps
tiempo_pasado = self.reloj.tick(60) implementa
el bucle del
# Pasamos los eventos a la escena
juego
escena.eventos(pygame.event.get())

# Actualiza la escena
escena.update(tiempo_pasado)

# Se dibuja en pantalla
escena.dibujar(self.screen)
pygame.display.flip()

4 – Escenas/director.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Un director sencillo (2/4):


def bucle(self, escena):
self.salir_escena = False

# Eliminamos todos los eventos producidos antes de entrar en el bucle


pygame.event.clear()

# El bucle del juego, las acciones que se realicen se harán en cada escena
while not self.salir_escena:
Envía la lista de
El director
# Sincronizar el juego a 60 fps eventos a la
tiempo_pasado = self.reloj.tick(60) implementa
escena
Actualiza la escena: el bucle del
# Pasamos los eventos a la escena juego
escena.eventos(pygame.event.get()) llama al método
update de la escena
# Actualiza la escena
escena.update(tiempo_pasado)
Llama al método de dibujar de
# Se dibuja en pantalla la escena, pero es el director
escena.dibujar(self.screen)
pygame.display.flip() el que cambia el buffer
4 – Escenas/director.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escenas:
○ El director implementa una pila de escenas:

■ Una lista de escenas que deben ejecutarse al terminar cada una

■ Inicialmente sólo tiene una escena, la inicial

■ Cada escena puede crear una escena nueva y:

● Sustituir la actual por la nueva (eliminar la actual de la pila)

○ No se va a volver a la escena actual

● o apilar la nueva

○ Se va a volver a la escena actual

■ También puede finalizar sin crear escena nueva

● En este caso, el director la eliminará de la pila y ejecutará la siguiente que esté en la cima

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escenas:
○ El director implementa métodos:

■ Salir de una escena

● Pone un flag a True, se dejará de ejecutar el bucle al finalizar el ciclo actual

● Elimina la escena actual de la cima de la pila de escenas

○ Ejecutará la siguiente escena de la cima de la pila

■ Salir del programa

● Pone un flag a True, se dejará de ejecutar el bucle al finalizar el ciclo actual

● Elimina todas las escenas pendientes de la pila de escenas

○ Al estar la pila vacía, no habrá escenas que ejecutar y la aplicación finalizará

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escenas:
○ El director implementa métodos:

■ Cambiar de escena a otra nueva (pasada como parámetro)

● Llama a “salir de una escena”

○ Termina el bucle y elimina la escena actual de la pila

● Añade la nueva escena en la cima de la pila para que sea ejecutada

■ Apilar la escena actual y ejecutar otra nueva (pasada como parámetro)

● Pone un flag a True, se dejará de ejecutar el bucle al finalizar el ciclo actual

● Añade la nueva escena en la cima de la pila para que sea ejecutada

○ Pero no elimina la actual, que será ejecutada cuando termine la actual

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Un director sencillo (3/4):

def ejecutar(self):
Mientras haya
escenas en la pila, se
# Mientras haya escenas en la pila, ejecutaremos la de arriba ejecutará la de la
while (len(self.pila)>0):
cima
# Se coge la escena a ejecutar como la que este en la cima de la pila
escena = self.pila[len(self.pila)-1] Se toma la escena
de la cima
# Ejecutamos el bucle de eventos hasta que termine la escena
self.bucle(escena)
Se ejecuta el bucle
en esta escena,
hasta que ella
decida terminar
Ejecutar la pila de escenas. Inicialmente sólo habrá una

4 – Escenas/director.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Un director sencillo (4/4):


def salirEscena(self):
# Indicamos en el flag que se quiere salir de la escena Método para indicar
self.salir_escena = True
# Eliminamos la escena actual de la pila (si la hay) que una escena
if (len(self.pila)>0): quiere terminar
self.pila.pop()

def salirPrograma(self): Método para indicar que una escena


# Vaciamos la lista de escenas pendientes desea terminar la ejecución de todo
self.pila = []
self.salir_escena = True el programa

def cambiarEscena(self, escena):


self.salirEscena()
Método para indicar que una
# Ponemos la escena pasada en la cima de la pila escena desea cambiar por
self.pila.append(escena) otra (y no volver)
def apilarEscena(self, escena):
self.salir_escena = True Método para indicar que una
# Ponemos la escena pasada en la cima de la pila escena desea cambiar por
# (por encima de la actual)
self.pila.append(escena) otra, y luego volver
4 – Escenas/director.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Implementación de una escena:


○ Clase abstracta:

Director Escena

Menu Fase

4 – Escenas/escena.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Implementación de una escena:


○ Clase abstracta:

class Escena:
Mantiene una referencia al director para
def __init__(self, director): poder llamar a métodos como salirEscena
self.director = director o salirPrograma
def update(self, *args):
raise NotImplemented("Tiene que implementar el metodo update.")

def eventos(self, *args):


raise NotImplemented("Tiene que implementar el metodo eventos.")

def dibujar(self, pantalla):


raise NotImplemented("Tiene que implementar el metodo dibujar.")

Definiciones abstractas de los métodos relacionados con los


eventos, actualizar y dibujar
Son métodos propios de cada escena
4 – Escenas/escena.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Implementación de una escena:


○ Clase Fase:

■ Método update:
def update(self, tiempo):

for enemigo in iter(self.grupoEnemigos):


enemigo.mover_cpu(self.jugador1, self.jugador2)

self.grupoSpritesDinamicos.update(self.grupoPlataformas, tiempo)

if pygame.sprite.groupcollide(
self.grupoJugadores, self.grupoEnemigos, False, False)!={}:
self.director.salirEscena()

self.actualizarScroll(self.jugador1, self.jugador2)

self.fondo.update(tiempo)

Cuando un jugador colisiona con un enemigo, el jugador muere y la escena


termina. Esto se le comunica al director para que deje de ejecutarla y pase a
ejecutar la que esté en la cima de la pila
4 – Escenas/fase.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Implementación de una escena:


○ Clase Fase:

■ Método eventos:
4 – Escenas/fase.py
def eventos(self, lista_eventos):
# Miramos a ver si hay algun evento de salir del programa
for evento in lista_eventos:
# Si se quiere salir, se le indica al director
if evento.type == pygame.QUIT:
self.director.salirPrograma()

# Indicamos la acción a realizar segun la tecla pulsada para cada jugador


teclasPulsadas = pygame.key.get_pressed()
self.jugador1.mover(teclasPulsadas, K_UP, K_DOWN, K_LEFT, K_RIGHT)
self.jugador2.mover(teclasPulsadas, K_w, K_s, K_a, K_d)

Si llega un evento de que desean cerrar la ventana, no se termina la escena


solamente, sino que es necesario decirle al director que termine toda la
ejecución, y que no ejecute de nuevo las escenas que están en la pila.
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Escena Menu:
○ Contiene el menú inicial

○ La escena contiene distintas pantallas de menú

■ Cada pantalla no es una escena distinta

● Todas juntas forman una única escena

■ Contiene una lista de pantallas y un índice que dice cuál es la actual

■ Contiene métodos para cambiar a una pantalla determinada

● mostrarPantallaInicial(), mostrarPantallaConfiguracion(), etc.

● Cambia el valor del índice

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escena Menu:
○ Cada pantalla contiene los elementos de la interfaz

■ Objetos de la clase ElementoGUI (abstracta)

■ Mantienen una referencia a la pantalla en la que están

○ Cuando se hace clic, el evento pasa de la clase Menú a la Pantalla correspondiente

■ El objeto Pantalla envía el evento a cada uno de los objetos ElementoGUI, que le devuelven True o False si el clic fue en él

■ Cada objeto ElementoGUI tiene un método accion()

● Se llama cuando se hace clic en él

● Puede realizar acciones en el menú como

○ Cambiar la pantalla actual

○ Salir del programa

○ etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escena Menu:

Escena

Menu Pantalla ElementoGUI

PantallaInicial Boton TextoGUI

BotonJugar BotonSalir TextoJugar TextoSalir

4 – Escenas/menu.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escena Menu:
○ Clase ElementoGUI:

class ElementoGUI:
def __init__(self, pantalla, rectangulo): Guarda una referencia a la pantalla a la que
self.pantalla = pantalla pertenece, y el rectángulo que ocupa en
self.rect = rectangulo pantalla, para saber si se ha hecho clic
def establecerPosicion(self, posicion):
(posicionx, posiciony) = posicion Método para situarlo en pantalla
self.rect.left = posicionx
self.rect.bottom = posiciony
def posicionEnElemento(self, posicion): Método que
(posicionx, posiciony) = posicion
if (posicionx>=self.rect.left) and (posicionx<=self.rect.right) and dice si se ha
(posiciony>=self.rect.top) and (posiciony<=self.rect.bottom): hecho clic en
return True él
else:
return False

def dibujar(self):
Método
raise NotImplemented("Tiene que implementar el metodo dibujar.")
def accion(self): abstractos
raise NotImplemented("Tiene que implementar el metodo accion.")

4 – Escenas/menu.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escena Menu:
○ Clases Boton, BotonJugar y BotonSalir:

class Boton(ElementoGUI):
def __init__(self, pantalla, nombreImagen, posicion):
# Se carga la imagen del boton Constructor recibe
self.imagen = GestorRecursos.CargarImagen(nombreImagen,-1)
self.imagen = pygame.transform.scale(self.imagen, (20, 20)) la imagen del
# Se llama al método de la clase padre con el rectángulo botón y la posición
ElementoGUI.__init__(self, pantalla, self.imagen.get_rect())
# Se coloca el rectangulo en su posicion
self.establecerPosicion(posicion)
def dibujar(self, pantalla):
pantalla.blit(self.imagen, self.rect)
Método para dibujarlo en pantalla
class BotonJugar(Boton):
def __init__(self, pantalla):
Boton.__init__(self, pantalla, 'boton_verde.png', (580,530))
def accion(self): Clases hijas
self.pantalla.menu.ejecutarJuego() implementan el
class BotonSalir(Boton):
def __init__(self, pantalla):
método accion()
Boton.__init__(self, pantalla, 'boton_rojo.png', (580,560))
def accion(self):
self.pantalla.menu.salirPrograma()
4 – Escenas/menu.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escena Menu:
○ Clases TextoGUI y TextoJugar:

class TextoGUI(ElementoGUI):
def __init__(self, pantalla, fuente, color, texto, posicion):
# Se crea la imagen del texto Constructor
self.imagen = fuente.render(texto, True, color)
# Se llama al método de la clase padre con el rectángulo recibe el texto, la
ElementoGUI.__init__(self, pantalla, self.imagen.get_rect()) fuente, el color y
# Se coloca el rectangulo en su posicion la posición
self.establecerPosicion(posicion)

def dibujar(self, pantalla):


pantalla.blit(self.imagen, self.rect)
Método para dibujarlo en
pantalla
class TextoJugar(TextoGUI):
def __init__(self, pantalla):
# La fuente la debería cargar el estor de recursos
fuente = pygame.font.SysFont('arial', 26);
TextoGUI.__init__(self, pantalla, fuente, (0, 0, 0), 'Jugar', (610, 535))
def accion(self):
self.pantalla.menu.ejecutarJuego()

Clases hijas implementan el método accion()


4 – Escenas/menu.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escena Menu: Contiene la imagen de fondo y la lista de elementos GUI


○ Clase PantallaGUI:

class PantallaGUI:
def __init__(self, menu, nombreImagen):
self.menu = menu
# Se carga la imagen de fondo
self.imagen = GestorRecursos.CargarImagen(nombreImagen)
self.imagen = pygame.transform.scale(self.imagen, (ANCHO_PANTALLA, ALTO_PANTALLA))
# Se tiene una lista de elementos GUI
self.elementosGUI = []

def eventos(self, lista_eventos): Para saber a qué


for evento in lista_eventos: elemento GUI se ha
if event.type == MOUSEBUTTONDOWN:
self.elementoClic = None hecho clic, se interroga a
for elemento in self.elementosGUI: todos. Es necesario que
if elemento.posicionEnElemento(event.pos): se haya pulsado sobre
self.elementoClic = elemento
if event.type == MOUSEBUTTONUP: ese elemento y se haya
for elemento in self.elementosGUI: levantado sobre el mismo.
if elemento.posicionEnElemento(event.pos): Se llama al método
if (elemento == self.elementoClic):
elemento.accion() accion() de ese elemento
4 – Escenas/menu.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escena Menu:
○ Clase Menu (1/3):

class Menu(Escena):

def __init__(self, director):


# Llamamos al constructor de la clase padre Crea cada una de
Escena.__init__(self, director); las pantallas y las
# Creamos la lista de pantallas almacena en una
self.listaPantallas = [] lista
# Creamos las pantallas que vamos a tener
# y las metemos en la lista
self.listaPantallas.append(PantallaInicialGUI(self))
# En que pantalla estamos actualmente
self.mostrarPantallaInicial()
En este caso el método update no hace
def update(self, *args):
nada: el menú es estático.
return
Podría haber, por ejemplo, Sprites que se
movieran.
4 – Escenas/menu.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escena Menu:
○ Clase Menu (2/3):

def eventos(self, lista_eventos):

# Se mira si se quiere salir de esta escena


for evento in lista_eventos:
# Si se quiere salir, se le indica al director
if evento.type == KEYDOWN:
if evento.key == K_ESCAPE:
self.salirPrograma()
elif evento.type == pygame.QUIT:
self.director.salirPrograma()

# Se pasa la lista de eventos a la pantalla actual


self.listaPantallas[self.pantallaActual].eventos(lista_eventos)

Eventos: le pasa el evento a la pantalla actual


4 – Escenas/menu.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Escena Menu: Para dibujar, se le dice a la pantalla actual que se dibuje


○ Clase Menu (3/3):

def dibujar(self, pantalla):


self.listaPantallas[self.pantallaActual].dibujar(pantalla)

#--------------------------------------
# Metodos propios del menu

def salirPrograma(self): Métodos de menú que pueden ser


self.director.salirPrograma()
llamados por los distintos botones:
def ejecutarJuego(self): • Salir del programa
fase = Fase(self.director) • Ejecutar el juego
self.director.apilarEscena(fase)
• Cambiar a la pantalla inicial
def mostrarPantallaInicial(self): • Cambiar a la pantalla de
self.pantallaActual = 0 configuración
# def mostrarPantallaConfiguracion(self):
• etc.
# self.pantallaActual = ...

4 – Escenas/menu.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Código principal:
○ Crear el director

■ O referenciarlo mediante el patrón Singleton

○ Crear la primera escena

○ Apilar esa escena en el director

○ Decirle al director que ejecute las escenas apiladas

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Código principal en main.py:


if __name__ == '__main__':

# Inicializamos la libreria de pygame


pygame.init()
Inicializa la librería y se crea el
# Creamos el director director
director = Director()

# Creamos la escena con la pantalla inicial


escena = Menu(director) Se crea la escena con la pantalla
inicial y se apila
# Le decimos al director que apile esta escena
director.apilarEscena(escena)
Se le dice al director que
# Y ejecutamos el juego ejecute lo que hay en la pila
director.ejecutar()

# Cuando se termine la ejecución, finaliza la librería


pygame.quit()

4 – Escenas/main.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● ¿Y si durante el juego se quiere hacer una pausa?


○ Mientras se ejecuta la escena Fase

○ 2 posibilidades:

■ Se desea dejar la pantalla congelada

■ Se desea ir a un menú

● u otro tipo de funcionalidad

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Se desea dejar la pantalla congelada


○ ¡¡¡No parar el bucle de eventos!!!

■ No dejar el sistema a la espera en una línea de código esperando un evento determinado

■ Si se para, en la siguiente iteración el tiempo transcurrido en la iteración anterior (durante la pausa) será demasiado alto

● Al calcular las nuevas posiciones de los Sprites según su velocidad, estas no serán correctas

○ Modificar el método update de Fase a algo similar a:

def update(self, tiempo):

if (not self.pausa):

for enemigo in iter(self.grupoEnemigos):


enemigo.mover_cpu(self.jugador1, self.jugador2)

self.grupoSpritesDinamicos.update(self.grupoPlataformas, tiempo)

■ El bucle se sigue ejecutando, pero no se actualizan los objetos

○ Proceso similar si se quiere congelar el juego para mostrar un mensaje por pantalla, etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Se desea ir a un menú
○ Este menú será una nueva escena (MenuPausa)

○ Desde la escena Fase habrá que:

■ Crear la nueva escena MenuPausa

● O pedírsela al gestor de recursos

■ Apilar la escena y terminar el bucle (poner flag a True)

○ El director, cuando acabe el ciclo actual, pasará a ejecutar la escena que está en la cima de la pila (MenuPausa)

■ Cuando termine, la sacará de la pila y volverá a la escena que está en la cima (Fase) en el punto en el que la dejó

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Director (resumen):
○ Implementa el bucle de eventos

■ Envía los eventos a la escena

■ Llama a la función update de la escena

■ Llama a la función “dibujar” de la escena

○ Posibilita la transición entre escenas

■ Implementa los métodos de

● apilarEscena

● cambiarEscena

● salirEscena

● salirPrograma

○ Implementa un inicio de un primitivo motor de juego

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Otras funcionalidades:
○ Clipping

■ Operación útil para dibujar solamente en una parte de la pantalla

● El resto puede estar ocupada por elementos como menú de estado, panel, etc.

○ Se desea no pintar encima de estos elementos

○ Habitualmente al dibujar los elementos en la pantalla no se tiene en cuenta

■ Se dibujan los elementos en sus posiciones, aunque sean fuera de la pantalla

■ Podría ocurrir que estas posiciones coincidiesen encima del estos paneles

● Para hacer esto, los objetos Surface tienen un área de clipping

○ Rectángulo que define en qué parte de la ventana se puede dibujar

● Llamar al método set_clip de un objeto Surface pasándole un objeto Rect

○ También, método get_clip

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Otras funcionalidades:
○ Clipping

■ Ejemplo:

● Juego de estrategia en el que hay que dibujar un mapa y un panel


screen.set_clip(0, 0, 640, 300)
draw_map() El mapa será dibujado solamente en las 300
screen.set_clip(0, 300, 640, 180)
filas de píxeles superiores de la pantalla
draw_panel()

El panel se dibujará solamente en las 180


filas inferiores de píxeles

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Animaciones:
○ Sprites

■ En cada llamada al método update, se calcula qué imagen mostrar

■ Llamada 1 vez cada 1/FPS segundos

● Necesario calcular cada cuántas veces hay que cambiar la imagen a mostrar

○ Mejor técnica:

■ Crear una clase “Animacion”

● Definirla a partir de las imágenes que componen la animación y el tiempo que durará cada una

● Cada vez que se llame al método dibujar, dibuja la imagen correspondiente

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Animaciones: Imagen Tiempo (seg.) Imagen Tiempo (seg.) Imagen Tiempo (seg.)

○ Ejemplo:
0.1
0.1 0.1

0.1

0.1 0.1

0.1

0.1 0.2

0.1

0.1 0.2
0.1

0.1 0.2
0.1

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Animaciones:
○ Un sprite “clásico” también se puede ver como una animación

■ Cada tipo de movimiento es una animación distinta

■ Se asigna una duración a cada imagen

● La duración de cada imagen será el tiempo dividido por el número de imágenes

■ Cuando se cambia el tipo de movimiento, se cambia la animación a mostrar

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Animaciones:
○ De igual manera, una animación puede verse como un sprite
Imagen Tiempo (seg.)
■ Calcular, en cada llamada a update, según el tiempo transcurrido, si es necesario cambiar
de imagen o no
0.1
● Según la duración de cada imagen y el FPS, habrá que calcular cada cuántas
llamadas a update hay que cambiar la imagen

● Ejemplo: como si estas imágenes estuvieran montadas sobre una hoja de sprites 0.1

0.1

0.1

0.1

0.1

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Animaciones:
○ ¿Cuándo utilizar un tipo u otro?

■ Sprites: se quiere controlar y cambiar de forma arbitraria de una imagen a otra

● Por ejemplo, personajes

■ Animaciones

● La secuencia de animación va a ser fija

○ Como un vídeo

○ A pesar de ello, se puede desear realizar tareas como:

■ Pausar

■ Rebobinar

■ Avanzar al siguiente frame

■ etc.

○ y otras como

■ Cambiar de tamaño

■ Rotar

■ etc.

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Animaciones:
○ ¿Cómo mostrar animaciones con pygame?

■ Módulo pyganim, clase PygAnimation

■ http://inventwithpython.com/pyganim/

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Animaciones:
○ Clase PygAnimation:
Constructor: se le indican las imágenes y
class PygAnimation(object): la duración de cada una (frames)
def __init__(self, frames, loop=True)

def blit(self, destSurface, dest)


Método para dibujar: se le indica el
def getFrame(self, frameNum) visor y la posición
def getCurrentFrame(self)
def nextFrame(self, jump=1)
def prevFrame(self, jump=1) Métodos para cambiar el frame
def reverse(self)
def isFinished(self)
def play(self, startTime=None) Métodos para controlar la reproducción
def pause(self, startTime=None) de la animación
def stop(self)
def togglePause(self)

def flip(self, xbool, ybool) Métodos para realizar transformaciones


def scale(self, width_height) a la animación
def rotate(self, angle)
… 5 – Animaciones con pygame/pyganim.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ejemplo:
○ Se puede extender esa clase para darle atributos de posición
# Extendemos la clase animacion de PygAnimation para darle posicion
class Animacion(pyganim.PygAnimation):
def __init__(self, *args):
pyganim.PygAnimation.__init__(self, args)
# Posicion que tendra esta animacion
self.posicionx = 0
self.posiciony = 0

def mover(self, distanciax, distanciay):


self.posicionx += distanciax
self.posiciony += distanciay

def dibujar(self, pantalla):


self.blit(pantalla, (self.posicionx, self.posiciony))
5 – Animaciones con pygame/animaciones.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ejemplo:
○ Cada una de las animaciones que haya pueden ser

■ Instancias de esa clase

■ Si hay varias copias de una animación, se puede extender esa clase

class AnimacionFuego(Animacion):
def __init__(self):
pyganim.PygAnimation.__init__(self,[
('imagenes/flame_a_0001.png', 0.1),
('imagenes/flame_a_0002.png', 0.1),
('imagenes/flame_a_0003.png', 0.1),
('imagenes/flame_a_0004.png', 0.1),
('imagenes/flame_a_0005.png', 0.1),
('imagenes/flame_a_0006.png', 0.1)])

En el constructor se indica cada frame: imagen y duración

5 – Animaciones con pygame/animaciones.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ejemplo:
○ Cada una de las animaciones que haya pueden ser

■ Instancias de esa clase

■ Si hay varias copias de una animación, se puede extender esa clase

class AnimacionRayo(Animacion):
def __init__(self):
pyganim.PygAnimation.__init__(self,[
('imagenes/bolt_strike_0001.png', 0.1),
('imagenes/bolt_strike_0002.png', 0.1),
('imagenes/bolt_strike_0003.png', 0.1),
('imagenes/bolt_strike_0004.png', 0.1),
('imagenes/bolt_strike_0005.png', 0.1),
('imagenes/bolt_strike_0006.png', 0.1),
('imagenes/bolt_strike_0007.png', 0.1),
('imagenes/bolt_strike_0008.png', 0.1),
('imagenes/bolt_strike_0009.png', 0.1),
('imagenes/bolt_strike_0010.png', 0.1)])

En el constructor se indica cada frame: imagen y duración


5 – Animaciones con pygame/animaciones.py
Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco
Programación en 2D

● Ejemplo:
○ Cada una de las animaciones que haya pueden ser

■ Instancias de esa clase

■ Si hay varias copias de una animación, se puede extender esa clase


# La animacion del humo
class AnimacionHumo(Animacion):
def __init__(self):
pyganim.PygAnimation.__init__(self,[
('imagenes/smoke_puff_0001.png', 0.1),
('imagenes/smoke_puff_0002.png', 0.1),
('imagenes/smoke_puff_0003.png', 0.1),
('imagenes/smoke_puff_0004.png', 0.1),
('imagenes/smoke_puff_0005.png', 0.1),
('imagenes/smoke_puff_0006.png', 0.1),
('imagenes/smoke_puff_0007.png', 0.1),
('imagenes/smoke_puff_0008.png', 0.2),
('imagenes/smoke_puff_0009.png', 0.2),
('imagenes/smoke_puff_0010.png', 0.2)])

En el constructor se indica cada frame: imagen y duración


5 – Animaciones con pygame/animaciones.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ejemplo:
○ Animaciones en la escena Menu:

■ Las animaciones estarán en una pantalla determinada

■ Modificar el constructor de la clase abstracta PantallaGUI para que almacene una lista de animaciones

■ Modificar el método dibujar de la clase abstracta PantallaGUI para que dibuje también las animaciones

● Para dibujar cada animación, se llama al método «dibujar» de la clase Animacion

○ Llama al método blit de PygAnimation

■ El objeto de clase PygAnimation sabe qué imagen ha de poner en cada momento

■ Cada clase hija de PantallaGUI instanciará en el constructor las animaciones que use y las dará de alta en la lista

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ejemplo: Escena Menu:


○ Clase abstracta PantallaGUI: Constructor y dibujar:

class PantallaGUI:
def __init__(self, menu, nombreImagen):
self.menu = menu
# Se carga la imagen de fondo
self.imagen = GestorRecursos.CargarImagen(nombreImagen)
self.imagen = pygame.transform.scale(self.imagen, (ANCHO_PANTALLA, ALTO_PANTALLA))
# Se tiene una lista de elementos GUI
self.elementosGUI = []
# Se tiene una lista de animaciones Se crea la lista de animaciones
self.animaciones = []

def dibujar(self, pantalla):


# Dibujamos primero la imagen de fondo
pantalla.blit(self.imagen, self.imagen.get_rect())
# Después las animaciones
for animacion in self.animaciones: Se dibujan las animaciones
animacion.dibujar(pantalla)
# Después los botones
for elemento in self.elementosGUI:
elemento.dibujar(pantalla)
5 – Animaciones con pygame/menu.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ejemplo: Escena Menu:


○ Animaciones en el objeto PantallaInicialGUI:

class PantallaInicialGUI(PantallaGUI):
def __init__(self, menu):
PantallaGUI.__init__(self, menu, 'portada.jpg')
# Creamos los botones y los metemos en la lista

# La animacion del fuego
animacionFuego = AnimacionFuego()
Se crea la animación de la
# Aumentamos un poco el tamaño de la animacion escena
animacionFuego.scale((200,200)) Se transforma la animación
# La situamos en su posicion
animacionFuego.posicionx = 70 Se sitúa en el sitio donde estará
animacionFuego.posiciony = 100
# Iniciamos la animacion
animacionFuego.play() Se empieza a reproducir
# Y la introducimos en la lista
self.animaciones.append(animacionFuego) Se introduce en la lista

… 5 – Animaciones con pygame/menu.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ejemplo: PantallaInicialGUI: Constructor:



# La animacion del humo
animacionHumo = AnimacionHumo()
# La situamos en su posicion
animacionHumo.posicionx = 695
animacionHumo.posiciony = 420
# Iniciamos la animacion
animacionHumo.play() Se realiza el mismo proceso
# Y la introducimos en la lista
self.animaciones.append(animacionHumo) con el resto de animaciones

# La animacion del rayo


animacionRayo = AnimacionRayo()
# Rotamos un poco la animacion
animacionRayo.rotate(30)
# La situamos en su posicion
animacionRayo.posicionx = 512
animacionRayo.posiciony = 130
# Iniciamos la animacion
animacionRayo.play()
# Y la introducimos en la lista
self.animaciones.append(animacionRayo)
5 – Animaciones con pygame/menu.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ejemplo:
○ Escena Fase:

■ Animaciones de Fuego (detrás del decorado y delante de todo)

■ Constructor:

class Fase(Escena):
def __init__(self, director):

# Primero invocamos al constructor de la clase padre


Escena.__init__(self, director)

# Creamos el decorado y el fondo


self.decorado = Decorado()
self.fondo = Cielo()

… 5 – Animaciones con pygame/fase.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ejemplo:
○ Escena Fase:

■ Animaciones de Fuego (detrás del decorado y delante de todo)

■ Constructor:

self.animacionesDetras = []
for i in range(9):
# La animacion del fuego
animacionFuego = AnimacionFuego()
# Aumentamos un poco el tamaño de la animacion Se crean las
animacionFuego.scale((400,400)) animaciones que
# La situamos en su posicion
estarán “detrás”
animacionFuego.posicionx = 120*i - 200
animacionFuego.posiciony = 250 del decorado y
# Iniciamos la animacion se meten en una
animacionFuego.play() lista
animacionFuego.nextFrame(i)
# y la anadimos a la lista de animaciones detras
self.animacionesDetras.append(animacionFuego)
5 – Animaciones con pygame/fase.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ejemplo:
○ Escena Fase:

■ Animaciones de Fuego (detrás del decorado y delante de todo)

■ Constructor:

self.animacionesDelante = []
for i in range(11):
# La animacion del fuego
animacionFuego = AnimacionFuego()
# Aumentamos un poco el tamaño de la animacion Se crean las
animacionFuego.scale((450,450)) animaciones que
# La situamos en su posicion
estarán “delante”
animacionFuego.posicionx = 120*i - 200
animacionFuego.posiciony = 450 del decorado y
# Iniciamos la animacion se meten en una
animacionFuego.play() lista
animacionFuego.nextFrame(i)
# y la anadimos a la lista de animaciones delante
self.animacionesDelante.append(animacionFuego)
5 – Animaciones con pygame/fase.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Ejemplo:
○ Escena Fase:

■ Animaciones de Fuego (detrás del decorado y delante de todo)

■ Método dibujar:

Se dibuja en este orden:


def dibujar(self, pantalla): • Fondo
# Ponemos primero el fondo
self.fondo.dibujar(pantalla) • Animaciones detrás del
# Despues, las animaciones que haya detras decorado
for animacion in self.animacionesDetras: • Decorado
animacion.dibujar(pantalla)
# Después el decorado • Sprites
self.decorado.dibujar(pantalla) • Animaciones delante
# Luego los Sprites del decorado
self.grupoSprites.draw(pantalla)
# Y por ultimo, dibujamos las animaciones por encima del decorado
for animacion in self.animacionesDelante:
animacion.dibujar(pantalla)

5 – Animaciones con pygame/fase.py

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● Con esta forma de realizar animaciones se puede controlar el tiempo de cada frame, y de la animación en
general:
○ Útil para introducir animaciones dentro de un juego desarrollado con pygame

○ Pero para realizar una escena que sea una animación se necesitan otras funciones

■ Para sincronizar animaciones

○ Para realizar animaciones de este estilo:

■ Librería pyglet

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco


Programación en 2D

● pyglet: Librería multimedia para python


○ Basada en OpenGL

■ Posibilidad de realizar gráficos en 3D

○ Opciones de reproducción multimedia

○ Tiene implementado el bucle de eventos

○ No está orientada al desarrollo de videojuegos, aunque puede ser utilizada para ello

■ Algunas opciones, como la detección de colisiones, no las realiza, hay que gestionarlas a mano

Contornos Inmersivos, Interactivos y de Entretenimiento Enrique Fernández-Blanco

También podría gustarte