Trabajar en 3D con Pygame
Introducción
Los juegos desarrollados durante este libro son exclusivamente en dos dimensiones (2D). En la práctica, Pygame está más asociado con el desarrollo de juegos 2D, aunque permite la visualización tridimensional.
Hay varios videojuegos que son tridimensionales o utilizan gráficos que hacen que se acerquen a las tres dimensiones. En particular, podemos mencionar los llamados juegos FPS (First Person Shooter, videojuego de disparos con vista en primera persona, en español). Este tipo de juego suele consistir en la encarnación de un personaje con un arma de fuego dentro de un 3D inmersivo. El objetivo es derribar a los enemigos que se va encontrando. En general, el personaje principal se mueve a través de una red de pasillos dibujados en tres dimensiones.
Debido a su base SDL, Pygame es una herramienta muy orientada a las 2D. Todos los juegos que se han puesto como ejemplos en este libro, son juegos bidimensionales. Entonces, ¿qué necesitarían para ser juegos en 3D? La respuesta puede parecer obvia: una decoración en 3D y una visualización en 3D. En otras palabras, su algoritmo sigue siendo el mismo: la lógica y el bucle del juego en sí, se supone que no necesitan modificaciones excesivas. Pero queda por ver cómo hacer 3D con Pygame. La respuesta se puede resumir en una palabra: OpenGL.
La librería 3D OpenGL
OpenGL (Open Graphics Library) es una librería que permite hacer 3D. Se utiliza en muchos dominios más allá de los videojuegos: simulación científica, CAO, realidad aumentada, etc. Su objetivo es permitir crear objetos 3D a los que se puedan asociar texturas. OpenGL se encarga de la visualización de estos objetos 3D teniendo en cuenta la orientación, la distancia y los aspectos relativos a la luz, como la sombra proyectada, la transparencia, etc.
Desde luego, el objetivo aquí no es enseñarle OpenGL. De hecho, OpenGL es un mundo amplio y complejo y se necesitaría mucho más que estas pocas páginas para aprender a programar de forma autónoma con esta herramienta. El objetivo es acompañarle en sus primeros pasos en 3D en un entorno Pygame.
OpenGL en Python/Pygame
1. PyOpenGL
Hay un módulo de Python que permite trabajar con OpenGL: PyOpenGL. Si no está instalado en su máquina, lanzar el comando pip en el terminal le permite hacerlo.
pip install PyOpenGL
Para usarlo en código Python, simplemente importe el módulo como se hace normalmente (para Pygame, por ejemplo). En general, procedemos a importar escribiendo estas dos líneas:
from OpenGL.GL import *
from OpenGL.GLU import *
2. Las nociones fundamentales: vértice y arista
Para definir objetos 3D en OpenGL, utilizaremos dos conceptos fundamentales: vértice (vertex en inglés) y arista (edge en inglés).
Esquemáticamente, podemos considerar que los vértices son puntos en el espacio y, por lo tanto, tienen sus coordenadas en tres dimensiones. Por ejemplo: (1, 2, -4). Las aristas son los segmentos que conectan los vértices.
De esta manera, si definimos dos vértices:
-
vértice 1 situado en las coordenadas (1, 0, 0),
-
vértice 2 situado en las coordenadas (0, -1, 0),
-
entonces podemos definir la arista 1 que conecta el vértice 1 con el vértice 2.
Esto es equivalente a pensar que definir los objetos 3D en OpenGL es, ante todo, definir una colección de puntos tridimensionales (vértices) y luego definir las aristas que conectan estos vértices entre sí.
3. PyOpenGL y Pygame
Cuando creamos la ventana de juego...
PyOpenGL/Pygame: el ejemplo del cubo
Tomemos el ejemplo clásico de un cubo representado en tres dimensiones, que sometemos a una rotación continua. La idea es mostrar solo las aristas del cubo; no definimos ninguna textura o coloración de las caras.
1. El código global
Comencemos presentando el código global del ejemplo, antes de proceder a la explicación detallada del programa.
import pygame
from OpenGL.GL import *
from OpenGL.GLU import *
VERTICES = (
(1, -1, -1),
(1, 1, -1),
(-1, 1, -1),
(-1, -1, -1),
(1, -1, 1),
(1, 1, 1),
(-1, -1, 1),
(-1, 1, 1)
)
ARISTAS = (
(0,1),
(0,3),
(0,4),
(2,1),
(2,3),
(2,7),
(6,3),
(6,4),
(6,7),
(5,1),
(5,4),
(5,7)
)
def Cubo():
glBegin(GL_LINES)
for arista in ARISTAS:
for vertice in arista:
glVertex3fv(VERTICES[vertice])
glEnd()
pygame.init()
ZONA = (800,800)
pygame.display.set_mode(ZONA, pygame.DOUBLEBUF|pygame.OPENGL) ...
Ir más lejos con PyOpenGL/Pygame: el ejemplo del cubo (continuación)
Volvamos al ejemplo anterior tratando de colorear las superficies de tal manera que se acentúe la impresión 3D.
Un punto importante que se debe aclarar: un color RGB, que se define en Python/Pygame con valores entre 0 y 255, se define en el lado OpenGL con valores entre 0 y 1. Si tomamos el ejemplo del color rojo:
-
está codificado (255, 0, 0) en Pygame,
-
está codificado (1, 0, 0) en OpenGL.
1. El código global
Nos encontramos en gran medida con la trama del código global anterior, al que vamos a añadir la gestión de superficies y colores.
import pygame
from OpenGL.GL import *
from OpenGL.GLU import *
COLORES = (
(1,0,0),
(0,1,0),
(0,0,1),
(1,0,0),
(0,1,0),
(0,0,1),
(1,0,0),
(0,1,0),
(0,0,1),
(1,0,0),
(0,1,0),
(0,0,1),
)
VERTICES = (
(1, -1, -1),
(1, 1, -1),
(-1, 1, -1),
(-1, -1, -1),
(1, -1, 1),
(1, 1, 1),
(-1, -1, 1),
(-1, 1, 1)
)
ARISTAS = (
(0,1),
(0,3),
(0,4),
(2,1),
(2,3),
(2,7),
(6,3),
(6,4),
(6,7),
(5,1),
(5,4), ...
OpenGL en Python y macOS
Se supone que los códigos anteriores funcionan en todos los sistemas operativos Linux, Windows o macOS, habilitando un entorno virtual basado en el archivo requirements.txt (generado en diciembre de 2022):
pygame==2.1.2
PyOpenGL==3.1.6
Sin embargo, un error que afecta exclusivamente a algunos sistemas macOS (Linux y Windows no se ven afectados), causa el siguiente error al usar OpenGL con Python:
ImportError: ('Unable to load OpenGL library', 'dlopen(OpenGL, 10):
image not found', 'OpenGL', None)
Dos soluciones pendientes de la resolución de este error, en el lado de PyOpenGL:
-
Parchear el árbol de PyOpenGL, que es bastante desaconsejable.
-
Utilizar en su código PyOpenGL, las siguientes líneas de código.
import pygame
try:
import OpenGL as ogl
try:
import OpenGL.GL
except ImportError:
print('Patch para MacOS')
from ctypes import util
orig_util_find_library = util.find_library
def new_util_find_library(name):
res = orig_util_find_library(name)
if...
La noción de motor de videojuegos
1. Definición
Un motor de videojuegos es un paquete de software que hace la vida más fácil en el marco del desarrollo de videojuegos. Tomemos como ejemplo el desarrollo de un juego que consiste en lanzar una pelota al suelo, una pelota que rebota de forma realista, algunas veces en el suelo y otras en un charco, en cuyo caso la simulación difiere un poco (proyección de agua, amortiguación, etc.). El motor permite centrarse en la parte principal del juego de simulación, es decir, la ambientación, la jugabilidad y el escenario. Los aspectos técnicos están completamente dedicados al motor del juego. Entre otros:
-
la colisión pelota/suelo,
-
la simulación de la gravedad,
-
la simulación de proyecciones acuosas,
-
la representación 3D de la pelota,
-
etc.
Muchos aspectos que podemos agrupar en una gran familia de aspectos físicos, representaciones 2D/3D, gestión de colisiones, etc.
2. ¿Crear su propio motor de juego?
Pygame ofrece una serie de herramientas para la gestión de colisiones (funciones de tipo spritecollide, etc.). ¿Significa esto que Pygame es un motor de juego? En absoluto. Es cierto que algunos aspectos de Pygame podrían sugerirlo (gestión de colisiones), pero no va mucho más allá. Simplemente no es el papel de Pygame, que sigue siendo de nivel relativamente bajo. Pudimos...