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 les personnages et l'animation fonctionnent dans Cave, prenons du recul et comprenons le système de script qui relie généralement tout ensemble : Python.

Python est utilisé dans Cave pour écrire le comportement de gameplay, les outils de l'éditeur, les rappels d'UI, les rappels d'animation, les événements de timeline et de petits bouts de logique personnalisée qui seraient 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 forme de base d'un script Cave.

Dans cette leçon, vous apprendrez :

  • À quoi sert Python dans Cave.
  • Comment les actifs de script Python et les composants Python fonctionnent ensemble.
  • Ce que font start() et update().
  • Comment accéder à l'entité actuelle à partir d'un script.
  • Comment obtenir des composants à partir d'une entité.
  • Quand utiliser un composant Python ou un composant de code Python.

L'objectif n'est pas d'enseigner l'ensemble du 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 également des quêtes d'apprentissage gratuites sur Python. Allez sur uniday.studio/learn pour voir toutes les options ou commencez ici :

Pour cette section, nous supposerons que vous avez déjà une compréhension des sujets abordés dans ces deux quêtes d'apprentissage.


À Quoi Sert Python Dans Cave

Dans Cave, Python est la couche de script que vous utilisez lorsqu'un objet de jeu a besoin de 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ènes.
  • Mettre à jour un élément UI.
  • Créer des outils d'éditeur personnalisés.

En gros, vous pouvez écrire des scripts Python 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 également une logique de gameplay qui lit les entrées, déplace le personnage, fait tourner le maillage et joue l'animation correcte.

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

Actifs de Script et Composants Python

Le code Python vit généralement à l'intérieur d'un actif de script Python.

image.png

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

Pensez-y de cette manière :

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

Par exemple, vous pouvez avoir un actif de script appelé Contrôleur de Porte. À l'intérieur, vous pouvez avoir une classe appelée DoorController, qui hérite de cave.Component. Ensuite, vous ajoutez un composant Python à votre entité porte et sélectionnez cette classe.

Cette séparation est importante car le même script peut être réutilisé. Vous pouvez placer de nombreuses 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 composant Python de base dans Cave ressemble à ceci :

import cave

class MyComponent(cave.Component):
    def start(self, scene):
        print("Le composant a démarré !")

    def update(self):
        pass

Il y a 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 lorsque le composant démarre.
  • update(self) s'exécute chaque image alors que le composant est actif.

Le nom MyComponent peut être n'importe quoi, mais dans de vrais projets, vous devriez utiliser un nom clair comme DoorController, EnemyAI, Checkpoint, ou PlayerHealth.

Méthodes de Cycle de Vie

Cave appelle certaines méthodes automatiquement lorsque votre composant est en cours d'exécution.

Les plus courantes sont :

Méthode Quand Elle S'Exécute Utilisation Courante
start(self, scene) Lorsque le composant démarre. Obtenir des références, lire des propriétés, préparer des variables.
firstUpdate(self) Toujours après chaque méthode de démarrage de Composant d'Entité, lors de la première mise à jour. Créer des variables qui dépendent de l'initialisation d'autres composants.
update(self) Chaque image, si la scène n'est pas en pause. Mouvement, entrée, temporisateurs, vérifications d'état.
pausedUpdate(self) Chaque image, si la scène EST en pause. Logique en pause.
end(self, scene) Lorsque le composant se termine. Nettoyer si nécessaire.

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

Nous avons également 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é Actuelle

À l'intérieur d'un composant Cave, self.entity est l'entité qui possède le composant Python.

C'est l'une des idées les plus importantes de la syntaxe de script de Cave. Le script ne flotte pas autour de la scène par lui-même. 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é de la porte.
  • self.entity.getTransform() obtient le composant Transform de la porte.
  • self.isOpen est une variable utilisée par le script pour se souvenir de l'état ouvert de la porte.

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

Obtenir D'Autres Composants

Pour contrôler une entité, vous devez généralement obtenir un ou plusieurs composants à partir de celle-ci.

Par exemple, un script de joueur peut obtenir :

  • Le composant Transform pour déplacer ou faire tourner l'entité.
  • Le composant Character pour gérer le mouvement du personnage.
  • Le composant Animation d'une entité maillée enfant pour jouer des 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.

La partie importante est cave.getDeltaTime(). Comme update() s'exécute à chaque image, multiplier le mouvement par le temps delta maintient la vitesse de mouvement constante même si le taux de rafraîchissement change.

Lire des Propriétés Personnalisées

Écrire des valeurs en dur est acceptable pour un premier test, mais les propriétés modifiables sont généralement meilleures pour les vrais objets de jeu.

Par exemple, au lieu d'écrire ceci :

self.speed = 2.0

Vous pouvez lire la valeur à partir des 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.
  • Si elle n'en a pas, utilisez 2.0 comme valeur par défaut.

C'est très utile pour les scripts réutilisables. Vous pouvez créer un script SimpleMover et l'utiliser sur plusieurs entités, chacune avec une vitesse différente.

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 par entité.

Alternativement, vous pouvez créer des variables modifiables localement pour le composant lui-même au lieu de compter sur les propriétés de l'entité. Lorsque vous créez une variable comme ceci :

import cave

class PlatformMover(cave.Component):
    # Cela 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 de composant :

image.png


Obtenir des Entités Enfants

De nombreux objets Cave sont construits comme de petites hiérarchies.

Le modèle de 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 d'animation.

Donc, si un script sur l'entité racine du joueur veut jouer des animations, il doit d'abord obtenir l'entité maillée enfant :

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 schéma utilisé par le contrôleur de joueur par défaut :

  1. Obtenez l'entité enfant.
  2. Obtenez le composant de cet enfant.
  3. Utilisez le composant lorsque c'est nécessaire.

Une fois que vous comprenez ce schéma, de nombreux scripts Cave deviennent beaucoup plus faciles à lire.

La méthode getChild a un paramètre optionnel "récursif", par défaut True. Si True, elle interrogera tous les enfants de l'entité, y compris les enfants des enfants, jusqu'à ce qu'elle trouve l'entité que vous avez demandée par nom.

Obtenir des Entités de Scène (Requêtes de Scène)

Lors de la création d'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 consiste à obtenir la scène elle-même, et dans Cave, vous avez deux façons de le faire :

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

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

Pour votre commodité, les méthodes start et end de cave.Component reçoivent également la scène en tant que paramètre, car il est très probable que vous les utilisiez dans ces méthodes. Pour le code local tel que le Composant de Code Python, il y a aussi une variable scene définie par défaut que vous pouvez magiquement "utiliser simplement" et vous attendre à ce qu'elle fonctionne.

Une fois que vous avez obtenu la scène, vous pouvez obtenir une Entité spécifique par son nom en utilisant le code suivant :

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

Cave fournit beaucoup d'autres méthodes dans la classe scène pour vous permettre d'effectuer d'autres requêtes de scène. Vous pouvez effectuer des ray casts, des sphere casts, vérifier une boîte ou une 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 un tag spécifique, toutes les entités avec des propriétés spécifiques, 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 renvoyé quelque chose de valide. Par exemple :

# Requêter une Entité inexistante :
ent = scene.get("This Entity Doesnt Exist")

if ent is None:
    print("Entité invalide !")

Obtenir les Composants d'Entité

Une fois que vous avez une entité spécifique et que vous vérifiez qu'elle est connue, il est bon de comprendre comment vous pouvez obtenir des composants spécifiques à partir de celle-ci.

L'entité a une méthode spécifique appelée get à laquelle vous pouvez passer le nom du composant en tant que chaîne de caractères et qui portera automatiquement l'entité pour voir s'il y a un composant qui correspond à votre recherche :

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

Pour vous faciliter la vie, si un nom de composant se termine par le mot "Component", ce qui est très courant, vous pouvez complètement omettre cette fin lorsque vous tapez le nom du composant dans cette méthode. Vous pouvez également choisir si vous souhaitez inclure un espace dans les noms de composants à plusieurs mots ou tout regrouper.

Par exemple, si vous souhaitez obtenir le Rigid Body Component d'une Entité, 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")

Étant donné que Python n'est pas fortement typé, il est de bonne pratique de programmation dans Cave de fournir une indication de type pour le type de composant, ce qui peut être fait de la manière suivante. Notez également qu'à cet égard, vous devez inclure le nom complet du composant, car c'est une sémantique Python :

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

Cela permet à IntelliSense de fonctionner dans des éditeurs externes tels que Visual Studio Code ou même l'auto-complétion intégrée que Cave Engine vous fournit dans son éditeur de scripts intégré.

Pour le Composant de Transform, étant donné que c'est l'un des types de composants les plus courants et que vous allez le requêter souvent, l'Entité a une méthode native pour obtenir son Transform principal :

transf = self.entity.getTransform()

L'obtenir en appelant self.entity.getTransform() fournit le même résultat que de l'obtenir en appelant self.entity.get("Transform"), mais la première option est plus rapide et plus optimisée.

Si une entité a plusieurs composants de ce même type, elle renverra la première correspondance, mais parfois vous souhaiterez peut-être avoir toutes les correspondances. Par exemple, dans un maillage à matériaux multiples qui dans Cave sera représenté par une entité avec plusieurs composants de maillage, vous souhaiterez peut-être obtenir tous les composants de maillage. Et 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 les Composants Python d'Entité

Maintenant que nous savons comment obtenir des Composants natives de Cave, mais que faire si vous voulez obtenir d'une Entité un composant qui a été écrit sur mesure par vous à l'aide de Python ?

Pour cela, vous avez besoin d'une méthode spéciale appelée getPy, et elle fonctionne exactement de la même manière que la méthode get régulière, sauf qu'elle renverra également des composants Python :

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

Elle nécessite une méthode spéciale en raison des optimisations internes pour s'assurer que Cave fonctionne aussi vite que possible.

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

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

# Changer les variables python :
myCmp.customValue = 10

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

Composant de Code Python

Outre le Composant Python régulier, Cave dispose également d'un Composant de Code Python. Le Composant de Code Python est utile pour des scripts rapides écrits directement sur l'entité, sans créer d'abord un actif de script Python séparé.

Il possède toutes les autres méthodes, et la différence est que dans le composant de code Python, vous écrivez le script Python directement à l'intérieur du composant lui-même, et il n'est pas modulaire et n'est pas réutilisable. Donc, si vous créez une copie de l'entité, cela créera également une copie du script dans celle-ci. Mais parfois, cela peut être un moyen plus rapide de créer une logique simple, comme une pièce qui tourne.

Il est bon pour :

  • Tests rapides.
  • Petits rappels.
  • Comportement unique.
  • Prototypes.

Ce n'est pas la meilleure option pour des systèmes de gameplay plus vastes car le code est plus difficile à réutiliser et à organiser, mais c'est optimal pour de petits codes.

Composant Python vs Composant de Code Python

Utilisez ceci comme une règle simple :

Utilisez Cela Quand
Composant Python Le comportement doit être réutilisé, édité en tant qu'actif, ou évoluer avec le temps.
Composant de Code Python Le comportement est petit, local.

Par exemple, un EnemyAI réutilisable devrait être un actif de script Python utilisé par un Composant Python. Un petit code qui imprime quelque chose ou appelle une fonction lorsque un bouton est pressé peut être un Composant de Code Python.

Un Bon Premier Objectif de Script

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

Essayez de construire l'un de ces éléments :

  • Une plateforme qui avance.
  • Une porte qui s'ouvre lorsque la scène commence.
  • 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 petits, mais ils enseignent le flux de travail le plus important : obtenir l'entité, obtenir le composant, changer quelque chose, tester en mode Play, et ajuster.

Ce que Vous Devez Retenir

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

Le modèle de base le plus important pour les débutants est :

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

Une fois que ce modèle semble naturel, la programmation dans Cave devient beaucoup moins mystérieuse. Vous n'écrivez pas seulement du code. Vous enseignez aux entités comment se comporter.