Cave: Einstiegshandbuch
Einführung in das Python-Scripting
Lesson 15 of 19 • 50 XP
Keep your place in this quest
Log in or sign up for free to subscribe, follow lesson progress, and access more learning content.
Jetzt, da du gesehen hast, wie Charaktere und Animationen in Cave funktionieren, lass uns einen Schritt zurückmachen und das Skripting-System verstehen, das normalerweise alles miteinander verbindet: Python.
Python wird in Cave verwendet, um das Gameplay-Verhalten, Editor-Tools, UI-Callbacks, Animations-Callbacks, Timeline-Events und kleine Teile von benutzerdefinierter Logik zu schreiben, die zu spezifisch wären, um als integrierte Komponente zu existieren. Du musst kein fortgeschrittener Python-Programmierer werden, bevor du Cave verwendest, aber es ist sehr hilfreich, die grundlegende Struktur eines Cave-Skripts zu verstehen.
In dieser Lektion wirst du lernen:
- Wofür Python in Cave verwendet wird.
- Wie Python-Skript-Assets und Python-Komponenten zusammenarbeiten.
- Was
start()undupdate()tun. - Wie man auf die aktuelle Entität von einem Skript aus zugreift.
- Wie man Komponenten von einer Entität erhält.
- Wann man eine Python-Komponente oder eine Python-Code-Komponente verwenden sollte.
Das Ziel ist nicht, die gesamte Python-Sprache zu lehren. Das Ziel ist es, dass Cave-Skripte verständlich erscheinen, wenn du sie öffnest.
Wo kann man Python lernen?
Wenn du nicht weißt, wie man in Python programmiert, bietet Uniday Studio tatsächlich auch kostenlose Lernquests zu Python an. Gehe zu uniday.studio/learn, um alle Optionen zu sehen oder beginne hier:
- Python Grundlagen: Deine ersten Schritte in die Programmierung
- Python Objektorientierte Programmierung (OOP)
Für diesen Abschnitt nehmen wir an, dass du bereits ein Verständnis für die Themen hast, die in diesen beiden Lernquests behandelt werden.
Wofür wird Python in Cave verwendet?
In Cave ist Python die Skriptschicht, die du verwendest, wenn ein Spielobjekt Verhalten benötigt.
Zum Beispiel kann Python verwendet werden, um:
- Einen Spieler oder Feind zu bewegen.
- Eine Tür zu öffnen.
- Eine Animation abzuspielen.
- Einen Sound auszulösen.
- Eine Timeline zu starten.
- Szenen zu wechseln.
- Ein UI-Element zu aktualisieren.
- Benutzerdefinierte Editor-Tools zu erstellen.
Im Grunde kannst du Python-Skripte schreiben, um alle Logik deines Spiels zu erstellen.
Deshalb enthält das Startprojekt bereits Python-Skripte. Der Standardspieler ist beispielsweise nicht nur ein Mesh mit einer Animation. Er hat auch Gameplay-Logik, die Eingaben liest, den Charakter bewegt, das Mesh rotiert und die richtige Animation abspielt.
Wenn du also Python in Cave schreibst, schreibst du normalerweise keinen isolierten Code. Du schreibst Logik, die eine Entität steuert und mit den an dieser Entität angefügten Komponenten kommuniziert.
Skript-Assets und Python-Komponenten
Python-Code lebt normalerweise in einem Python-Skript-Asset.

Um dieses Skript in einer Szene auszuführen, fügst du eine Python-Komponente zu einer Entität hinzu und wählst aus, welche Klasse aus dem Skript ausgeführt werden soll.
Denk daran, es so zu betrachten:
| Teil | Was es macht |
|---|---|
| Python-Skript-Asset | Speichert den Code. |
| Python-Komponente | Führt eine Klasse aus diesem Skript an einer Entität aus. |
| Entität | Das Objekt, das vom Skript gesteuert wird. |
cave.Component-Klasse |
Das tatsächliche Verhalten, das du geschrieben hast. |
Zum Beispiel kannst du ein Skript-Asset mit dem Namen Door Controller haben. Darin kannst du eine Klasse mit dem Namen DoorController haben, die von cave.Component erbt. Dann fügst du einer Tür-Entität eine Python-Komponente hinzu und wählst diese Klasse aus.
Diese Trennung ist wichtig, weil das gleiche Skript wiederverwendet werden kann. Du kannst viele Türen in der Szene platzieren, von denen jede dasselbe Türsteuerungs-Skript verwendet, jedoch mit unterschiedlichen Eigenschaften oder unterschiedlichen Kindobjekten.
Eine minimale Cave-Komponente
Eine grundlegende Cave-Python-Komponente sieht so aus:
import cave
class MyComponent(cave.Component):
def start(self, scene):
print("Die Komponente wurde gestartet!")
def update(self):
pass
Hier sind einige wichtige Details:
import cavegibt deinem Skript Zugriff auf Caves Python-API.class MyComponent(cave.Component)erstellt eine Komponentenklasse, die Cave ausführen kann.start(self, scene)wird aufgerufen, wenn die Komponente startet.update(self)wird jeden Frame ausgeführt, während die Komponente aktiv ist.
Der Name MyComponent kann beliebig sein, aber in realen Projekten solltest du einen klaren Namen wie DoorController, EnemyAI, Checkpoint oder PlayerHealth verwenden.
Lebenszyklusmethoden
Cave ruft einige Methoden automatisch auf, wenn deine Komponente läuft.
Die häufigsten sind:
| Methode | Wann sie läuft | Häufige Verwendung |
|---|---|---|
start(self, scene) |
Wenn die Komponente startet. | Referenzen abrufen, Eigenschaften lesen, Variablen vorbereiten. |
firstUpdate(self) |
Immer nach der Startmethode von jeder Entity-Komponente, im ersten Update. | Variablen erstellen, die von anderen Komponenten abhängen, um initialisiert worden zu sein. |
update(self) |
Jeden Frame, wenn die Szene nicht pausiert ist. | Bewegung, Eingaben, Timer, Statusprüfungen. |
pausedUpdate(self) |
Jeden Frame, wenn die Szene pausiert ist. | Pausierte Logik. |
end(self, scene) |
Wenn die Komponente endet. | Aufräumen, falls nötig. |
Die meisten Anfängerskripte verwenden start() und update(). Zum Beispiel, wenn du eine bewegliche Plattform erstellst, ist start() ein guter Platz, um die Originalposition zu speichern, und update() ist der Ort, an dem du die Plattform jeden Frame bewegst.
Wir haben auch die Methoden editorUpdate und lateUpdate, aber wir werden sie hier nicht genauer untersuchen, da sie etwas fortgeschrittener sind.
Zugriff auf die aktuelle Entität
Innerhalb einer Cave-Komponente ist self.entity die Entität, die die Python-Komponente besitzt.
Dies ist eine der wichtigsten Ideen im Cave-Scripting. Das Skript schwebt nicht selbstständig in der Szene. Es gehört zu einer Entität.
Hier ist ein einfaches Beispiel:
import cave
class DoorController(cave.Component):
def start(self, scene):
self.transform = self.entity.getTransform()
self.isOpen = False
def update(self):
pass
In diesem Skript:
self.entityist die Tür-Entität.self.entity.getTransform()erhält die Transform-Komponente der Tür.self.isOpenist eine Variable, die vom Skript verwendet wird, um zu merken, ob die Tür offen ist.
Du wirst dieses Muster ständig verwenden. Zuerst erhältst du die Entität, dann die Komponenten oder Kind-Entitäten, die du benötigst, und dann verwendest du sie in deiner Logik.
Andere Komponenten abrufen
Um eine Entität zu steuern, musst du normalerweise eine oder mehrere Komponenten von ihr abrufen.
Zum Beispiel kann ein Spielerskript:
- Die
Transform-Komponente abrufen, um die Entität zu bewegen oder zu drehen. - Die
Character-Komponente abrufen, um die Charakterbewegung zu steuern. - Die
Animation-Komponente von einer Kind-Mesh-Entität abrufen, um Animationen abzuspielen. - Die
Audio-Komponente abrufen, um einen sich wiederholenden Sound abzuspielen.
Hier ist ein kleines Beispiel:
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)
Das bewegt die Entität jeden Frame vorwärts.
Der wichtige Teil ist cave.getDeltaTime(). Da update() jeden Frame ausgeführt wird, sorgt die Multiplikation der Bewegung mit der Delta-Zeit dafür, dass die Bewegungsgeschwindigkeit konstant bleibt, selbst wenn die Bildrate sich ändert.
Benutzerdefinierte Eigenschaften lesen
Hardcodierte Werte sind für einen ersten Test in Ordnung, aber bearbeitbare Eigenschaften sind normalerweise besser für echte Spielobjekte.
Anstatt dies zu schreiben:
self.speed = 2.0
kannst du den Wert aus den Entitätseigenschaften lesen:
self.speed = self.entity.properties.get("speed", 2.0)
Das bedeutet:
- Wenn die Entität eine
speed-Eigenschaft hat, verwende sie. - Wenn nicht, verwende
2.0als Standardwert.
Das ist sehr nützlich für wiederverwendbare Skripte. Du kannst ein SimpleMover-Skript erstellen und es auf mehreren Entitäten verwenden, jede mit einer anderen Geschwindigkeit.
Zum Beispiel:
| Entität | speed-Eigenschaft |
|---|---|
| Langsame Plattform | 1.0 |
| Schnelle Plattform | 4.0 |
| Bewegliche Gefahr | 7.0 |
Das Skript bleibt gleich, aber das Verhalten ändert sich pro Entität.
Alternativ kannst du lokal modifizierbare Variablen für die Komponente selbst erstellen, anstatt auf die Eigenschaften der Entität angewiesen zu sein. Wenn du eine Variable wie folgt erstellst:
import cave
class PlatformMover(cave.Component):
# Dies wird lokal sein:
speed = 2.0
def start(self, scene: cave.Scene):
pass
def update(self):
events = cave.getEvents()
Wird die speed-Variable für jede Komponenteninstanz lokal modifizierbar sein:

Kind-Entitäten abrufen
Viele Cave-Objekte sind als kleine Hierarchien aufgebaut.
Die Spieler-Vorlage ist ein gutes Beispiel. Die Wurzel-Player-Entität hat die Charakterphysik und Logik, während die Kind-Mesh-Entität das visuelle Charakter-Mesh und die Animationskomponente hat.
Wenn ein Skript auf der Wurzel-Spieler-Entität Animationen abspielen möchte, erhält es zuerst die Kind-Mesh-Entität:
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)
Das ist das gleiche Muster, das vom Standard-Spieler-Controller verwendet wird:
- Kind-Entität abrufen.
- Komponente von dieser Kind-Entität abrufen.
- Die Komponente nach Bedarf verwenden.
Sobald du dieses Muster verstehst, werden viele Cave-Skripte viel einfacher zu lesen.
Die Methode getChild hat einen optionalen "rekursiven" Parameter, der standardmäßig auf True gesetzt ist. Wenn True, wird sie alle Kind-Entitäten abfragen, einschließlich der Kinder der Kinder, bis sie die Entität findet, die du angefordert hast.
Abruf von Szenen-Entitäten (Szenenabfragen)
Beim Erstellen eines Spiels ist es sehr wahrscheinlich, dass du andere Entitäten in der Szene abrufen musst. Lass uns das erkunden. Der erste Schritt besteht darin, die Szene selbst zu erhalten, und in Cave hast du zwei Möglichkeiten, dies zu tun:
# Gibt die aktive Szene zurück:
scene = cave.getScene()
# Gibt die Szene zurück, zu der die Entität gehört:
scene = self.entity.getScene()
Zur Vereinfachung erhalten die start- und end-Methoden des cave.Component ebenfalls die Szene als Parameter, da es sehr wahrscheinlich ist, dass Sie sie in diesen Methoden verwenden werden. Für lokalen Code wie die Python Code Komponente gibt es auch standardmäßig eine scene-Variable, die Sie "einfach verwenden können" und erwarten können, dass sie funktioniert.
Sobald Sie die Szene erhalten haben, können Sie eine bestimmte Entität anhand ihres Namens mit dem folgenden Code abrufen:
watchtower = scene.get("Watch Tower 01")
Cave bietet viele andere Methoden in der Szenenklasse an, um andere Szenenabfragen durchzuführen. Sie können Strahlencast, Kugelcast durchführen, eine Kontaktbox oder -kugel für grundlegende Kollisionsabfragen überprüfen oder sogar alle Entitäten, alle Wurzelentitäten, alle Entitäten mit einem bestimmten Tag, alle Entitäten mit bestimmten Eigenschaften oder mit einem bestimmten Namen usw. abrufen. Es lohnt sich also, die Python API auf weitere Details zu überprüfen.
Denken Sie immer daran, zu überprüfen, ob Ihre Abfrage etwas Gültiges zurückgegeben hat. Zum Beispiel:
# Abfrage einer nicht existierenden Entität:
ent = scene.get("Diese Entität existiert nicht")
if ent is None:
print("Ungültige Entität!")
Abrufen von Entitätenkomponenten
Sobald Sie eine bestimmte Entität haben und überprüfen, ob sie bekannt ist, ist es eine gute Idee zu verstehen, wie Sie spezifische Komponenten davon abrufen können.
Die Entität hat eine spezifische Methode namens get, bei der Sie den Namen der Komponente als String übergeben können, und es wird automatisch überprüft, ob eine Komponente vorhanden ist, die Ihrer Suche entspricht:
animator = self.entity.get("Animation Komponente")
Um es Ihnen einfacher zu machen, wenn ein Komponentenname mit dem Wort "Komponente" endet, was sehr häufig ist, können Sie dieses Ende beim Eingeben des Komponentennamens in dieser Methode vollständig weglassen. Sie können auch entscheiden, ob Sie Leerzeichen in mehrteiligen Komponenten-Namen einfügen möchten oder alles zusammenhalten möchten.
Wenn Sie beispielsweise die Rigid Body Komponente von einer Entität abrufen möchten, auch wenn ihr Python-Name RigidBodyComponent ist, funktionieren alle Optionen unten:
rb = self.entity.get("Rigid Body")
rb = self.entity.get("RigidBody")
rb = self.entity.get("Rigid Body Komponente")
rb = self.entity.get("RigidBodyComponent")
rb = self.entity.get("RigidBody Komponente")
Da Python nicht stark typisiert ist, ist es eine gute Programmierpraxis in Cave, dass Sie einen Typ-Hinweis für den Komponententyp bereitstellen, was auf die folgende Weise erledigt werden kann. Achten Sie auch darauf, dass Sie in diesem Fall den vollständigen Komponenten-Namen angeben müssen, da es sich um eine Python-Semantik handelt:
rb : cave.RigidBodyComponent = self.entity.get("Rigid Body")
Dies ermöglicht, dass IntelliSense in externen Editoren wie Visual Studio Code oder sogar die integrierte Autovervollständigung, die Cave Engine Ihnen im eingebetteten Skripteditor bietet, funktioniert.
Für die Transform Komponente, da es sich um eine der häufigsten Komponentenarten handelt und Sie sie oft abfragen werden, hat die Entität eine native Methode, um ihren Haupt-Transform abzurufen:
transf = self.entity.getTransform()
Das Abrufen durch den Aufruf self.entity.getTransform() liefert dasselbe Ergebnis wie der Aufruf self.entity.get("Transform"), aber die erste Option ist schneller und optimierter.
Wenn eine Entität mehrere Komponenten desselben Typs hat, wird die erste Übereinstimmung zurückgegeben. Manchmal möchten Sie jedoch alle Übereinstimmungen abrufen. Wenn Sie beispielsweise in einem Mehrmaterial-Mesh, das in Cave durch eine Entität mit mehreren Mesh-Komponenten dargestellt wird, alle Mesh-Komponenten abrufen möchten. Und dafür können Sie die Methode getAll verwenden:
meshCmps = self.entity.getAll("Mesh")
# Ändern aller Materialien in ein leuchtendes:
for meshCmp in meshCmps:
meshCmp.material.setAsset("Glowing Material")
Abrufen von Python-Komponenten der Entität
Jetzt wissen wir, wie man native Cave-Komponenten erhält, aber was ist, wenn Sie von einer Entität eine Komponente abrufen möchten, die Sie mit Python selbst geschrieben haben?
Dafür benötigen Sie eine spezielle Methode namens getPy, und sie funktioniert genau wie die reguläre get-Methode, mit dem Unterschied, dass diese auch Python-Komponenten zurückgibt:
myCmp = self.entity.getPy("MyCustomComponent")
Es erfordert eine spezielle Methode aufgrund interner Optimierungen, um sicherzustellen, dass Cave so schnell wie möglich läuft.
Einmal haben Sie Ihre benutzerdefinierte Python-Komponente, können Sie frei auf ihre in Python erstellten Variablen und Methoden zugreifen:
myCmp = self.entity.getPy("MyCustomComponent")
# Python-Variablen ändern:
myCmp.customValue = 10
# Benutzerdefinierte Methoden aufrufen:
myCmp.doSomething()
myCmp.applyDamage(10)
Python Code Komponente
Neben der regulären Python-Komponente hat Cave auch eine Python Code Komponente. Die Python Code Komponente ist nützlich für schnelle Skripte, die direkt an der Entität geschrieben werden, ohne zuerst eine separate Python-Skript-Asset zu erstellen.
Sie hat alle anderen Methoden, und der Unterschied ist, dass Sie in der Python-Code-Komponente das Python-Skript direkt innerhalb der Komponente selbst schreiben, und es ist nicht modular und nicht wiederverwendbar. Wenn Sie also eine Kopie der Entität erstellen, wird auch eine Kopie des Skripts darin erstellt. Aber manchmal kann dies ein schnellerer Weg sein, um einige einfache Logik zu erstellen, wie eine sich drehende Münze.
Es ist gut für:
- Schnelle Tests.
- Kleine Rückrufe.
- Einmalige Verhaltensweisen.
- Prototypen.
Es ist nicht die beste Option für größere Gameplay-Systeme, da der Code schwieriger wiederverwendbar und zu organisieren ist, aber optimal für kleine Codes.
Python-Komponente vs. Python-Codekomponente
Verwenden Sie dies als einfache Regel:
| Verwenden Sie Dies | Wann |
|---|---|
| Python-Komponente | Das Verhalten sollte wiederverwendet, als Asset bearbeitet oder im Laufe der Zeit wachsen. |
| Python-Code-Komponente | Das Verhalten ist klein, lokal. |
Zum Beispiel sollte eine wiederverwendbare EnemyAI ein Python-Skript-Asset sein, das von einer Python-Komponente verwendet wird. Ein kleiner Code, der etwas ausgibt oder eine Funktion aufruft, wenn eine Taste gedrückt wird, kann eine Python-Code-Komponente sein.
Ein gutes erstes Skriptziel
Ein gutes erstes Cave-Skript ist etwas Klein und Sichtbares.
Versuchen Sie, eines dieser Projekte zu erstellen:
- Eine Plattform, die sich nach vorne bewegt.
- Eine Tür, die sich öffnet, wenn die Szene beginnt.
- Ein Licht, das alle paar Sekunden ein- und ausgeschaltet wird.
- Ein Pickup, das einen Sound abspielt und sich selbst deaktiviert.
- Ein Button, der zu einer anderen Szene wechselt.
Diese Beispiele sind klein, lehren jedoch den wichtigsten Workflow: Holen Sie sich die Entität, holen Sie sich die Komponente, ändern Sie etwas, testen Sie im Spielmodus und passen Sie an.
Was Sie sich merken sollten
Python-Skripte in Cave sind normalerweise über Python-Komponenten an Entitäten angehängt.
Das wichtigste Muster für Anfänger ist:
- Verwenden Sie
start(), um Referenzen zu erhalten und Werte vorzubereiten. - Verwenden Sie
update(), um Verhalten in jedem Frame auszuführen. - Verwenden Sie
self.entity, um auf die Entität zuzugreifen, die das Skript besitzt. - Verwenden Sie Komponenten, um tatsächlich zu bewegen, zu animieren, Geräusche abzuspielen oder das Objekt zu steuern.
Sobald dieses Muster natürlich erscheint, wird das Skripting in Cave viel weniger mysteriös. Sie schreiben nicht nur Code. Sie lehren Entitäten, wie sie sich verhalten sollen.