Keep your place in this quest

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

Maintenant que vous avez vu comment fonctionnent les personnages et l'animation dans Cave, faisons un pas en arrière pour comprendre le système de script qui relie généralement tout : Python.

Python est utilisé dans Cave pour écrire le comportement du gameplay, les outils de l'éditeur, les callbacks de l'interface utilisateur, les callbacks d'animation, les événements de timeline, et de petites logiques personnalisées trop spécifiques pour exister en tant que composant intégré. Vous n'avez pas besoin de devenir un programmeur Python avancé avant d'utiliser Cave, mais il est très utile de comprendre la structure basique d'un script Cave.

Dans cette leçon, vous apprendrez :

  • À quoi sert Python dans Cave.
  • Comment les assets Python Script et les Python Components fonctionnent ensemble.
  • Que font start() et update().
  • Comment accéder à l'entité actuelle depuis un script.
  • Comment obtenir des composants depuis une entité.
  • Quand utiliser un Python Component ou un Python Code Component.

L'objectif n'est pas d'enseigner tout le langage Python. L'objectif est de rendre les scripts Cave compréhensibles lorsque vous les ouvrez.

Où apprendre Python ?

Si vous ne savez pas coder en Python, Uniday Studio propose en fait des learn quests gratuites sur Python. Rendez-vous sur uniday.studio/learn pour voir toutes les options ou commencez par ici :

Pour cette section, nous supposerons que vous avez déjà une compréhension des sujets traités dans ces deux learn quests.


À quoi sert Python dans Cave

Dans Cave, Python est la couche de script que vous utilisez quand un objet de jeu a besoin d'un comportement.

Par exemple, Python peut être utilisé pour :

  • Déplacer un joueur ou un ennemi.
  • Ouvrir une porte.
  • Jouer une animation.
  • Déclencher un son.
  • Démarrer une timeline.
  • Changer de scène.
  • Mettre à jour un élément UI.
  • Créer des outils personnalisés pour l'éditeur.

En gros, vous pouvez écrire des Python Scripts pour créer toute la logique de votre jeu.

C'est pourquoi le projet de démarrage contient déjà des scripts Python. Le joueur par défaut, par exemple, n'est pas juste un maillage avec une animation. Il a aussi une logique de gameplay qui lit les entrées, déplace le personnage, fait pivoter le maillage et joue la bonne animation.

Donc, quand vous écrivez du Python dans Cave, vous n'écrivez généralement pas du code isolé. Vous écrivez une logique qui contrôle une entity et interagit avec les composants attachés à cette entité.

Assets Script et Python Components

Le code Python vit généralement dans un asset Python Script.

image.png

Ensuite, pour faire tourner ce script dans une scène, vous ajoutez un Python Component à une entité et choisissez quelle classe du script doit être exécutée.

Pensez-y comme ceci :

Partie Ce qu'elle fait
Asset Python Script Stocke le code.
Python Component Exécute une classe de ce script sur une entité.
Entity L'objet contrôlé par le script.
Classe cave.Component Le comportement que vous avez écrit.

Par exemple, vous pouvez avoir un asset script appelé Door Controller. À l'intérieur, vous pouvez avoir une classe nommée DoorController qui hérite de cave.Component. Ensuite vous ajoutez un Python Component à votre entité porte et sélectionnez cette classe.

Cette séparation est importante parce que le même script peut être réutilisé. Vous pouvez placer plusieurs portes dans la scène, chacune utilisant le même script de contrôleur de porte, mais avec des propriétés ou des objets enfants différents.

Un composant Cave minimal

Un Python Component basique dans Cave ressemble à ceci :

import cave

class MyComponent(cave.Component):
    def start(self, scene):
        print("The component started!")

    def update(self):
        pass

Quelques détails importants ici :

  • import cave donne à votre script accès à l'API Python de Cave.
  • class MyComponent(cave.Component) crée une classe de composant que Cave peut exécuter.
  • start(self, scene) s'exécute quand le composant démarre.
  • update(self) s'exécute à chaque image tant que le composant est actif.

Le nom MyComponent peut être ce que vous voulez, mais dans les vrais projets vous devriez utiliser un nom clair comme DoorController, EnemyAI, Checkpoint, ou PlayerHealth.

Méthodes du cycle de vie

Cave appelle automatiquement certaines méthodes quand votre composant est actif.

Les plus communes sont :

Méthode Quand elle s'exécute Usage courant
start(self, scene) Quand le composant débute. Récupérer des références, lire les propriétés, préparer des variables.
firstUpdate(self) Toujours après la méthode start de chaque Entity Component, à la première mise à jour. Créer des variables dépendantes d'autres composants déjà initialisés.
update(self) Chaque frame, si la scène n'est pas en pause. Mouvement, entrée, timers, vérifications d'état.
pausedUpdate(self) Chaque frame, si la scène est en pause. Logique en pause.
end(self, scene) Quand le composant se termine. Nettoyer si nécessaire.

La plupart des scripts débutants utilisent start() et update(). Par exemple, pour une plateforme mobile, start() est un bon endroit pour stocker la position d'origine, et update() est là où vous déplacez la plateforme à chaque frame.

Nous avons aussi les méthodes editorUpdate et lateUpdate, mais nous ne les explorerons pas ici car elles sont un peu plus avancées.

Accéder à l'entité courante

Dans un composant Cave, self.entity est l'entité propriétaire du Python Component.

C'est une des idées les plus importantes dans la programmation de scripts Cave. Le script ne flotte pas seul dans la scène. Il appartient à une entité.

Voici un exemple simple :

import cave

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

    def update(self):
        pass

Dans ce script :

  • self.entity est l'entité porte.
  • self.entity.getTransform() récupère le composant Transform de la porte.
  • self.isOpen est une variable utilisée par le script pour savoir si la porte est ouverte.

Vous utiliserez ce modèle tout le temps. D'abord vous récupérez l'entité, puis vous obtenez les composants ou entités enfants dont vous avez besoin, puis vous les utilisez dans votre logique.

Obtenir d'autres composants

Pour contrôler une entité, vous devez souvent obtenir un ou plusieurs composants de celle-ci.

Par exemple, un script joueur peut récupérer :

  • Le composant Transform pour déplacer ou faire pivoter l'entité.
  • Le composant Character pour gérer les mouvements du personnage.
  • Le composant Animation d'une entité enfant maillage pour jouer les animations.
  • Le composant Audio pour jouer un son en boucle.

Voici un petit exemple :

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)

Cela déplace l'entité vers l'avant à chaque image.

L'important est cave.getDeltaTime(). Comme update() s'exécute chaque image, multiplier le mouvement par delta time garde la vitesse constante même si le framerate change.

Lire les propriétés personnalisées

Coder en dur des valeurs est correct pour un premier test, mais les propriétés éditables sont généralement préférables pour de vrais objets de jeu.

Par exemple, au lieu d'écrire ceci :

self.speed = 2.0

Vous pouvez lire la valeur depuis les propriétés de l'entité :

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

Cela signifie :

  • Si l'entité a une propriété speed, utilisez-la.
  • Sinon, utilisez 2.0 comme valeur par défaut.

C'est très utile pour les scripts réutilisables. Vous pouvez créer un seul script SimpleMover et l'utiliser sur plusieurs entités avec des vitesses différentes.

Par exemple :

Entité Propriété speed
Plateforme lente 1.0
Plateforme rapide 4.0
Danger mobile 7.0

Le script reste le même, mais le comportement change selon l'entité.

Alternativement, vous pouvez créer des variables modifiables localement pour le Component lui-même au lieu de dépendre des propriétés de l'Entity. Lorsque vous créez une variable ainsi :

import cave

class PlatformMover(cave.Component):
    # Ceci sera local :
    speed = 2.0

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

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

La variable speed sera modifiable localement pour chaque instance du composant :

image.png


Obtenir des entités enfants

Beaucoup d'objets Cave sont construits comme de petites hiérarchies.

Le template joueur est un bon exemple. L'entité racine Player a la physique et la logique du personnage, tandis que l'entité enfant Mesh a le maillage visuel du personnage et le composant Animation.

Donc, si un script sur l'entité racine du joueur veut jouer des animations, il récupère d'abord l'entité enfant 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)

C'est le même type de modèle utilisé par le contrôleur joueur par défaut :

  1. Récupérer l'entité enfant.
  2. Récupérer le composant depuis cet enfant.
  3. Utiliser le composant quand c'est nécessaire.

Une fois que vous comprenez ce modèle, de nombreux scripts Cave deviennent beaucoup plus faciles à lire.

La méthode getChild a un paramètre optionnel "recursive", par défaut à True. S'il est True, elle interrogera tous les enfants de l'Entity, y compris les enfants des enfants, jusqu'à trouver l'Entity demandée par nom.

Obtenir des entités de la scène (Requêtes de scène)

Quand vous créez un jeu, il est très probable que vous ayez besoin d'obtenir d'autres entités dans la scène. Explorons cela. La première étape est d'obtenir la scène elle-même, et dans Cave, vous avez deux façons de le faire :

# Retourne la scène active :
scene = cave.getScene()

# Retourne la scène à laquelle appartient l'entité :
scene = self.entity.getScene()

Pour plus de commodité, les méthodes start et end du cave.Component reçoivent aussi la scène en paramètre, car il est très probable que vous en ayez besoin dans ces méthodes. Pour un code local comme le Python Code Component, une variable scene est également définie par défaut et vous pouvez simplement "l'utiliser" sans autre formalité.

Une fois que vous avez la scène, vous pouvez récupérer une Entity spécifique par son nom avec le code suivant :

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

Cave fournit de nombreuses autres méthodes dans la classe scene pour effectuer d'autres requêtes sur la scène. Vous pouvez faire des ray casts, sphere casts, vérifier une boîte ou sphère de contact pour des requêtes de collision, ou même obtenir toutes les entités, toutes les entités racines, toutes les entités avec une étiquette spécifique, toutes les entités avec une propriété donnée, ou avec un nom spécifique, etc. Il vaut donc la peine de consulter l'API Python pour plus de détails.

N'oubliez jamais de vérifier si votre requête a retourné quelque chose de valide. Par exemple :

# Requête sur une Entity inexistante :
ent = scene.get("This Entity Doesnt Exist")

if ent is None:
    print("Entity invalide !")

Obtenir des Components d'une Entity

Une fois que vous avez une entity spécifique et que vous avez vérifié qu'elle existe, il est bon de comprendre comment récupérer des components spécifiques.

L'entity dispose d'une méthode spécifique appelée get à laquelle vous pouvez passer le nom du component sous forme de chaîne, et elle recherchera automatiquement si un component correspondant existe dans l'entité :

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

Pour vous faciliter la vie, si le nom du component se termine par le mot "Component", ce qui est très courant, vous pouvez omettre complètement cette terminaison lors de la saisie du nom dans cette méthode. Vous pouvez aussi choisir d'inclure des espaces dans les noms composés ou les écrire collés.

Par exemple, si vous voulez récupérer le Rigid Body Component d'une entity, même si son nom Python est RigidBodyComponent, toutes les options ci-dessous fonctionneront :

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")

Comme Python n'est pas fortement typé, il est recommandé dans Cave de fournir un hint de type pour les components, ce qui peut se faire de la façon suivante. Notez aussi que dans ce cas, il faut inclure le nom complet du component, car il s'agit d'une sémantique Python :

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

Cela permet à IntelliSense de fonctionner dans des éditeurs externes comme Visual Studio Code ou même l'autocomplétion intégrée que Cave Engine offre dans son éditeur de scripts intégré.

Pour le Transform Component, puisqu'il est l'un des types de components les plus courants et que vous le récupérerez souvent, l'Entity dispose d'une méthode native pour obtenir son Transform principal :

transf = self.entity.getTransform()

L'appeler avec self.entity.getTransform() donne le même résultat que self.entity.get("Transform"), mais la première option est plus rapide et plus optimisée.

Si une entity possède plusieurs components du même type, elle retournera la première correspondance, mais parfois vous voudrez toutes les correspondances. Par exemple, pour un mesh multi-matériaux qui dans Cave sera représenté par une entity avec plusieurs components mesh, vous pouvez vouloir récupérer tous les components mesh. Pour cela, vous pouvez utiliser la méthode getAll :

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

# Changer tous les matériaux pour un matériau lumineux :
for meshCmp in meshCmps:
    meshCmp.material.setAsset("Glowing Material")

Obtenir des Components Python d'une Entity

Maintenant que nous savons comment obtenir les Components natives de Cave, que faire si vous souhaitez récupérer d'une entity un component créé sur-mesure par vous en Python ?

Pour cela, il faut utiliser une méthode spéciale appelée getPy, qui fonctionne de la même manière que la méthode classique get, sauf qu'elle retourne aussi les components Python :

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

Cette méthode spéciale est nécessaire en raison d'optimisations internes pour assurer que Cave fonctionne aussi rapidement que possible.

Une fois que vous avez votre Component Python personnalisé, vous pouvez accéder librement à ses variables et méthodes Python :

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

# Modifier les variables Python :
myCmp.customValue = 10

# Appeler des méthodes personnalisées :
myCmp.doSomething()
myCmp.applyDamage(10)

Python Code Component

En plus du Python Component classique, Cave propose également un Python Code Component. Le Python Code Component est utile pour des scripts rapides écrits directement sur l'entité, sans créer un asset Python Script séparé.

Il possède toutes les autres méthodes, et la différence est que pour ce component, vous écrivez le script Python directement dans le component, ce qui n'est pas modulaire ni réutilisable. Ainsi, si vous créez une copie de l'entité, une copie du script sera aussi faite. Mais parfois, c'est un moyen plus rapide de créer une logique simple, comme une pièce qui tourne.

Il est adapté pour :

  • Tests rapides.
  • Petits callbacks.
  • Comportements ponctuels.
  • Prototypes.

Ce n'est pas la meilleure option pour de gros systèmes de gameplay car le code est plus difficile à réutiliser et organiser, mais c'est optimal pour du code très court.

Python Component vs Python Code Component

Utilisez cette règle simple :

Utilisez Ceci Quand
Python Component Le comportement doit être réutilisé, modifié en tant qu'asset, ou évoluer dans le temps.
Python Code Component Le comportement est petit, local.

Par exemple, une IA EnemyAI réutilisable devrait être un asset Python Script utilisé par un Python Component. Un petit code qui affiche quelque chose ou appelle une fonction lorsqu'un bouton est pressé peut être un Python Code Component.

Un bon premier script

Un bon premier script Cave est quelque chose de petit et visible.

Essayez d'en créer un de ceux-ci :

  • Une plateforme qui avance.
  • Une porte qui s'ouvre au démarrage de la scène.
  • Une lumière qui s'allume et s'éteint toutes les quelques secondes.
  • Un objet à ramasser qui joue un son et se désactive.
  • Un bouton qui change de scène.

Ces exemples sont simples, mais enseignent le workflow le plus important : obtenir l'entité, obtenir le component, modifier quelque chose, tester en mode jeu, et ajuster.

À retenir

Les scripts Python dans Cave sont généralement attachés aux entités via des Python Components.

Le pattern débutant clé est :

  1. Utiliser start() pour obtenir des références et préparer des valeurs.
  2. Utiliser update() pour exécuter le comportement à chaque frame.
  3. Utiliser self.entity pour accéder à l'entité qui possède le script.
  4. Utiliser des components pour déplacer, animer, jouer des sons ou contrôler l'objet.

Une fois ce pattern devenu naturel, le scripting dans Cave est beaucoup moins mystérieux. Vous n'écrivez pas simplement du code, vous apprenez aux entités à se comporter.