Keep your place in this quest

Log in or sign up for free to subscribe, follow lesson progress, and access more learning content.

Ahora que has visto cómo funcionan los personajes y la animación en Cave, vamos a dar un paso atrás y entender el sistema de scripting que suele conectar todo: Python.

Python se usa en Cave para escribir el comportamiento del juego, herramientas del editor, callbacks de la interfaz de usuario, callbacks de animación, eventos de timeline y pequeños fragmentos de lógica personalizada que serían demasiado específicos para existir como un componente incorporado. No necesitas ser un programador avanzado de Python antes de usar Cave, pero es muy útil entender la forma básica de un script en Cave.

En esta lección, aprenderás:

  • Para qué se usa Python en Cave.
  • Cómo funcionan juntos los assets Python Script y los Python Components.
  • Qué hacen start() y update().
  • Cómo acceder a la entidad actual desde un script.
  • Cómo obtener componentes de una entidad.
  • Cuándo usar un Python Component o un Python Code Component.

El objetivo no es enseñar todo el lenguaje Python. El objetivo es que los scripts de Cave te resulten comprensibles cuando los abras.

¿Dónde Aprender Python?

Si no sabes programar en Python, Uniday Studio ofrece quests gratuitas para aprender Python. Ve a uniday.studio/learn para ver todas las opciones o empieza por aquí:

Para esta sección, asumiremos que ya tienes un conocimiento básico de los temas tratados en esas dos quests.


Para Qué Se Usa Python en Cave

En Cave, Python es la capa de scripting que usas cuando un objeto del juego necesita comportamiento.

Por ejemplo, Python puede usarse para:

  • Mover un jugador o enemigo.
  • Abrir una puerta.
  • Reproducir una animación.
  • Activar un sonido.
  • Iniciar un timeline.
  • Cambiar escenas.
  • Actualizar un elemento de UI.
  • Crear herramientas personalizadas para el editor.

Básicamente, puedes escribir Python Scripts para crear toda la lógica de tu juego.

Por eso el proyecto de inicio ya contiene scripts Python. El jugador por defecto, por ejemplo, no es solo una malla con animación. También tiene lógica de juego que lee la entrada, mueve el personaje, rota la malla y reproduce la animación correcta.

Así que, cuando escribes Python en Cave, normalmente no escribes código aislado. Estás escribiendo lógica que controla una entidad y se comunica con los componentes adjuntos a esa entidad.

Assets de Script y Python Components

El código Python normalmente vive dentro de un asset Python Script.

image.png

Luego, para hacer que ese script se ejecute en una escena, agregas un Python Component a una entidad y eliges qué clase del script debe ejecutarse.

Piénsalo así:

Parte Qué Hace
Asset Python Script Almacena el código.
Python Component Ejecuta una clase de ese script en una entidad.
Entidad El objeto que controla el script.
Clase cave.Component El comportamiento real que escribiste.

Por ejemplo, puedes tener un asset script llamado Door Controller. Dentro, puedes tener una clase llamada DoorController que hereda de cave.Component. Luego agregas un Python Component a tu entidad puerta y seleccionas esa clase.

Esta separación es importante porque el mismo script puede reutilizarse. Puedes colocar muchas puertas en la escena, cada una usando el mismo script controlador de puertas pero con propiedades diferentes o hijos distintos.

Un Componente Cave Mínimo

Un componente Python básico en Cave se ve así:

import cave

class MyComponent(cave.Component):
    def start(self, scene):
        print("¡El componente empezó!")

    def update(self):
        pass

Hay algunos detalles importantes aquí:

  • import cave da acceso al API Python de Cave.
  • class MyComponent(cave.Component) crea una clase de componente que Cave puede ejecutar.
  • start(self, scene) se ejecuta cuando el componente inicia.
  • update(self) se ejecuta cada frame mientras el componente está activo.

El nombre MyComponent puede ser cualquiera, pero en proyectos reales debes usar un nombre claro como DoorController, EnemyAI, Checkpoint o PlayerHealth.

Métodos de Ciclo de Vida

Cave llama algunos métodos automáticamente cuando tu componente está corriendo.

Los más comunes son:

Método Cuándo Se Ejecuta Uso Común
start(self, scene) Al iniciar el componente. Obtener referencias, leer propiedades, preparar variables.
firstUpdate(self) Siempre después del start de cada Entity Component, en la primera actualización. Crear variables que dependen de otros componentes ya inicializados.
update(self) Cada frame, si la escena no está en pausa. Movimiento, entrada, temporizadores, chequeo de estados.
pausedUpdate(self) Cada frame, si la escena está en pausa. Lógica mientras está en pausa.
end(self, scene) Al terminar el componente. Limpieza si es necesario.

La mayoría de scripts para principiantes usan start() y update(). Por ejemplo, si creas una plataforma que se mueve, start() es un buen lugar para guardar la posición original, y update() es donde mueves la plataforma cada frame.

También existen los métodos editorUpdate y lateUpdate, pero no los exploraremos aquí porque son un poco más avanzados.

Accediendo a la Entidad Actual

Dentro de un componente Cave, self.entity es la entidad que posee el Python Component.

Esta es una de las ideas más importantes en el scripting de Cave. El script no está flotando por la escena solo. Pertenece a una entidad.

Aquí un ejemplo sencillo:

import cave

class DoorController(cave.Component):
    def start(self, scene):
        self.transform = self.entity.getTransform()
        self.isOpen = False

    def update(self):
        pass

En este script:

  • self.entity es la entidad puerta.
  • self.entity.getTransform() obtiene el Componente Transform de la puerta.
  • self.isOpen es una variable usada por el script para recordar si la puerta está abierta.

Usarás este patrón todo el tiempo. Primero obtienes la entidad, luego los componentes o entidades hijas que necesitas, y después los usas en tu lógica.

Obteniendo Otros Componentes

Para controlar una entidad, normalmente necesitas obtener uno o más componentes de ella.

Por ejemplo, un script para jugador puede obtener:

  • El componente Transform para mover o rotar la entidad.
  • El componente Character para manejar el movimiento del personaje.
  • El componente Animation de una entidad hija malla para reproducir animaciones.
  • El componente Audio para reproducir un sonido en loop.

Aquí un ejemplo pequeño:

import cave

class SimpleMover(cave.Component):
    def start(self, scene):
        self.transform = self.entity.getTransform()
        self.speed = 2.0

    def update(self):
        self.transform.move(0, 0, self.speed * cave.getDeltaTime(), local=True)

Esto mueve la entidad hacia adelante cada frame.

Lo importante es cave.getDeltaTime(). Como update() se ejecuta cada frame, multiplicar el movimiento por delta time mantiene la velocidad constante aunque cambie la tasa de frames.

Leyendo Propiedades Personalizadas

Hardcodear valores está bien para una primera prueba, pero las propiedades editables suelen ser mejores para objetos reales del juego.

Por ejemplo, en lugar de escribir:

self.speed = 2.0

Puedes leer el valor desde las propiedades de la entidad:

self.speed = self.entity.properties.get("speed", 2.0)

Esto significa:

  • Si la entidad tiene una propiedad speed, úsala.
  • Si no, usa 2.0 como valor por defecto.

Esto es muy útil para scripts reutilizables. Puedes crear un script SimpleMover y usarlo en varias entidades, cada una con diferente velocidad.

Por ejemplo:

Entidad Propiedad speed
Plataforma Lenta 1.0
Plataforma Rápida 4.0
Peligro en Movimiento 7.0

El script es el mismo, pero el comportamiento cambia según la entidad.

Alternativamente, puedes crear variables modificables localmente para el propio Component en lugar de depender de las propiedades de la Entidad. Cuando creas una variable así:

import cave

class PlatformMover(cave.Component):
    # Esto será local:
    speed = 2.0

    def start(self, scene: cave.Scene):
        pass

    def update(self):
        events = cave.getEvents()

La variable speed será modificable localmente en cada instancia del componente:

image.png


Obteniendo Entidades Hijas

Muchos objetos en Cave están construidos como pequeñas jerarquías.

La plantilla de jugador es un buen ejemplo. La entidad raíz Player contiene la física y lógica de personaje, mientras que la entidad hija Mesh tiene la malla visual del personaje y el Componente Animation.

Así que, si un script en la entidad raíz del jugador quiere reproducir animaciones, primero obtiene la entidad hija mesh:

import cave

class PlayerAnimationExample(cave.Component):
    def start(self, scene):
        self.mesh = self.entity.getChild("Mesh")
        self.animator = self.mesh.get("Animation")

    def update(self):
        self.animator.playByName("p-idle", blend=0.2, loop=True)

Este es el mismo patrón usado por el controlador de jugador por defecto:

  1. Obtén la entidad hija.
  2. Obtén el componente de esa hija.
  3. Usa el componente cuando sea necesario.

Una vez entiendas ese patrón, muchos scripts de Cave son mucho más fáciles de leer.

El método getChild tiene un parámetro opcional "recursive" que por defecto es True. Si es True, buscará en todos los hijos de la entidad, incluyendo hijos de hijos, hasta encontrar la entidad que solicitaste por nombre.

Obteniendo Entidades de la Escena (Consultas de Escena)

Al crear un juego, es muy probable que necesites obtener otras entidades dentro de la escena. Así que exploremos eso. El primer paso es obtener la escena misma, y en Cave, tienes dos formas de hacerlo:

# Devuelve la escena activa:
scene = cave.getScene()

# Devuelve la escena a la que pertenece la entidad:
scene = self.entity.getScene()

Para mayor comodidad, los métodos start y end de cave.Component también reciben la escena como parámetro, ya que es muy probable que los uses en esos métodos. Para código local, como el Python Code Component, también hay una variable scene definida por defecto que puedes "usar mágicamente" y esperar que funcione.

Una vez que tengas la escena, puedes obtener una Entidad específica por su nombre usando el siguiente código:

watchtower = scene.get("Watch Tower 01")

Cave proporciona muchos otros métodos en la clase escena para que hagas otras consultas sobre la escena. Puedes hacer ray casts, sphere casts, comprobar una caja o esfera de contacto para consultas de colisión básicas, o incluso obtener todas las entidades, todas las entidades raíz, todas las entidades con una etiqueta específica, todas las entidades con propiedades específicas, o con un nombre específico, etc. Así que vale la pena revisar la API de Python para más detalles.

Recuerda siempre verificar si tu consulta devolvió algo válido. Por ejemplo:

# Consultando una Entidad inexistente:
ent = scene.get("This Entity Doesnt Exist")

if ent is None:
    print("Entidad inválida!")

Obtener Componentes de una Entidad

Una vez que tienes una entidad específica y verificas que no es nula, es buena idea entender cómo obtener componentes específicos de ella.

La entidad tiene un método específico llamado get al que le puedes pasar el nombre del componente como cadena, y automáticamente busca en la entidad si hay un componente que coincida con tu búsqueda:

animator = self.entity.get("Animation Component")

Para facilitarte la vida, si el nombre de un componente termina con la palabra "Component", lo cual es muy común, puedes omitir completamente esta terminación al escribir el nombre del componente en este método. También puedes elegir si quieres incluir espacios en nombres compuestos o juntarlo todo.

Por ejemplo, si quieres obtener el Rigid Body Component de una Entidad, aunque su nombre en Python sea RigidBodyComponent, todas las opciones siguientes funcionarán:

rb = self.entity.get("Rigid Body")
rb = self.entity.get("RigidBody")
rb = self.entity.get("Rigid Body Component")
rb = self.entity.get("RigidBodyComponent")
rb = self.entity.get("RigidBody Component")

Dado que Python no es fuertemente tipado, es buena práctica en Cave proporcionar una pista de tipo para el componente, lo que puede hacerse de la siguiente manera. Nota que en este caso debes incluir el nombre completo del componente, porque es una semántica de Python:

rb : cave.RigidBodyComponent = self.entity.get("Rigid Body")

Esto permite que IntelliSense funcione en editores externos como Visual Studio Code o incluso el autocompletado incorporado que Cave Engine ofrece en su editor de scripts integrado.

Para el Transform Component, al ser uno de los tipos de componentes más comunes y que consultarás frecuentemente, la Entidad tiene un método nativo para obtener su Transform principal:

transf = self.entity.getTransform()

Obtenerlo llamando self.entity.getTransform() da el mismo resultado que llamando self.entity.get("Transform"), pero la primera opción es más rápida y optimizada.

Si una entidad tiene múltiples componentes del mismo tipo, retornará la primera coincidencia, pero a veces puedes querer obtener todas las coincidencias. Por ejemplo, en una malla con múltiples materiales que en Cave se representa por una entidad con varios componentes mesh, puede que quieras obtener todos los componentes mesh. Para eso puedes usar el método getAll:

meshCmps = self.entity.getAll("Mesh")

# Cambiando todos los materiales a uno brillante:
for meshCmp in meshCmps:
    meshCmp.material.setAsset("Glowing Material")

Obtener Componentes Python de una Entidad

Ahora sabemos cómo obtener componentes nativos de Cave, pero ¿qué pasa si quieres obtener de una Entidad un componente que fue escrito por ti usando Python?

Para eso necesitas un método especial llamado getPy, que funciona igual que el método normal get, salvo que este también devuelve componentes Python:

myCmp = self.entity.getPy("MyCustomComponent")

Se requiere un método especial debido a optimizaciones internas para asegurar que Cave funcione lo más rápido posible.

Una vez que tienes tu Componente Python personalizado, puedes acceder libremente a sus variables y métodos hechos en Python:

myCmp = self.entity.getPy("MyCustomComponent")

# Modificando variables Python:
myCmp.customValue = 10

# Llamando métodos personalizados:
myCmp.doSomething()
myCmp.applyDamage(10)

Python Code Component

Además del componente Python regular, Cave también tiene un Python Code Component. Este componente es útil para scripts rápidos escritos directamente en la entidad, sin crear un asset de Python Script por separado.

Tiene todos los demás métodos, y la diferencia es que en el Python Code Component escribes el script Python directamente dentro del componente, no es modular ni reusable. Así que si creas una copia de la entidad, también se crea una copia del script. Pero a veces esto puede ser una forma más rápida de crear una lógica simple, como una moneda que gira.

Es bueno para:

  • Pruebas rápidas.
  • Callbacks pequeños.
  • Comportamientos puntuales.
  • Prototipos.

No es la mejor opción para sistemas de juego más grandes porque el código es más difícil de reutilizar y organizar, pero es óptimo para códigos pequeños.

Python Component vs Python Code Component

Usa esta regla simple:

Usa esto Cuando
Python Component El comportamiento debe ser reutilizado, editado como un asset o crecer con el tiempo.
Python Code Component El comportamiento es pequeño y local.

Por ejemplo, un EnemyAI reutilizable debería ser un asset Python Script usado por un Python Component. Un código pequeño que imprime algo o llama una función cuando un botón es presionado puede ser un Python Code Component.

Un buen primer objetivo para un script

Un buen primer script en Cave es algo pequeño y visible.

Intenta construir uno de estos:

  • Una plataforma que se mueve hacia adelante.
  • Una puerta que se abre cuando inicia la escena.
  • Una luz que se enciende y apaga cada pocos segundos.
  • Un objeto para recoger que reproduce un sonido y se desactiva.
  • Un botón que cambia a otra escena.

Estos ejemplos son pequeños, pero enseñan la flujo de trabajo más importante: obtener la entidad, obtener el componente, cambiar algo, probar en modo Play y ajustar.

Lo que debes recordar

Los scripts Python en Cave normalmente se adjuntan a entidades a través de Python Components.

El patrón más importante para principiantes es:

  1. Usa start() para obtener referencias y preparar valores.
  2. Usa update() para ejecutar comportamiento en cada frame.
  3. Usa self.entity para acceder a la entidad que posee el script.
  4. Usa componentes para mover, animar, reproducir sonidos o controlar el objeto.

Una vez que este patrón te resulte natural, programar en Cave será mucho menos misterioso. No solo escribes código, enseñas a las entidades cómo comportarse.