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 scripts que generalmente conecta todo: Python.

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

En esta lección, aprenderás:

  • Para qué se utiliza Python en Cave.
  • Cómo funcionan los activos de Python Script y los Python Components juntos.
  • 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 de Python. El objetivo es que los scripts de Cave se sientan comprensibles cuando los abras.

¿Dónde Aprender Python?

Si no sabes cómo programar en Python, Uniday Studio también ofrece misiones de aprendizaje gratuitas sobre Python. Ve a uniday.studio/learn para ver todas las opciones o comienza aquí:

Para esta sección, asumiremos que ya tienes un entendimiento sobre los temas tratados en esas dos misiones de aprendizaje.


Para Qué Se Utiliza Python en Cave

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

Por ejemplo, Python puede ser usado para:

  • Mover a un jugador o enemigo.
  • Abrir una puerta.
  • Reproducir una animación.
  • Activar un sonido.
  • Comenzar una línea de tiempo.
  • Cambiar escenas.
  • Actualizar un elemento de UI.
  • Crear herramientas personalizadas para el editor.

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

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

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

Activos de Script y Python Components

El código Python generalmente reside dentro de un activo de 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.

Piensa en ello de esta manera:

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

Por ejemplo, puedes tener un activo de script llamado Controlador de Puerta. Dentro de él, puedes tener una clase llamada DoorController. que hereda de cave.Component. Luego agregas un Python Component a tu entidad de puerta y seleccionas esa clase.

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

Un Componente Mínimo de Cave

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

import cave

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

    def update(self):
        pass

Hay algunos detalles importantes aquí:

  • import cave le da a tu script acceso a la API de Python de Cave.
  • class MyComponent(cave.Component) crea una clase de componente que Cave puede ejecutar.
  • start(self, scene) se ejecuta cuando comienza el componente.
  • update(self) se ejecuta cada frame mientras el componente esté activo.

El nombre MyComponent puede ser cualquier cosa que desees, pero en proyectos reales deberías usar un nombre claro como DoorController, EnemyAI, Checkpoint o PlayerHealth.

Métodos del Ciclo de Vida

Cave llama a algunos métodos automáticamente cuando tu componente está en funcionamiento.

Los más comunes son:

Método Cuando Se Ejecuta Uso Común
start(self, scene) Cuando comienza el componente. Obtener referencias, leer propiedades, preparar variables.
firstUpdate(self) Siempre después del método start de cada componente de entidad, en la primera actualización. Crear variables que dependen de que otros componentes hayan sido inicializados.
update(self) Cada frame, si la escena no está pausada. Movimiento, entrada, temporizadores, comprobaciones de estado.
pausedUpdate(self) Cada frame, si la escena ESTÁ pausada. Lógica de pausa.
end(self, scene) Cuando termina el componente. Limpiar si es necesario.

La mayoría de los scripts para principiantes usan start() y update(). Por ejemplo, si estás creando una plataforma móvil, start() es un buen lugar para almacenar la posición original, y update() es donde mueves la plataforma cada frame.

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

Accediendo a la Entidad Actual

Dentro de un componente de 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 flota por la escena por sí mismo. Pertenece a una entidad.

Aquí hay un ejemplo simple:

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 de la puerta.
  • self.entity.getTransform() obtiene el Transform Component de la puerta.
  • self.isOpen es una variable utilizada por el script para recordar si la puerta está abierta.

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

Obteniendo Otros Componentes

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

Por ejemplo, un script de 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 de malla hija para reproducir animaciones.
  • El componente Audio para reproducir un sonido en bucle.

Aquí hay un pequeño ejemplo:

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.

La parte importante es cave.getDeltaTime(). Dado que update() se ejecuta cada frame, multiplicar el movimiento por el delta time mantiene la velocidad de movimiento constante incluso si la tasa de cuadros cambia.

Leyendo Propiedades Personalizadas

El codificar valores fijos está bien para una primera prueba, pero las propiedades editables suelen ser mejores para objetos de juego reales.

Por ejemplo, en lugar de escribir esto:

self.speed = 2.0

Puedes leer el valor de 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 la tiene, 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 una velocidad diferente.

Por ejemplo:

Entidad Propiedad speed
Plataforma Lenta 1.0
Plataforma Rápida 4.0
Peligro Móvil 7.0

El script permanece igual, pero el comportamiento cambia por entidad.

Alternativamente, puedes crear variables localmente modificables para el componente en sí 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á localmente modificable para cada instancia de componente:

image.png


Obteniendo Entidades Hijas

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

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

Entonces, si un script en la entidad raíz del jugador quiere reproducir animaciones, primero obtiene la entidad hija de malla:

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 tipo de patrón utilizado por el controlador de jugador predeterminado:

  1. Obtener la entidad hija.
  2. Obtener el componente de esa hija.
  3. Usar el componente cuando sea necesario.

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

El método getChild tiene un parámetro opcional "recursivo", que por defecto es True. Si es True, consultará todos los hijos de la Entidad, incluyendo hijos de hijos, hasta que encuentre 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 en la escena. Así que exploraremos 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()

Por conveniencia, los métodos start y end de cave.Component también reciben la escena como un parámetro, ya que es muy probable que los uses en esos métodos. Para el código local como el Componente de Código Python, también hay una variable scene definida para ti por defecto que puedes "simplemente usarla" y esperar que funcione.

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

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

Cave proporciona muchos otros métodos en la clase de la escena para que realices otras consultas de escena. Puedes hacer ray casts, sphere casts, verificar una caja de contacto o esfera 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. Por lo tanto, vale la pena consultar la API de Python para más detalles.

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

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

if ent is None:
    print("¡Entidad no válida!")

Obtención de Componentes de la Entidad

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

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

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

Para facilitarte la vida, si un nombre de componente termina con la palabra "Component", que es muy común, puedes omitir completamente este final al escribir el nombre del componente en este método. También puedes elegir si quieres incluir espacio en blanco en los nombres de componentes de varias palabras o poner todo junto.

Por ejemplo, si quieres obtener el Rigid Body Component de una Entidad, aunque su nombre en Python sea RigidBodyComponent, todas las opciones a continuación 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 está fuertemente tipado, es una buena práctica de programación en Cave que proporciones un tipo de sugerencia para el tipo de componentes, lo cual se puede hacer de la siguiente manera. También nota que en este caso, debes incluir el nombre completo del componente, ya que 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 la autocompletación incorporada que Cave Engine proporciona para ti en su editor de scripts embebido.

Para el Transform Component, dado que es uno de los tipos de componentes más comunes y lo estarás consultando mucho, la Entidad tiene un método nativo para obtener su Transformación principal:

transf = self.entity.getTransform()

Obtenerlo llamando self.entity.getTransform() proporciona 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 de ese mismo tipo, devolverá la primera coincidencia, pero a veces puedes querer obtener todas las coincidencias. Por ejemplo, en una malla de múltiples materiales que en cave será representada por una entidad con múltiples componentes de malla, puedes querer obtener todos los componentes de malla. Y para eso, puedes usar el método getAll:

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

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

Obtención de Componentes de Python de la 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, y funciona exactamente de la misma manera que el método get regular, excepto que este también devolverá componentes de Python:

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

Requiere un método especial debido a optimizaciones internas para asegurarse de que Cave funcione lo más rápido posible.

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

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

# Cambiando variables de python:
myCmp.customValue = 10

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

Componente de Código Python

Además del Componente de Python regular, Cave también tiene un Componente de Código Python. El Componente de Código Python es útil para guiones rápidos escritos directamente en la entidad, sin crear primero un activo de Script de Python separado.

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

Es bueno para:

  • Pruebas rápidas.
  • Pequeños callbacks.
  • Comportamiento único.
  • 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.

Componente de Python vs Componente de Código Python

Usa esto como una regla simple:

Usa Esto Cuándo
Componente de Python El comportamiento debe ser reutilizado, editado como un activo o crecer con el tiempo.
Componente de Código Python El comportamiento es pequeño, local.

Por ejemplo, un EnemyAI reutilizable debería ser un activo de Script de Python utilizado por un Componente de Python. Un pequeño código que imprime algo o llama a una función cuando se presiona un botón puede ser un Componente de Código Python.

Un Buen Primer Objetivo de Script

Un buen primer script de 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 comienza la escena.
  • Una luz que se enciende y apaga cada pocos segundos.
  • Un ítem que reproduce un sonido y se desactiva.
  • Un botón que cambia a otra escena.

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

Lo Que Debes Recordar

Los scripts de Python en Cave suelen estar adjuntos a entidades a través de Componentes de Python.

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

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

Una vez que este patrón se sienta natural, la programación en Cave se vuelve mucho menos misteriosa. No solo estás escribiendo código. Estás enseñando a las entidades cómo comportarse.