Im letzten Teil haben wir die Schwerthiebe des Skeletts verarbeitet und eine Game Over Szene gebaut. Nach viel allgemeiner Spieleentwicklung widmen wir uns nun endlich mal wieder einem XR-spezifischen Thema: Sockets.
Problemstellung
Erinnern wir uns kurz an die Geschichte aus dem ersten Teil. Da haben wir uns ausgedacht:
Der Spieler muss den Friedhof erkunden und drei Knochenteile finden, die in uralte Säulen eingesetzt werden, um das magische Tor zu öffnen.
Das Finden und Aufnehmen von Knochenteilen sollte kein Problem mehr sein, wir kennen ja jetzt die XRGrabInteractable Komponente. Wie aber setzen wir solche Objekte dann woanders ein? Genau darum geht es in diesem Teil:
- Erstellen von drei Knochenteilen als “Schlüssel”
- Erstellen von drei Gegenstücken, die jeweils genau ein Knochenteil aufnehmen können, als “Schloss”
- Logik, um ein Tor zu öffnen, sobald alle drei Schlüssel in ihr jeweiliges Schloss eingesetzt wurden
- Ein Tor, das auch geöffnet werden kann… denn bisher ist unser “crypt” Modell komplett statisch
Unterscheidung von Schlössern und Knochenschlüsseln
Bevor wir uns an die Implementierung begeben, müssen wir überlegen, wie wir eigentlich die Schlösser gestalten und wie wir dafür mit den vorhandenen Mitteln drei verschiedene und für den Spieler klar unterscheidbare Schlüssel erzeugen können.
In Teil 7 war mir bereits ein Modell aus Kay Lousberg’s Halloween Bits Pack aufgefallen, das wunderbar als “Schloss” geeignet ist: der Schrein.

Die Vertiefung in der Säule kann einen Knochen aufnehmen. Wir würden also auf der Karte drei dieser Schreine platzieren und der Spieler muss den jeweils passenden Knochen dort hineinlegen. (Dass die Säule die gleiche Vertiefung auch auf der anderen Seite hat, ignorieren wir hier mal und arbeiten nur auf einer Seite.)
Zufälligerweise haben wir auch drei verschiedene Knochen aus dem Halloween Bits Pack zur Verfügung. Allerdings scheinen nicht alle gleich gut geeignet: die Form von bone_A und bone_C ist sehr ähnlich.
Wir ersetzen den bone_A durch eine Variante von bone_C, nämlich zwei gekreuzte Knochen, wie bei einer Piratenflagge. So haben wir drei Varianten, die für den Spieler gut auseinanderzuhalten sind.
Nun müssen wir überlegen, wie wir eigentlich signalisieren können, welcher Knochen in welche Vertiefung passt. Ein Weg wäre, die jeweilige Form auf die Textur des Schreins zu zeichnen, aber damit würden wir weitere Tools ins Spiel bringen müssen. Zu kompliziert! Oder wir platzieren das Modell als Vorschau in die Vertiefung. Aber was passiert dann, wenn der Spieler seinen aufgesammelten Knochenschlüssel daran hält? Weicht die Vorschau dann? Auch kompliziert!
Vielleicht gibt es eine Möglichkeit, beide Varianten zu vereinen? Wie wäre es, wenn wir das Knochenmodell auf einer Achse so stark plätten, dass es wirkt, als sei es auf den Schrein aufgezeichnet? Und wenn wir dann noch unser neu angelegtes “Black” Material nutzen, sieht auch die Oberfläche ganz anders aus. Hier sind die drei möglichen Varianten des Schreins mit einer geplätteten Variante der Knochenschlüssel und Material “Black”:

Wir könnten noch weiter gehen und den Zeichen andere, oder gar eigene Materialien und Farben geben; sogar leuchtende, schließlich wissen wir jetzt, wie das mit dem Emissive Attribut funktioniert… Ich habe mich letztendlich für ein dunkles Rot mit schwacher Emission entschieden, damit der Spieler auch ohne Beleuchtung erkennt, was wohin gehört.
Hier ist eine Schritt für Schritt Anleitung für einen solchen Schrein mit Symbol am Beispiel des linken Schreins:
- Ziehe das “shrine” Model aus dem Ordner
Assets/040_Models/Halloween
in die Hierarchy oder direkt in den Scene View. Die Position ist vorerst irrelevant, Hauptsache du kannst es gut sehen und bearbeiten und im Spiel zum Testen erreichen. - Markiere das neue “shrine” GO als “Static”.
- Füge einen MeshCollider hinzu.
- Ziehe jetzt das “bone_C” Model aus dem gleichen Ordner auf den “shrine” in der Hierarchy, so dass “bone_C” in der Szene ein Child-GO von “shrine” wird. Erstmal ist noch nichts zu sehen, da es am Sockel des Schreins platziert wird.
- Verschiebe das “bone_C” GO jetzt über das Move Tool so, dass es in der Vertiefung zu sehen ist.
- Rotiere das “bone_C” GO jetzt so um die Z-Achse, dass es wie einer der beiden oben zu sehenden Knochen schräg in der Vertiefung steht.
- Ändere jetzt die Größe des “bone_C” GOs über den Inspektor, so dass es gut in die Vertiefung passt. Ich nutze (0.5, 0.5, 0.5).
- Ändere jetzt die Skalierung von “bone_C” auf der Z-Achse, so dass es ganz platt wird. Ich nutze 0.01 als Faktor, also ist die Skalierung nun (0.5, 0.5, 0.01).
- Korrigiere jetzt die Position des Knochens über das Move Tool und ggf. Rotate Tool, so dass er ganz eng an der Vertiefung anliegt, ohne im Schrein zu verschwinden.
- Ziehe das “Black” Material auf das “bone_C” GO oder weise das Material im Mesh Renderer Material-Slot zu.
- Dupliziere “bone_C” in der Hierarchy und rotiere das Duplikat so, dass es sich mit dem anderen GO kreuzt.
Neben den drei Schreinen benötigen wir die Knochen in der passenden Größe auch noch als Objekt, das der Spieler greifen kann. Dazu gehen wir jetzt einen Teil des Weges rückwärts: wir duplizieren jeweils die Knochen, ziehen die Duplikate aus den “shrine” GOs raus auf eine Ebene höher, entfalten die Knochen wieder, indem wir die geplättete Achse auf die Skalierung der anderen Achsen anpassen (aus (0.5, 0.5, 0.01) wird also (0.5, 0.5, 0.5)) und setzen das Material wieder zurück zu unserem “HalloweenBits” Material. Bei den gekreuzten Knochen mache ich einen zum Child-GO, so dass ich nur das Parent-GO verschieben oder rotieren muss.
Wenn du willst, kannst du natürlich auch beide Schritte in einem Durchgang erledigen. Wichtig ist am Ende, dass du 1. eine Version des “shrine” GOs mit dem Knochenmuster in schwarz in der Szene hast und 2. eine Version der Knochen separat daneben, etwa so:

Rot umrandet sind die GOs für den Schrein sowie das schwarze Knochenmuster. Grün umrandet sind die Objekte, die der Spieler aufnehmen kann.
Benenne die benutzbaren Knochen jeweils BoneKey1, BoneKey2 und BoneKey3 und die dazugehören Schreine Shrine1, Shrine2 und Shrine3.
Schlüssel
Um die Schlüssel zu vollenden, ist nicht mehr viel nötig: wir kennen die Schritte schon von anderen Objekten.
Zum einen benötigen die Knochenschlüssel Collider. Also fügen wir zu jedem GO einen MeshCollider hinzu. Aufgrund der einfachen Form können wir den MeshCollider optimieren, indem wir das Häkchen bei “Convex” setzen. Bedenke, dass die gekreuzten Knochen ingesamt zwei MeshCollider benötigen.
Zum anderen benötigen die Knochenschlüssel einen Rigidbody. Der wird einmal auf dem obersten GO (BoneKey1, BoneKey2, BoneKey3) angelegt, die gekreuzten Knochen benötigen keinen weiteren Rigidbody auf dem Child-GO. Als “Mass” Attribut wähle ich 5. Die “Collision Detection” setzen wir wieder auf “Continuous Dynamic”.
Wenn du das Spiel nun startest und die Knochenschlüssel in der Ausgangsposition etwas über dem Boden schweben, wie in meinem Beispiel oben, dann sollten sie physikalisch korrekt zu Boden fallen.
Nun fehlt noch die XRGrabInteractable Komponente, die wir jeweils an BoneKey1, BoneKey2 und BoneKey3 hängen.
Wenn du das Spiel jetzt startest und die Schlüssel nacheinander greifst, fällt auf, dass der Knochenschlüssel, der schon von Haus aus aus zwei Knochen besteht (bone_B) nicht richtig in der Hand liegt. Deshalb bedienen wir uns hier eines Tricks: wir erzeugen ein Parent-GO für dieses GO (Rechtsklick auf “bone_B” in der Hierarchy und dann Create Empty), verschieben XRGrabInteractable und Rigidbody auf dieses neue Parent-GO und übertragen auch die bisherige Rotation des “bone_B” GO dorthin. Dann rotieren wir bone_B zu (0, 90, 90).
Und jetzt müssen wir eine Änderung pro Schlüssel an der XRGrabInteractable Komponente vornehmen: damit der Schlüssel später nur auf ein bestimmtes Schloss passt, müssen wir jedem Schlüssel einen eigenen “Interaction Layer” zuweisen. Klicke dazu auf das Dropdown bei “Interaction Layer Mask” und wähle dann “Add layer…”:

Fülle dann in den angezeigten Interaction Layer Settings drei Layer mit den Namen “Key1”, “Key2” und “Key3”:

Interaction Layers sind übrigens unabhängig von den Layers, die du vielleicht schon aus den GameObjects im Inspector kennst.
Gehe dann wieder in die XRGrabInteractable Komponente der drei Schlüssel und füge bei der “Interaction Layer Mask” jeweils eine der neuen Masks hinzu, so dass beim ersten Schlüssel “Default” und “Key1” aktiv sind, beim zweiten Schlüssel “Default” und “Key2” und beim dritten Schlüssel “Default” und “Key3”.
Das war’s für die Schlüssel! Damit wir sie später zur Laufzeit irgendwo platzieren können, machen wir daraus Prefabs: ziehe BoneKey1, BoneKey2 und BoneKey3 jeweils in den Assets/020_Prefabs
Ordner im Project View.
Schlösser
Für die Gegenseite führen wir eine neue Komponente ein, die wir noch nicht besprochen haben: XRSocketInteractor. Mithilfe dieser Komponente kann man andere XRGrabInteractables in einer bestimmten Orientierung aufnehmen: einen Schlüssel in ein Türschloss, einen Pfeil in einen Bogen, einen Stecker in eine Steckdose, eine Patrone in einem Magazin, ein Magazin in einer Waffe,…
Die Komponente arbeitet mit einem Trigger-Collider. Sobald ein XRGrabInteractable Objekt in diesen Trigger-Collider kommt, und vorausgesetzt, die Interaction Layer Mask dieses Objekts passt zur Konfiguration des XRSocketInteractors, wird eine Vorschau angezeigt. Lässt der Spieler das Objekt hier los, wird es entsprechend positioniert.
Das Ganze ist so einfach, dass es super schnell zu realisieren ist – wir beginnen mit Shrine1:
Klicke mit rechts auf einen der Schreine in der Hierarchy und wähle Create Empty. Nenne das neue Child-GO “Keyhole”. Verschiebe jetzt temporär dein “BoneKey1” Objekt in dieses neue “Keyhole” GO und setze Position und Rotation des “BoneKey1” Objekts auf 0 zurück. Verschiebe dann das “Keyhole” GO – nicht den Knochenschlüssel! – so, dass der Knochenschlüssel so in der Vertiefung zu sehen ist, als wäre er korrekt auf der Vorlage platziert worden:

Wichtig ist, dass du lediglich das “Keyhole” GO bewegst und rotierst: der “BoneKey1” unterhalb des “Keyhole” bleibt stets bei Position/Rotation (0, 0, 0).
Wenn die Positionierung des “Keyhole” abgeschlossen ist, kann das “BoneKey1” GO wieder auf obere Ebene gezogen und wegpositioniert werden.
Füge jetzt ein Sphere Collider Komponente zum “Keyhole” GO hinzu. Der Radius des Sphere Colliders sollte so gewählt sein, dass er nicht zu den Seiten herausragt, ich nutze 0.35. Außerdem muss das “Is Trigger” Feld des Colliders aktiviert werden. Der Trigger-Collider bestimmt den Bereich, den der Spieler mit dem Knochen in der Hand treffen muss, damit er abgelegt werden kann – je kleiner der Collider wird, desto schwerer wird das. Wie immer hilft es, mit den Werten zu experimentieren und es einfach mal im Spiel auszuprobieren.

Zuletzt fügen wir die XRSocketInteractor Komponente hinzu und testen das Ganze ohne weitere Änderungen:
Wir sehen, dass der XRSocketInteractor uns eine Vorschau in einem transparenten Blau präsentiert, sobald ein XRGrabInteractable in den Trigger-Collider gelangt. Wir sehen aber auch, dass bisher noch kein Unterschied gemacht wird, welches Objekt ich hier anbiete – ich könnte sogar das Schwert hier hineinlegen.
Jetzt kommen die Interaction Layer Masks zum Einsatz: wir setzen in den BoneKeys auf dem XRSocketInteractor die Maske von aktuell “Everything” auf den passenden Key-Layer. Du kannst hier einfach “Nothing” klicken, um alles zu deaktivieren, und dann den “Key”:

Wiederhole die Schritte nun für Shrine2 und Shrine3:
- Keyhole Child-GO anlegen, den passenden BoneKey hineinziehen und Position und Rotation auf (0, 0, 0) setzen
- Keyhole verschieben und rotieren, so dass der Schlüssel zum Symbol passt
- XRSocketInteractor und SphereCollider hinzufügen und konfigurieren
- BoneKey wieder herausziehen und umpositionieren
Im Spiel sollte jeder Schlüssel jetzt nur in genau die Säule passen, die das entsprechende Symbol zeigt, und Vorschau und tatsächliche Position sollten ebenfalls richtig aussehen.
Damit sind auch die Schlösser für’s Erste fertig.
Anmerkung: Streng genommen müssen wir die drei Schreine nicht als Prefabs wegspeichern, denn davon wird’s nicht mehr geben und sie müssen nicht dynamisch erzeugt werden. Ich mach’s aber trotzdem: mich beruhigt der Gedanke, dass das Objekt nochmal separat im Dateisystem liegt und nicht ausschließlich in der Szene, nicht zuletzt, weil ich bei “Build And Run” ungespeicherte Änderungen verloren habe. (Diesen Fehler habe ich übrigens auch in der Release-Version nochmal erlebt.)
Außerdem könntest du anstatt drei verschiedenen Prefabs auch nur ein Prefab erzeugen und dann für die Anpassungen in Form der Symbole Prefab Variants erzeugen. Das würde den Änderungsprozess etwas vereinfachen, wenn wir später noch Kleinigkeiten erweitern. Das würde ich allerdings erfahrenen Unity-Nutzern überlassen.
Tor
Unglaublich, aber wahr: das Tor ist eine größere Herausforderung als die VR Interaktion zuvor.
Vorbereitung des Crypt-Meshs
Die Gruft, das “crypt” Modell, ist leider nicht für unseren Anwendungsfall vorbereitet. Weder ist der Eingang separiert, so dass er geöffnet werden könnte, noch gibt es einen Innenraum, noch gibt es ein Loch im Boden, um hinabzusteigen. Hier gibt es auch keine Tricks: wir kommen nicht drum herum, das Mesh selbst nachzubearbeiten.
Aber keine Sorge! Da das ein weiteres Tutorial füllen könnte, habe ich das Mesh entsprechend vorbereitet. Dennoch will ich kurz zwei Möglichkeiten aufzeigen, wie man das bewerkstelligen könnte:
- Unity bringt ein eigenes Tool zum Bearbeiten von Meshes direkt im Editor mit. Es heißt ProBuilder und muss über den Package Manager nachinstalliert werden. ProBuilder war, genau wie TextMeshPro, ursprünglich ein 3rd Party Plugin, das sich inzwischen gewaltig weiterentwickelt hat und zu Unity gehört.
- Blender ist ein kostenloses 3D Modelling Tool, das nicht nur alle nötigen Funktionen zum Importieren, Modellieren und Exportieren von Unity-kompatiblen Models mitbringt, sondern auch noch viele weitere Funktionen, wie UV Editing, Rigging oder Animationen.
Ich hatte mit den frühen Versionen von ProBuilder so viele Probleme, dass ich zu Blender gewechselt habe und inzwischen alle Anpassungen an 3D Modellen dort vornehme. Es macht für mich mehr Sinn, ein separates Tool zu nutzen, das alles ziemlich gut kann, als viele verschiedene Editor-Erweiterungen in Unity zu installieren, die dann gelegentlich noch fehlerbehaftet sind und im Falle eines Absturzes womöglich ungespeicherte Arbeit an der aktuellen Szene mit in den Tod reißen. YMMV.
Hier findest du das angepasste 3D Modell:
Lade es herunter und speichere es in Assets/040_Models
. Suche dann dein “crypt” GO in der Szene und ersetze sowohl im Filter als auch im Mesh Collider einfach das Mesh mit dem neuen Mesh aus diesem Modell. Falls du aus dem “crypt” bereits ein Prefab gemacht hast, kannst du die Änderung auch noch im Inspector über den “Overrides” Button auf das Prefab anwenden.
Wie bei jedem neu importierten Modell musst du in Unity die Lightmap UVs generieren. Klicke dazu auf das crypt_no_door Model im Project View, dann rechts im Inspector im Model Tab auf “Generate Lightmap UVs” und dann auf “Apply”.
Wenn du dir jetzt die Gruft in der Szene ansiehst, solltest du feststellen, dass 1. der Eingang frei ist und 2. ein Loch im Boden das Terrain darunter sichtbar macht:

Vorbereitung des Terrains
Das Terrain stellt uns nun vor die gleiche Herausforderung: es hat kein Loch im Mesh, durch das der Spieler in die Gruft hinabsteigen könnte. Und wir könnten jetzt den gleichen Weg gehen und einfach ein passendes Loch in das Terrain-Mesh schneiden. Das wäre der einfachere Weg, da wir damit auch die Kollisionsabfrage erledigt hätten, die ja beim Terrain auf einem MeshCollider mit dem gleichen Mesh basiert. Aber dann müsstest du entweder das Loch selbst schneiden – womit wir wieder bei der Benutzung der o.g. 3D Modeling Tools wären – oder die Gruft müsste bei uns allen an der gleichen Stelle stehen. Daher wählen wir hier einen anderen Weg.
Die Herausforderung, durch ein Objekt mindestens hindurch gucken zu können, gibt es in der Spieleentwicklung öfter. Vielleicht hast du schonmal gesehen, dass du plötzlich durch ein Haus sehen konntest, wenn deine Spielfigur dahinter verschwunden ist. Oder du hast Portal gespielt, wo man an beliebiger Stelle ein Portal zu einer anderen Stelle des Levels platzieren konnte.
Ein Teil der Lösung ist der sogenannte Stencil Buffer. Dabei handelt es sich um ein Raster entsprechend der Auflösung des Spielbildschirms, in das Materialien schreiben, lesen und das Rendern des eigenen Materials davon abhängig machen können. Wir werden uns dieses Verfahren zunutze machen, indem wir auf dem Terrain an der Stelle des Durchgangs einen Würfel platzieren, der unsichtbar ist und nichts anderes tut, als einen Wert in den Stencil Buffer zu schreiben. Dann ändern wir das Material des Terrains so, dass es diesen Wert prüft und nur dort rendert, wo der Wert noch nicht im Stencil Buffer steht. Auf diese Weise können wir eine Aussparung erzeugen, ohne dass wir das Material ändern müssen.
Realisiert wird das über Shader. Während ein Material, von dem wir bereits einige im Verlauf des Tutorials angelegt haben, beschreibt, welche Eigenschaften eine Oberfläche haben soll, ist der Shader das Programm dazu, das die Oberfläche tatsächlich mithilfe des Materials bemalt. Bei allen Objekten und Materialien haben wir bereits implizit einen der Standard-Shader verwendet, “Universal Render Pipeline/Lit”. Nun benötigen wir zwei weitere:
- Einen Shader zum Ausschneiden: der macht nichts anderes, als einen bestimmten Wert in den Stencil Buffer zu schreiben. Er schreibt keinerlei Farbinformationen, womit das Material, an dem der Shader hängt, und damit das entsprechende Objekt, unsichtbar bleibt. Ich habe diesen Shader “StencilMask” genannt.
- Ein Version des Standard “Lit” Shaders, mit dem aktuell bereits alle Materialien in der Szene gerendert werden. Dieser wird einfach um den Stencil Buffer Test erweitert. Ich habe diesen Shader “LitCustom” genannt.
Beide Shader kannst du hier herunterladen. Platziere sie in deinen Assets/060_Materials
Ordner:
Selektiere nun das Terrain-Material in Assets/060_Materials und ändere oben rechts im Inspector den Shader von Standard “Universal Render Pipeline/Lit” zu “Custom/LitCustom”.

Das Terrain im Scene View verfärbt sich möglicherweise kurz, während der Shader compiliert wird. Danach sollte das Terrain genau so aussehen, wie zuvor, denn der neue Shader ist identisch und lediglich um einen Stencil Buffer Check erweitert, der aber bisher noch keine Wirkung erzielt, da noch nichts in den Stencil Buffer schreibt.
Das ändern wir jetzt, indem wir ein Objekt anlegen, das ein Loch in das Terrain schneidet. Klicke rechts in der Hierarchy auf das “crypt” GO und wähle 3D Object → Cube. Skaliere den Cube dann so, dass er das Loch im Boden der Gruft in Breite und Tiefe gut ausfüllt. Achte allerdings darauf, dass der Cube nur so hoch ist, wie es das Terrain verlangt – andernfalls ist das Loch im Terrain vielleicht auch von anderen Stellen sichtbar, wo die Crypt-Geometrie nicht den Blick versperrt.

Lege nun im Project View unter Assets/060_Materials
per Rechtsklick auf den Ordner ein neues Material an: Create → Rendering → Material. Nenne das Material “StencilMask”. Ändere dann in diesem neuen Material oben den Shader zu “Custom/StencilMask”.
Ziehe dann das Material auf den gerade angelegten Cube in der Gruft oder ändere den ersten Material Slot im Mesh Renderer des Cubes entsprechend. Jetzt kannst du durch das Terrain direkt auf die Skybox gucken:

Damit steht dem Abstieg in das Verlies des Skelettkönigs fast nichts mehr im Wege – nur der MeshCollider des Terrains. Aber das ist ein Thema für später, jetzt stellen wir sicher, dass wir ein neues Tor bekommen das wir öffnen können.
Entscheidung für ein Tor
Das ursprüngliche Tor im Crypt, das ich in der verlinkten Version entfernt habe, war leider nicht mehr als eine flache Ebene und hätte daher als Tor zum Öffnen nicht getaugt. Im Dungeon Remastered Pack finden sich einige Torbögen mit hölzernen Türen, aber die sind alle oben abgerundet und daher auch nicht wirklich brauchbar und für ein magisches Tor noch dazu reichlich unspektakulär.
Nach einigem Abwägen habe ich mich entschieden, das “arch_gate” Prefab, das ich bereits beim Umzäunen des Friedhofs verwendet und zu einem Prefab konvertiert hatte, zu verwenden:

Das integriert sich einigermaßen gut in die Gruft und gibt den Blick auf das Innere frei. Außerdem besteht es aus drei separaten Meshes: dem Torbogen selbst und einem linken und rechten Tor, so dass wir letztere bei erfolgreichem Platzieren aller Schlüssel problemlos aufschwenken können.
Allen Meshes habe ich unser eigenes HalloweenBits Material zugewiesen. Meinem “arch_gate” Prefab habe ich einen MeshCollider verpasst, den beiden Toren “arch_gate_left” und “arch_gate_right” jeweils einen BoxCollider. Das “arch_gate” Mesh habe ich als “Static” markiert, die beiden Tore allerdings nicht, denn die sind streng genommen eben nicht statisch.
Das “arch_gate” habe ich übrigens zu einem Child-GO unter dem “crypt”-GO gemacht, denn beides gehört zusammen und sollte auch zusammen bewegt werden.
Der Schließmechanismus
Jeder Schrein hat einen XRSocketInteractor und – genau wie die XRGrabInteractables – hat der Select Entered und Select Exited Events, die wir verarbeiten können. Dadurch, dass er außerdem jeweils für einen Interaction Layer – Key1, Key2 oder Key3 – konfiguriert ist, ist sichergestellt, dass nur der passende Skelettschlüssel ein Select Event auslösen kann.
Wenn also jedes dieser Schlösser eine Nachricht an das Tor sendet – Key eingesteckt, Key ausgesteckt –, dann kann das Tor sich den Status merken und feststellen, wenn alle Keys im Status “eingesteckt” sind und das Tor öffnen.
Um das mit Visual Scripting zu realisieren, benötigen wir also folgendes:
- Einen Script Graph, der die Select Events des XRSocketInteractors verarbeitet und eine Nachricht an das Tor sendet. Der Script Graph benötigt Kenntnis über das Tor, an das Events geschickt werden soll, z. B. in Form einer Object Variable. (Da die Gruft genau wieder Spieler ein zentrales und einmaliges Spielelement in der Szene ist, würde genau so gut eine Scene Variable funktionieren.)
- Einen Script Graph für das Tor. Es empfängt die Events der verschiedenen Schlösser und aktualisiert den Zustand. Das Tor muss nicht wissen, welches Schloss das Event schickt: es reicht, einen Zähler hoch- oder runterzuzählen. Sobald alle drei Schlüssel eingesteckt wurden, soll das Tor geöffnet werden. Dazu bedienen wir uns wieder einem bekannten Mechanismus: wir triggern eine Animation.
Beginnen wir mit dem ersten Teil: fügen wir eine Script Machine Komponente zu einem der drei “Keyhole” GOs an einem Schrein hinzu. Nach Klick auf “New” im Inspector speichern wir den Script Graph unter dem Namen “Lock”. In der Variables Komponente legen wir eine Variable “Crypt” vom Type GameObject an und ziehen das “crypt”-GO in das Feld. Jetzt öffnen wir den “Lock” Script Graph und legen die beiden Select Events an, von denen wir jeweils Custom Events mit den Namen “KeyIn” bzw. “KeyOut” an das “Crypt”-GO senden:

Wir wiederholen den Prozess jetzt für die anderen beiden “Keyhole”-GOs der Schreine: wir fügen die Script Machine Komponente hinzu, legen diesmal aber keinen neuen Script Graph an sondern wählen den gerade erstellten “Lock” Script Graph aus. Außerdem legen wir jeweils die “Crypt” Variable an.
Als Nächstes animieren wir das Tor. Da wir unsere Events direkt an’s “crypt”-GO schicken, legen wir auch dort den Animator an. Markiere das “crypt” in der Hierarchy und wechsle zum Animation Tab. Dort solltest du die Meldung sehen: “To begin animating crypt, create an Animator and an Animation Clip”. Klicke auf den “Create” Button darunter.
Im Speichern Dialog wechseln wir in den 070_Animation
Ordner und speichern den Clip als Crypt_OpenGates
. Dann drücken wir den “Record” Button. Um das erste Keyframe aufzunehmen, rotieren wir sowohl “arch_gate_left” als auch “arch_gate_right” ein wenig auf der Y-Achse und setzen dann die Rotation wieder zurück. Jetzt springen wir im Animation View ans Ende, zu Frame 59, und rotieren beide Tore so, dass sie weit offen stehen:
Der Clip soll sich nicht wiederholen, daher wählen wir Crypt_OpenGates im Project View und entfernen dann im Inspector das Häkchen bei “Loop Time”.
Zuletzt bearbeiten wir den Animator Controller, der unter dem Namen des GOs, “crypt”, angelegt wurde. Im Moment ist der Animation State der Default, wir wollen ihn allerdings nur im Falle eines expliziten Triggers ausführen. Daher legen wir einen zweiten Animation State an und wählen bei dem nach Rechtsklick auf den State “Set as Layer Default State”.
Links oben im Animator Controller Fenster fügen wir einen Parameter “Open” vom Typ Trigger ein. Dann ziehen wir von “Any State” eine Transition zu “Crypt_OpenGates” mit der Condition “Open”.

Jetzt legen wir eine Script Machine Komponente auf dem “crypt”-GO an und speichern wieder den Script Graph unter Assets/030_Scripts
mit dem Namen “Crypt”. In der Variables Komponente legen wir eine Variable mit dem Namen “KeyInCount” vom Typ “Integer” und Startwert 0 an.
Den Script Graph beginnen wir mit der Behandlung der beiden Events, die von den Schlössern geschickt werden: KeyIn und KeyOut. Bei KeyIn erhöhen wir “KeyInCount”, bei KeyOut reduzieren wir die Variable wieder:

Bei “KeyOut” müssen wir nichts weiter machen. Bei “KeyIn” wird aber der Zähler auf 3 gesetzt, wenn der Spieler alle drei Schlüssel platziert hat, und dann können wir den “Open” Trigger auf dem Animator auslösen. Und das genau einmal, denn wenn das Tor einmal auf ist, ist dieses Ziel erreicht. Da damit auch der Zweck dieser Script Machine erfüllt ist, können wir sie dann einfach deaktivieren:

Jetzt sieht das Ganze so aus:
Soundeffekte
Soundeffekte sind bisher etwas zu kurz gekommen, also habe ich mich etwas auf freesounds.org umgesehen und mich für zwei Soundeffekte entschieden, mit denen wir die Interaktion etwas erweitern können:
- Wet Spell shoot von “Bertsz” (CC0) ist ein passender Effekt für das Platzieren der Knochenschlüssel in die Schreine.
- Metal Door Opening von “nebyoolae” (CC-BY) passt gut zum Öffnen des Tors. Bitte beachte, dass die Verwendung dieses Soundeffekts eine Namensnennung erfordert.
Das Herunterladen der Soundeffekte auf freesounds.org erfordert einen Account, der aber komplett kostenlos angelegt werden kann. Falls du nicht selbst stöbern möchtest, kannst du sie auch hier herunterladen:
* Metal Door Opening by nebyoolae — https://freesound.org/s/267692/ — License: Attribution 4.0
Wenn du Soundeffekte heruntergeladen hast, kopiere sie in den Ordner Assets/080_SFX
.
Um den Soundeffekt der Schreine einfach abspielen zu können, erweitern wir in den “Shrine1/2/3”-GOs jeweils das “Keyhole”-Child-GO, in dem auch der XRSocketInteractor existiert, um eine Komponente namens AudioSource. Dann wählen wir für das Feld “Audio Resource” die richtige Audiodatei aus und entfernen das Häkchen bei “Play On Awake”. Damit der Spieler den Soundeffekt räumlich wahrnimmt, verschieben wir außerdem den “Spatial Blend” Slider von ganz links (2D) nach ganz rechts (3D). Da der Spieler sehr nah am Schrein stehen muss, um den Sound überhaupt auszulösen, reduzieren wir die “Min Distance” zu 0.1 und die “Max Distance” zu 5.
Die Änderung muss für alle drei Keyholes durchgeführt werden. Danach können die Änderungen wieder auf die jeweiligen Prefabs angewendet werden.
Um den Sound auszulösen, wenn der Knochenschlüssel erfolgreich platziert wurde, erweitern wir den “Lock” Script Graph um einen weiteren Node, nämlich “Audio Source Play”:

Genau so können wir auch beim “crypt”-GO vorgehen. Wir fügen eine AudioSource Komponente hinzu, wählen den Tor-Öffnen Soundeffekt und konfigurieren die restlichen Eigenschaften wie oben. Die “Min Distance” und “Max Distance” sollten so gewählt werden, dass der Spieler das Tor noch hört, selbst wenn der Schrein einige Meter entfernt steht. Da ich plane, die Schreine in unmittelbarer Nähe zur Gruft zu platzieren, wähle ich 1 und 10. Wie immer hilft es, mit den Werten zu experimentieren und sich das Ergebnis im Headset anzuhören.
Jetzt ändern wir den “Crypt” Script Graph. Da der Soundeffekt getriggert werden sollte, bevor die Script Machine deaktiviert wird, fügen wir den Node vor dem letzten ein:

So klingt das dann:
Der Effekt beim Öffnen des Tors ist etwas länger als die Animation. Das kann man leicht synchronisieren, indem man das letzte Keyframe der Animation im Animations-Fenster etwas weiter nach hinten schiebt und sie so verlängert.
Ausblick
Im nächsten Teil werden wir die Skelette aus Gräbern aufsteigen lassen und die Knochenschlüssel zufällig auf diese Skelette verteilen, so dass der Spieler sich etwas anstrengen muss, bevor er Zugang zur Gruft erhält.
Der nächste Teil ist in Arbeit, kehre bald wieder zurück um zu lesen, wie’s weitergeht!