Colisión

Pyxen usa un sistema de colisión basado en cuadrícula. Los cuerpos de colisión viven en una cuadrícula espacial y el motor detecta superposiciones entre ellos cada fotograma. Esto mantiene la detección de colisiones simple, rápida y determinista.

Grid maps y grid bodies

Un GridMap define el mundo espacial — una cuadrícula de tiles con un tamaño de celda dado. Un GridBody es un rectángulo de colisión que vive dentro de esa cuadrícula.

La entidad con un GridMap debe ser el padre de las entidades con GridBody. El motor resuelve todas las colisiones dentro de un solo GridMap de forma independiente, lo que significa que puedes ejecutar varios GridMaps diferentes en el mismo mundo y no interactuarán entre sí.

level = world.spawn(
    map=GridMap(rows=20, columns=20, size=(16, 16), image="tileset")
)

player = world.spawn(
    parent=level,
    body=GridBody(pos=(32, 32), size=(16, 16), tag=1, mask=(2,))
)

Posición de la entidad

La posición de la entidad (x, y) se deriva automáticamente de la resolución de colisión del GridMap. Cuando el motor resuelve el movimiento del cuerpo, actualiza la transformación de la entidad para coincidir con la posición resuelta del cuerpo. No configuras x/y manualmente en entidades con GridBody — el sistema de colisión es dueño de su posición.

Mover cuerpos

Para mover una entidad con cuerpo, configura la propiedad move del cuerpo. El motor intentará el movimiento y resolverá las colisiones:

def update():
    player.body.move = (0, 0)
    if input.keyboard.right.down:
        player.body.move = (2, 0)

El motor mueve el cuerpo, comprueba colisiones y ajusta la posición final.

Tags y masks

Los tags y masks controlan qué cuerpos pueden colisionar entre sí.

Cuando un cuerpo en movimiento encuentra otro cuerpo o tile cuyo tag está en su mask, desliza contra él en vez de atravesarlo. Esto te da colisión sólida con movimiento suave a lo largo de paredes y obstáculos.

player = world.spawn(
    parent=level,
    body=GridBody(pos=(0, 0), size=(16, 16), tag=1, mask=(2, 3))
)

enemy = world.spawn(
    parent=level,
    body=GridBody(pos=(0, 0), size=(16, 16), tag=2, mask=(1,))
)

El jugador colisiona con tags 2 y 3 (enemigos y paredes). El enemigo colisiona con tag 1 (el jugador). Los cuerpos con tag 0 o sin mask coincidente se atraviesan entre sí.

Callbacks de colisión

Cuando un cuerpo golpea un tile, el motor llama a on_hit_tile:

def on_hit_tile(entity, tile):
    # la entidad golpeó un tile sólido
    pass

Cuando dos cuerpos se superponen, el motor llama a on_hit_body:

def on_hit_body(entity, other):
    if entity == player:
        entity.health.value -= 1

Estos se ejecutan durante el paso de colisión, después de update() y antes del renderizado.

Colisión con tilemap

Un GridMap define una cuadrícula de tiles, y los tiles pueden marcarse como sólidos usando tile tags:

level.map.set(row=0, column=5, tile=(1, 0), tag=2)

Cualquier cuerpo cuyo mask incluya ese tag deslizará contra el tile.

Cómo funciona

El sistema de colisión está basado en cuadrícula, no es pixel-perfect. Los cuerpos son rectángulos alineados con los ejes que se mueven dentro de la cuadrícula. Las simulaciones están aisladas por GridMap — cada entidad GridMap ejecuta su propia resolución de colisión independiente. Esto hace la colisión:

Para la API completa, ve GridBody y GridMap.