Hace algunos dias me propuse el desafió de hacer un motor 2D que corra en un único hilo, sobre la API de SDL3. Logré, en mi opinión, una buena interfaz de uso que decidí pasar a python (aunque la interfaz del motor en python con ctypes fue con ayuda de la ia).
En un único hilo de 2Ghz (de mi Intel i3, dónde hice las pruebas) pude mantener 60 FPS hasta con 1200 entidades renderizandose en la pantalla.
El motor tiene gestión de eventos por teclado, y entidades que se dividen en sprites estaticos y spriteas animados.
Para poder subir este artículo fuera de off topic, decidí publicar el código fuente en la GNU Public v3. Aunque me interesa más crear mis proyectos que unirme a proyectos ya existentes, cuestión de gusto.
Fuente del motor en C, en el Makefile esta el script para compilar el .so del motor, junto con la interfaz en python del motor (no editar para un correcto funcionamiento) y un test que la usa.
Primero que nada, aquí un ejemplo de cómo usar:
from interface import *
# ------------------------------------------------------------
# Ejemplo de uso básico
# ------------------------------------------------------------
# Primero el condicional para arrancar
if __name__ == "__main__":
# Creación de la ventana, use una 'b' antes del nombre, evite bugs no deseados
app = Aplication(b"My Game")
# Sea crea el gestor de entidades donde creará los personajes de su juego
# app, man pueden tener otros nombres, eliga
man = app.CreateEntityManager()
# Luego cargamos un sprite, y le damos un nombre
# Se guardan en una 'biblioteca' separada de las entidades
# Si es estatico, no use el metodo SetFrame()
# Si es animado, cree sus frames 'stop motion' todos en el mismo png
# Se divide en columnas y filas, todas iguales, le tiene que avisar al motor la forma
# nameEntityManager.SetFrame(entidad,numero_de_columnas,numero_de_filas,AnimationToken.DYNAMIC)
# AnimationToken.DYNAMIC para animación
# AnimationToken.STATIC para sprite fijo
man.LoadSprite("assets/Animacion.png","SPRITE")
# Creamos la entidad, asignamos un sprite cargado con el metodo LoadSprite
# Mandamos el nombre del Sprite en el parametro primero, en el siguiente el nombre de la entidad
man.CreateEntity("SPRITE","CAPA")
# Ahora, usamos el nombre de la entidad para buscarla y cambiarle algunos atributos
# e será la entidad, si se encuentra
e = man.SearchEntity("CAPA")
# Si el sprite es animado, aqui se asigna cómo es la animación
# nameEntityManager.SetFrame(entidad,numero_de_columnas,numero_de_filas,AnimationToken.DYNAMIC)
# AnimationToken.DYNAMIC para animación
# AnimationToken.STATIC para sprite fijo
man.SetFrame(e,2,2,AnimationToken.DYNAMIC)
# Con SetDimension() podemos escalar la entidad
# gestor_de_entidades.SetDimension(entidad,escala)
# menos a uno achica, mayor agranda
man.SetDimension(e,0.5)
# Asignamos valores para programar la jugabilidad
speed=10
x=100
y=100
# La siguiente linea inicia el bucle principal
# Con while, not, ya que app.EventProcess() devuelve false si no se cierra
# Se cierra con la x de la ventana y con 'Esc' o Escape
while not app.EventProcess():
# Se recomienda buscar siempre la entidad
# El manager guarda 1000 entidades, si se superan
# el manager hace una nueva reserva duplicando su capacidad
# Lo que cambia las direcciones de memoria de las entidades
# Y puede provocar una violación de segmento si no se renueva
# Por lo menos vuelva a hacer las busquedas cada vez que agrega una entidad
e = man.SearchEntity("CAPA")
# Así quedo la API de eventos, copia de la de SDL3
# Con el ejemplo de cómo mover la entidad
if app.GetEvent(EventKeys.UP):
y-=speed
if app.GetEvent(EventKeys.DOWN):
y+=speed
if app.GetEvent(EventKeys.RIGHT):
x+=speed
if app.GetEvent(EventKeys.LEFT):
x-=speed
# Asignanos la posicion que trabajo la logica a la entidad
# El motor ya tiene motor de físicas, dando los siguientes atributos
# SetPosition() SetVelocity() SetAceleration()
# Todos usados de la misma forma, pero con interacciones diferentes
# Segun la fisica
man.SetPosition(e,x,y)
# Entre app.DrawBegin() y app.DrawEnd() se dibujan las entidades
app.DrawBegin()
# man.Draw() dibuja todas las entidades en un bucle for each
# Hay que pasarle el delta time y la camara
# El app.GetDeltaTime() se puede usar en python
# Aunque el motor ya lo usa por vos en las animaciones y las fisicas
man.Draw(app.GetDeltaTime(),app.GetCam())
app.DrawEnd()
# Puesto que es un motor en C, hay que liberar la aplicacion y el manager
man.Free()
app.Quit()
# El motor implementa un sistema de camara con lo que se puede desplazar
# Con las teclas W A S D, moviendo la camara, evite usar eso con app.GetEvent(EventKeys.Tecla)
# Estoy antento a ideas nuevas, y futuras implementaciones del motor, si les interesa
# Me despido, saludos