Cave: Guía para Principiantes
Personajes y Animación
Lesson 13 of 19 • 25 XP
Keep your place in this quest
Log in or sign up for free to subscribe, follow lesson progress, and access more learning content.
Ahora entendamos cómo funcionan los personajes y las animaciones en Cave. Un personaje no es solo un modelo 3D caminando por el nivel. En una configuración de juego real, por lo general, combina física, lógica de movimiento, una malla visible, un esqueleto, animaciones y código o Logic Bricks que deciden qué animación debe reproducirse.
Para hacer esto práctico, utilizaremos la plantilla Player por defecto de los proyectos iniciales de Cave como la referencia principal. Si creas un Third Person Game o un Top Down Game, Cave genera un jugador que ya se mueve, tiene un personaje animado visible y reproduce animaciones de locomoción básicas.

Esta lección te mostrará cómo está organizada esa configuración y cómo puedes controlar las animaciones desde Python.
Aprenderás:
- Cómo está construida la plantilla
Playerinicial. - Por qué la malla visual es una entidad hija y no está en la entidad raíz del jugador.
- Qué hace el Animation Component.
- Cómo obtener el animador desde Python (una lógica similar se aplica para Logic Bricks).
- Cómo reproducir animaciones por nombre.
- Cómo funcionan las transiciones, capas y filtros de huesos.
- Cómo encajan los callbacks de animación y los sockets en el sistema.
La Estructura de la Plantilla del Jugador
En el proyecto inicial, el Player es una Entity Template. Esto significa que la configuración del jugador es reutilizable y puede colocarse en múltiples escenas.
La estructura es aproximadamente así:

Los hijos exactos pueden variar dependiendo de las opciones del proyecto que seleccionaste, pero la idea importante es que la entidad raíz Player posee la configuración de juego, mientras que la entidad hija Mesh posee el personaje animado visible.
El enemigo inicial utiliza una idea muy similar. La entidad raíz del enemigo tiene física y comportamiento de personaje, y su entidad hija Mesh contiene la malla visible y el Animation Component.
La Entidad Raíz del Jugador
La entidad raíz Player representa el personaje de juego. Generalmente contiene:
- Un
Transform Component. - Un
Character Component. - Componentes de Python para el movimiento del jugador, UI, ayudantes de animación y otra lógica de juego.
- Propiedades como salud o configuraciones de comportamiento opcionales.
- Hijos utilizados para cámara, UI, malla visual y objetos auxiliares.
El Character Component es especialmente importante porque maneja la física estilo personaje: caminar, saltar, colisiones, pendientes y movimiento contra el mundo. Esto significa que el Player raíz es principalmente responsable del juego y la física.
La Entidad Hija de la Malla
Dentro de la plantilla del jugador, hay una entidad hija llamada Mesh. Esta hija generalmente contiene:
- Un
Transform Component. - Uno o más
Mesh Components. - Un
Animation Component.
El Mesh Component le da al personaje su modelo 3D visible y material. Si el personaje utiliza múltiples materiales, Cave puede representar eso con múltiples Mesh Components, siguiendo la misma regla de multimaterial que se explicó en la lección de importación.
El Animation Component es el que evalúa el esqueleto y reproduce animaciones.
Así, de manera simple:
| Entidad | Responsabilidad Principal |
|---|---|
Player |
Física, movimiento, lógica de juego, propiedades. |
Player -> Mesh |
Modelo visible, material, animación de esqueleto. |
Esta separación es intencionada.
Por Qué la Malla Es una Entidad Hija
Te pueden preguntar por qué la malla no está colocada directamente en la entidad raíz Player.
La razón es que el personaje físico y el personaje animado visual a menudo necesitan diferentes transformaciones.
La transformación raíz Player representa el cuerpo de juego. Es la cosa que se mueve por el mundo, colisiona con paredes y lleva el Character Component. La transformación hija Mesh representa cómo se ve el personaje.
Esa separación es útil en muchas situaciones:
- La malla puede necesitar una escala diferente a la cápsula física.
- La malla puede necesitar un pequeño desplazamiento para alinearse con la colisión del personaje.
- La malla puede necesitar rotar independientemente del cuerpo de movimiento.
- La malla puede necesitar enfrentar la dirección de movimiento mientras la raíz mantiene una orientación de juego.
- La malla puede necesitar mantenerse mirando hacia adelante mientras el jugador se mueve de lado.
Por ejemplo, imagina que el jugador se está moviendo a la izquierda. Si tienes una animación adecuada de "caminar a la izquierda", puede que quieras que la malla del personaje siga mirando hacia adelante mientras la animación maneja el movimiento lateral. Pero si no tienes una animación lateral, tal vez quieras que la malla gire hacia la izquierda para que el personaje visualmente camine en esa dirección.
Ambos casos son válidos.
Requieren transformaciones separadas:
| Transformación | Controles |
|---|---|
Transformación raíz Player |
Posición física, cuerpo de juego, movimiento a través del mundo. |
Transformación hija Mesh |
Orientación visual, escala, desplazamientos, presentación de animación. |
Por eso el jugador inicial y el enemigo mantienen la malla animada como una entidad hija.
Qué Hace el Animation Component
El Animation Component es el componente principal utilizado para reproducir animaciones esqueléticas en una entidad.
Necesita:
- Un
Armature. - Una
Animationpor defecto. - Un Mesh Component válido en la misma entidad (o múltiples).
El armature define el esqueleto. La animación define cómo se mueve ese esqueleto a lo largo del tiempo. El Animation Component evalúa la pose animada final y la aplica al personaje visible.
En el jugador inicial por defecto, el hijo Mesh utiliza:
Proto MeshProto MatProto Armaturep-idlecomo la animación por defecto (animación de inactividad)
Esos activos iniciales están allí para que puedas inspeccionar de inmediato un personaje animado en funcionamiento.
Armatures, Animaciones y Retargeting
- Un
Armaturees el esqueleto del personaje. Contiene los huesos que la malla sigue. - Una
Animationalmacena el movimiento a lo largo del tiempo para los huesos en un armature.
El Animation Component conecta esas piezas:
| Pieza | Significado |
|---|---|
| Mesh | El modelo de personaje visible. |
| Armature | El esqueleto dentro del personaje. |
| Animation | El movimiento, como inactivo, caminar, correr o caer. |
| Animation Component | El componente que reproduce la animación en la entidad. |
Cave también puede retargetear animaciones cuando es posible. Si la animación que se está reproduciendo pertenece a un armature compatible diferente, el Animation Component puede usar retargeting para aplicar ese movimiento al armature actual.
El retargeting es útil cuando deseas reutilizar animaciones entre personajes compatibles, pero aún depende de la calidad y compatibilidad de los rigs importados. Si una animación se ve torcida, desfasada o extraña, verifica el rig fuente, el armature importado y la configuración de retargeting.
Obteniendo el Animador Desde Python
Ahora empecemos a explorar cómo podemos animar personajes a través de la lógica.
En los proyectos de Cave, es práctica común mantener la lógica de juego del jugador en la entidad raíz Player, y luego acceder a la hija Mesh desde ese código. Los scripts iniciales por defecto siguen esta idea.
Aquí está el patrón básico:
import cave
class PlayerAnimationExample(cave.Component):
def start(self, scene: cave.Scene):
self.mesh : cave.Entity = self.entity.getChild("Mesh")
self.animator : cave.AnimationComponent = self.mesh.get("Animation")
def update(self):
# Ejemplo: reproduciendo una animación de inactividad.
self.animator.playByName("p-idle", blend=0.2, loop=True)
En este ejemplo:
self.entityes la entidad raízPlayer.getChild("Mesh")encuentra la entidad hija visual.self.mesh.get("Animation")obtiene el Animation Component.- La variable se llama
animator, que es la práctica de nomenclatura común en los scripts de Cave. playByName(...)reproduce un activo de animación por nombre.
Esta es la conexión básica que necesitas antes de controlar animaciones a través del código.
Reproduciendo Animaciones por Nombre
El método más común que utilizarás es playByName.
Su uso básico se ve así:
self.animator.playByName("p-walk", blend=0.2, loop=True)
Los parámetros importantes son:
| Parámetro | Significado |
|---|---|
anim |
El nombre del activo de animación a reproducir. |
blend |
Cuánto tiempo, en segundos, Cave debe mezclar en la nueva animación. |
loop |
Si la animación debe repetirse. |
layer |
Qué capa de animación debe reproducir la animación. |
Por ejemplo:
self.animator.playByName("p-idle", blend=0.2, loop=True)
self.animator.playByName("p-walk", blend=0.2, loop=True)
self.animator.playByName("p-run", blend=0.2, loop=True)
self.animator.playByName("p-fall", blend=0.2, loop=True)
Estas son las mismas llamadas que utilizan los scripts de jugador y enemigo por defecto.
Un Ejemplo Simple de Locomoción
Una configuración común de animación del jugador es:
- Reproducir inactividad cuando el personaje está parado.
- Reproducir caminar cuando el personaje se mueve.
- Reproducir correr cuando el personaje se mueve mientras corre.
- Reproducir caer cuando el personaje no está en el suelo.
Aquí hay un ejemplo simplificado:
import cave
class SimplePlayerAnimator(cave.Component):
def start(self, scene: cave.Scene):
self.character : cave.CharacterComponent = self.entity.get("Character")
self.mesh : cave.Entity = self.entity.getChild("Mesh")
self.meshTransform : cave.TransformComponent = self.mesh.getTransform() if self.mesh else None
self.animator : cave.AnimationComponent = self.mesh.get("Animation") if self.mesh else None
def update(self):
if self.animator is None or self.character is None:
return
direction = self.character.getWalkDirection()
isMoving = direction.length() > 0
isRunning = False # Reemplaza esto con tu propia entrada o condición de juego.
if self.character.onGround():
if isMoving:
if isRunning:
self.animator.playByName("p-run", blend=0.2, loop=True)
else:
self.animator.playByName("p-walk", blend=0.2, loop=True)
else:
self.animator.playByName("p-idle", blend=0.2, loop=True)
else:
self.animator.playByName("p-fall", blend=0.2, loop=True)
Este ejemplo es intencionadamente simple. El controlador de inicio real también maneja la entrada, la dirección del movimiento, el salto, el comportamiento opcional de apuntar y hacer clic, y la rotación de la malla, pero la idea de la animación es la misma.
Rotando la Malla hacia el Movimiento
Debido a que la entidad visual Mesh tiene su propia transformación, puedes rotarla por separado del Player raíz.
El jugador inicial hace esto cuando el personaje se mueve:
if direction.length() > 0 and self.meshTransform:
self.meshTransform.lookAtSmooth(
self.entity.getTransform().transformDirection(-direction),
6.0 * cave.getDeltaTime()
)
La idea importante no es la matemática exacta. La idea importante es que el cuerpo de juego y la malla visual pueden ser controlados por separado.
Esto te permite elegir cómo debe enfrentar el personaje:
- Enfrentar la dirección del movimiento.
- Seguir mirando hacia adelante mientras se desplaza lateralmente.
- Rotar suavemente hacia la dirección deseada.
- Usar diferentes reglas de orientación visual para diferentes modos de juego.
Esta es una de las principales razones por las que el personaje inicial tiene una entidad Mesh hijo.
Mezclando Animaciones
La mezcla de animaciones hace que las transiciones sean más suaves.
Sin mezcla, cambiar de una animación a otra puede ser abrupto. Con mezcla, Cave puede transitar suavemente entre animaciones en la misma capa.
Por ejemplo:
# De esto:
self.animator.playByName("p-idle", blend=0.2, loop=True)
# A esto:
self.animator.playByName("p-walk", blend=0.2, loop=True)
Aquí, blend=0.2 significa que Cave se mezclará en la nueva animación durante 0.2 segundos.
Esto es especialmente importante para los personajes. Un jugador puede notar fácilmente los saltos bruscos de animación, incluso en un prototipo.
Buenos valores de mezcla para principiantes suelen ser pequeños:
0.1para transiciones muy rápidas.0.2para transiciones de locomoción normales.0.4o más para transiciones más lentas y pesadas.
Deberías ajustar esto por intuición.
Capas de Animación
Cave soporta múltiples capas de animación. Cada capa puede ejecutar su propia animación, y las capas más altas pueden superponerse a las más bajas. Puedes ejecutar animaciones en cualquier capa que desees.
La animación de locomoción predeterminada generalmente se ejecuta en la capa 0.
Por ejemplo:
self.animator.playByName("p-walk", blend=0.2, layer=0, loop=True)
Si tienes una animación de acción de la parte superior del cuerpo, como un ataque, apuntar o recargar, puedes reproducirla en otra capa:
self.animator.playByName("Attack", blend=0.1, layer=1, loop=False)
La idea es:
| Capa | Propósito Común |
|---|---|
| Capa 0 | Locomoción de cuerpo completo, como inactividad, caminar, correr, saltar, caer. |
| Capa 1 | Acciones de la parte superior del cuerpo, como atacar, apuntar, recargar, sostener un arma. |
Esto permite que el personaje siga caminando mientras otra capa añade una acción encima.
Pesos de Capas
Cada capa tiene un peso, que controla cuánto influencia tiene esa capa.
Puedes cambiarlo desde Python:
self.animator.setLayerWeight(1, 1.0)
También puedes leerlo:
weight = self.animator.getLayerWeight(1)
Un peso de 1.0 significa que la capa está completamente activa. Un peso de 0.0 significa que no tiene influencia visible.
Los pesos de capa son útiles cuando deseas hacer que una capa de acción se desvanezca dentro o fuera, como levantar un arma lentamente, apuntar o mezclarse en una postura especial.
Vale la pena mencionar que el parámetro de mezcla solo mezcla animaciones entre las mismas capas. Y no vamos a mezclar animaciones que se reproducen en diferentes capas. Así que si tienes una animación reproducida en la capa 0, y de repente reproduces una animación en la capa 1, no importa cuál sea el valor de mezcla, no se mezclarán. Solo se mezclarán si ya había otra animación ejecutándose en la capa 1. Si deseas mezclar entre capas, necesitas hacer una lógica personalizada para utilizar el peso de la capa para hacerlo.
Filtros de Huesos
Los filtros de huesos controlan qué huesos son afectados por una capa y cuánto influencia recibe cada hueso.
Esto es lo que hace posible la animación de la parte superior del cuerpo.
Por ejemplo, puedes querer:
- La capa 0 para controlar todo el cuerpo.
- La capa 1 para controlar solo la columna, los brazos, las manos y los huesos del arma.
En Python, puedes crear un filtro para una capa y asignar influencia a un hueso y sus hijos:
def setupUpperBodyLayer(self):
armature = self.animator.armature.get()
spine = armature.getBone("mixamorig:Spine")
upperBody = self.animator.createLayerFilter(1)
upperBody.defaultBlend = 0.0
upperBody.setToBone(spine, 1.0, recursive=True)
En este ejemplo:
defaultBlend = 0.0significa que la capa no afecta ningún hueso por defecto.setToBone(spine, 1.0, recursive=True)significa que la columna y sus hijos son afectados completamente.- La capa
1ahora se puede utilizar para animaciones de la parte superior del cuerpo.
Los nombres de tus huesos dependen del armature que importaste. Siempre inspecciona tu armature y usa los nombres de huesos correctos para tu personaje.
Ejemplo Práctico de Capas
Imagina un personaje en tercera persona que puede caminar y atacar al mismo tiempo.
Una posible configuración es:
| Capa | Qué Maneja |
|---|---|
| Capa 0 | Inactividad, caminar, correr, caer. |
| Capa 1 | Ataque de la parte superior del cuerpo. |
El código podría verse así:
import cave
class CombatAnimator(cave.Component):
def start(self, scene: cave.Scene):
self.mesh = self.entity.getChild("Mesh")
self.animator : cave.AnimationComponent = self.mesh.get("Animation") if self.mesh else None
if self.animator:
armature = self.animator.armature.get()
spine = armature.getBone("mixamorig:Spine")
upperBody = self.animator.createLayerFilter(1)
upperBody.defaultBlend = 0.0
upperBody.setToBone(spine, 1.0, recursive=True)
self.animator.setLayerWeight(1, 1.0)
def playWalk(self):
self.animator.playByName("p-walk", blend=0.2, layer=0, loop=True)
def playAttack(self):
self.animator.playByName("Attack", blend=0.1, layer=1, loop=False)
Este ejemplo asume que tienes un activo de animación llamado Attack. Si tu proyecto utiliza un nombre de animación diferente, usa ese nombre en su lugar.
La idea importante es que la locomoción se mantenga en la capa 0, mientras que el ataque se reproduce en la capa 1 y afecta solo a los huesos filtrados.
Callbacks de Animación
Las animaciones pueden ejecutar código Python en momentos específicos.
Cave soporta callbacks como:
- Al inicio de la animación.
- Al final de la animación.
- En un fotograma específico de la animación.
Los callbacks son útiles cuando una animación debe activar el juego o efectos.
Ejemplos:
- Reproducir un sonido de pisada.
- Generar polvo en un paso.
- Aplicar daño en un fotograma de ataque.
- Iniciar un rastro de arma.
- Activar un efecto de partícula.
- Notificar a la lógica que una animación ha terminado.
Las animaciones a menudo conocen mejor el momento exacto que el código. Si una espada debe causar daño solo cuando el golpe alcanza el área objetivo, un callback de animación puede colocar ese momento de juego en el fotograma correcto.
Ejemplo de Callback de Pisada
Los proyectos iniciales pueden incluir callbacks de pisadas en las animaciones de caminar y correr. Inspeccionar esos es una buena manera de ver un callback de animación práctico en acción.
La idea es simple:
- La animación sabe cuándo el pie toca el suelo.
- El callback se coloca en ese fotograma.
- El callback reproduce un sonido o genera un pequeño efecto.
Dentro de un callback de animación, Cave proporciona variables útiles como:
entity, la entidad propietaria.animator, el Componente de Animación.handle, la capa/manejador de animación que se está reproduciendo.
Un callback de pisada muy pequeño podría lucir así:
cave.playSound("Footstep Grass", volume=0.5)
Puedes ampliar esta idea más tarde para elegir diferentes sonidos dependiendo del material del suelo, la velocidad del personaje o el estado actual.
Callbacks de Animación Reutilizables
Los callbacks pertenecen al activo de animación.
Eso significa que si múltiples entidades reproducen la misma animación, el callback puede ejecutarse para cada entidad que la utilice.
Esto hace que los callbacks sean reutilizables. Por ejemplo, la misma animación de caminar puede activar sonidos de pisadas para cada personaje que la reproduzca, siempre que el código del callback use correctamente la entidad propietaria.
La animación almacena el tiempo. La entidad proporciona el contexto.
Callbacks de Pose Avanzados
Cave también soporta callbacks post-evaluación a través de Python. Estos suceden después de que el Componente de Animación evalúe la pose del armature.
Esta es una función avanzada, pero se puede utilizar para:
- Cinemática inversa.
- Apuntando con la cabeza.
- Apuntando con el arma.
- Colocación de pies.
- Ajustes finales de pose.
El Player Toolkit inicial incluye un ejemplo de IK de pie que utiliza este concepto. Obtiene el hijo Mesh, obtiene el componente Animation, y luego registra un método con:
self.animator.addPostEvaluationCallback(self.postEvaluation)
No necesitas escribir sistemas de IK de inmediato, pero es útil saber que Cave permite a Python ajustar la pose final después de la reproducción normal de la animación.
Componente de Socket de Animación
El Componente de Socket de Animación permite a una entidad hija seguir un hueso de una entidad padre animada.
La entidad hija debe estar bajo un padre que tenga un Componente de Animación. Luego, el socket puede elegir un hueso y copiar la posición, rotación y, opcionalmente, escala de ese hueso, con desplazamientos si es necesario.
Los sockets son útiles para adjuntar cosas a personajes animados:
- Espada en una mano.
- Arma en una mano.
- Escudo en un brazo.
- Casco en una cabeza.
- Mochila en un hueso de la columna.
- Prop conectado a una mano.
Por ejemplo, si un personaje sostiene una espada, la espada puede ser una entidad hija con un Componente de Socket de Animación que sigue el hueso de la mano. A medida que el personaje ataca, corre o permanece inactivo, la espada se mantiene unida a la posición animada correcta.
Lo Que Debes Recordar
- El jugador inicial es una plantilla con una entidad raíz
Playery una entidad hijaMesh. - La raíz
Playergeneralmente maneja la física, el movimiento, las propiedades y la lógica del juego. - La entidad hija
Meshgeneralmente maneja el modelo visible, material, armadura y Componente de Animación. - Esta separación le da al cuerpo físico y al personaje visual transformaciones separadas.
- El Componente de Animación reproduce animaciones esqueléticas usando una armadura y activos de animación.
- En Python, es común llamar al Componente de Animación
animator. playByNamereproduce un activo de animación por nombre y soporta mezclas, bucles y capas.- Las capas permiten que múltiples animaciones se apilen juntas.
- Los filtros de huesos permiten que una capa afecte solo a parte del esqueleto.
- Los callbacks de animación conectan el tiempo de la animación a eventos de juego.
- Los sockets de animación conectan props a huesos animados.
Cuando inspecciones la plantilla inicial Player o Enemy, usa esta estructura como tu mapa: entidad raíz para la jugabilidad, entidad hija Mesh para visuales y animación.