pyxen.Entity
Un Entity es un identificador ligero — un contenedor de componentes.
Para una introducción al sistema de entidad-componente, consulta Entidades y componentes.
Componentes integrados
| Componente | Acceso |
|---|---|
| Transform | e.x, e.y, e.rotation, e.scale |
| Sprite | e.sprite, e.color |
| Animation | e.animation |
| Sound | e.sound |
| Music | e.music |
| GridBody | e.body |
| GridMap | e.map |
| Node | e.parent, e.children |
| Layer | e.layer |
| Name | e.name |
| Camera | e.camera |
Transform
e.x = 100
e.y = 50
e.rotation = 45
e.scale = (2, 2)
Leer scale devuelve un Vector2:
s = e.scale
print(s.x, s.y)
Sprite
e.sprite = sprite
e.color = (1.0, 0.5, 0.5, 1.0)
Leer color devuelve un Color:
c = e.color
print(c.r, c.g, c.b, c.a)
Animación de sprites
Cuando un sprite se crea con el parámetro animation, el motor reproduce la animación automáticamente. Puedes leer el estado de la animación mediante e.animation:
player.sprite = Sprite("hero", animation="walk")
Propiedades
| Propiedad | Tipo | Descripción |
|---|---|---|
name | str | Nombre del clip de animación actual |
frame | int | Índice del fotograma actual dentro del clip |
playing | bool | True si la animación se está reproduciendo |
finished | bool | True si una animación de una sola vez ha terminado |
anim = player.animation
if anim:
print(anim.name) # "walk"
print(anim.frame) # 2
print(anim.playing) # True
print(anim.finished) # False
Si no hay ninguna animación activa, e.animation devuelve None:
player.sprite = Sprite("hero", tile=(0, 0, 16, 16))
print(player.animation) # None
Cancelar
Asigna None a e.animation para detener la reproducción:
player.animation = None
Asignar un nuevo sprite cancela automáticamente cualquier animación existente.
Animaciones de una sola vez
Usa loop=False para animaciones que se reproducen una vez:
player.sprite = Sprite("hero", animation="attack", loop=False)
def update():
if player.animation and player.animation.finished:
player.sprite = Sprite("hero", animation="idle")
Tweens
Los tweens animan las propiedades de una entidad a lo largo del tiempo. El motor se encarga de la interpolación automáticamente — sin código por frame.
entity.tween()
# Forma con nombre de propiedad
entity.tween("x", to=200, duration=1.0)
entity.tween("alpha", to=0, duration=0.5, ease="out_quad")
# Forma abreviada — animar varias propiedades a la vez
entity.tween(x=200, y=100, duration=1.0, ease="out_quad")
| Parámetro | Tipo | Por defecto | Descripción |
|---|---|---|---|
property | str | — | Nombre de la propiedad (posicional o forma abreviada) |
to | float | — | Valor objetivo |
by | float | — | Offset relativo (alternativa a to) |
start | float | actual | Valor inicial (por defecto el valor actual) |
duration | float | 1.0 | Duración en segundos |
ease | str | "linear" | Nombre de la función de easing |
Usa to para un objetivo absoluto, o by para un offset relativo:
entity.tween("x", to=200, duration=1.0) # mover a x=200
entity.tween("x", by=50, duration=0.5) # mover 50 píxeles a la derecha
Usa start para sobreescribir el valor inicial:
entity.tween("x", to=200, start=0, duration=1.0) # siempre empezar desde 0
Propiedades animables
| Componente | Propiedades |
|---|---|
| Transform | x, y, rotation, sx, sy |
| Sprite | r, g, b, alpha |
| Camera | zoom |
Funciones de easing
| Nombre | Curva |
|---|---|
linear | Velocidad constante (por defecto) |
in_quad | Inicio lento, acelerando |
out_quad | Inicio rápido, desacelerando |
in_out_quad | Inicio y final lentos |
in_cubic | Inicio más lento que quad |
out_cubic | Inicio más rápido que quad |
in_out_cubic | Inicio y final suaves |
in_expo | Inicio muy lento, aceleración explosiva |
in_out_expo | Parte media explosiva |
out_expo | Inicio muy rápido, parada suave |
in_back | Retrocede antes de avanzar |
out_back | Sobrepasa el objetivo y se estabiliza |
in_out_back | Retroceso y sobrepaso |
in_elastic | Tensión elástica |
out_elastic | Sobrepaso y rebote elástico |
in_out_elastic | Elástico en ambos extremos |
entity.tweens.cancel()
Cancela tweens de una entidad. Pasa un nombre de propiedad para cancelar un solo tween, o llama sin argumentos para cancelar todos. El valor se congela en su posición actual.
entity.tweens.cancel("x") # cancelar una propiedad
entity.tweens.cancel() # cancelar todos
Resolución de conflictos
Iniciar un nuevo tween en la misma entidad y propiedad reemplaza el anterior. Los tweens en propiedades diferentes se ejecutan de forma independiente.
entity.tween("x", to=100, duration=1.0)
entity.tween("x", to=200, duration=0.5) # reemplaza el primer tween
entity.tween("y", to=50, duration=1.0) # se ejecuta junto al tween de x
Los tweens en entidades destruidas se limpian automáticamente.
Iniciar un animate() en una propiedad cancela cualquier tween() activo en esa propiedad, y viceversa.
Animaciones
Animaciones continuas que se ejecutan indefinidamente — oscilación de onda sinusoidal (pulsación, respiración, balanceo) y rotación a velocidad constante.
entity.animate() — Oscilar
Oscila una propiedad alrededor de un valor central con una onda sinusoidal.
entity.animate("sx", "sy", by=0.15, speed=6.0) # coin pulse
entity.animate("alpha", by=0.3, center=0.7, speed=4.0) # flicker 0.4–1.0
El valor sigue: center + sin(elapsed * speed) * by
entity.animate() — Girar
Acumula un valor a una velocidad constante. Omite by= para usar el modo de giro.
entity.animate("rotation", speed=180) # 180°/sec clockwise
Parámetros
| Parámetro | Tipo | Defecto | Descripción |
|---|---|---|---|
*props | str | — | Uno o más nombres de propiedad (posicionales) |
by | float | — | Amplitud (modo oscilación). Omitir para giro. |
center | float | actual | Valor central para la oscilación |
speed | float | 1.0 | Frecuencia angular (oscilación) o unidades/seg (giro) |
Las mismas 10 propiedades que los tweens: x, y, rotation, sx, sy, r, g, b, alpha, zoom.
entity.animations.cancel()
Cancela animaciones de una entidad. Pasa un nombre de propiedad para cancelar una sola animación, o llama sin argumentos para cancelar todas.
entity.animations.cancel("rotation") # cancelar una propiedad
entity.animations.cancel() # cancelar todas
Parent / Children
child.parent = parent
Acceder a los hijos:
for c in parent.children:
print(c.name)
Obtener el número de hijos:
parent.children.count
Componentes personalizados
Los componentes se crean asignando:
- Un diccionario — componente estructurado
- Un booleano — componente marcador
e.cell = {
'row': 0,
'col': 0
}
La primera asignación crea el esquema. Los tipos de campo se infieren y el esquema queda fijo — no se pueden añadir nuevos campos después.
Uso de componentes en sistemas
for cell in world.all("cell"):
...
Ver: pyxen.world.all()
Componentes marcadores
e.alive = True
Los marcadores no almacenan datos — solo indican presencia. Se consultan como componentes normales:
for e in world.all("alive"):
...
Acceso a campos de componentes
e.cell.row
e.cell.col
e.cell.row = 5
Tipos de campo soportados
| Tipo Python | Almacenado como |
|---|---|
int | Entero de 32 bits |
float | Flotante de 32 bits |
bool | Booleano |
str | Cadena fija (máx 31 chars) |
Entity | Referencia a entidad |
Los tipos inválidos producen:
TypeError: invalid value type in dict
Reglas de esquema
- Tamaño máximo de componente: 256 bytes
- Cadenas máximo: 31 caracteres
- Las claves del diccionario deben ser cadenas
- No se pueden añadir nuevos campos después de la creación del esquema
e.cell = {'row': 0}
e.cell = {'row': 0, 'new_field': 5}
ValueError: cannot create new fields after schema is complete
Es válido omitir campos en asignaciones posteriores — toman valores por defecto 0, 0.0, False, "", etc.
Comprobar si tiene un componente
e.has("cell")
Devuelve True si la entidad tiene el componente, False en caso contrario.
Funciona tanto con nombres de componentes personalizados como integrados:
e.has("sprite") # True si la entidad tiene un sprite
e.has("body") # True si la entidad tiene un grid body
Consultar descendientes
entity.all(*components, without=(), include_self=False)
Devuelve un iterador sobre todos los descendientes de la entidad (en profundidad a través de la jerarquía del grafo de escena) que coincidan con el filtro de componentes dado.
Funciona exactamente como world.all(), pero limitado a un subárbol en lugar de todo el mundo.
# Todos los descendientes con el componente "enemy"
for e in parent.all("enemy"):
...
# Con exclusión
for e in parent.all("enemy", without=("boss",)):
...
# Incluir la propia entidad padre
for e in parent.all("tag", include_self=True):
...
| Parámetro | Tipo | Por defecto | Descripción |
|---|---|---|---|
*components | str | — | Nombres de componentes requeridos |
without | tuple | () | Nombres de componentes a excluir |
include_self | bool | False | Si incluir la propia entidad |
Se soportan tanto nombres de componentes integrados como personalizados.
Eliminar componentes
Elimina un componente de una entidad usando del o asignando None:
# Ambos son equivalentes:
del entity.health
entity.health = None
Después de eliminarlo, entity.has("health") retorna False y la entidad ya no coincide con consultas para ese componente.
e = world.spawn()
e.health = {'hp': 100}
del e.health
e.has("health") # False
# world.all("health") no incluirá e
Puedes volver a añadir un componente después de eliminarlo:
del e.health
e.health = {'hp': 50} # funciona correctamente
Componentes integrados
Todos los componentes integrados se pueden eliminar:
del e.sprite # limpia datos del sprite
del e.body # limpia cuerpo de físicas
del e.camera # elimina cámara
del e.text # elimina texto
del e.sound # elimina sonido
del e.music # elimina música
del e.map # elimina mapa de cuadrícula
Sprite y body se inicializan a cero (la entidad aún tiene el almacenamiento, pero los datos están vacíos). Camera, text, sound, music y map se borran completamente.
Marcadores
e.enemy = True
del e.enemy # o: e.enemy = None
e.has("enemy") # False
Ciclo de vida de Entity
- Se crea con
world.spawn() - Se modifica mediante asignación de componentes
- Se eliminan componentes con
delo= None - Se destruye con
world.destroy()