Du kannst mit Verse deine eigenen Komponenten erstellen, um sie deinen Entitäten hinzuzufügen. Mit benutzerdefinierten Verse-Komponenten kannst du Entitäten in der Szene spawnen und entfernen, Komponenten zu Entitäten hinzufügen oder davon entfernen, dein eigenes Verhalten erstellen, wie z. B. eine verschwindende Entität in einer Schleife, oder was dir sonst so einfällt!
Erstellen einer neuen Verse-Komponente
Du kannst über die Verse-Vorlagendatei eine neue Verse-Komponente erstellen.
So erstellst du eine neue Verse-Komponente:
Wähle im Details-Panel für deine Entität die Option Komponente hinzufügen > Neue Verse-Komponente.
Du kannst auch eine neue Verse-Komponente erstellen, indem du eine neue Verse-Datei über den Verse-Explorer hinzufügst.
Wähle in der Liste der Verse-Codevorlagen die Option Scene Graph Component aus.
Setze den Komponentennamen auf den Namen deiner mit Verse erstellten Komponente. In diesem Beispiel heißt die Komponente
my_verse_component.Klicke auf Erstellen, um deine Verse-Komponentendatei zu erstellen. Deine mit Verse erstellte Komponente wird nun in der Liste der Komponenten angezeigt, wenn du eine Komponente zu deiner Entität hinzufügst.
Du kannst nur eine Komponente aus einer bestimmten Klasse oder Subklasse hinzufügen. Du kannst zum Beispiel nur eine mesh_component auf einer Entität haben. Dies gilt auch für Subklassen von Komponenten, d. h., wenn du eine capsule_light_component zu deiner Entität hinzufügst, kannst du nicht auch eine rect_light_component hinzufügen, da beide Subklassen von light_component sind. Die gleiche Einschränkung gilt auch für deine benutzerdefinierten Komponenten, die in Verse erstellt wurden.
Komponentenlebenszeit
Komponenten durchlaufen eine Reihe von Lebenszeitfunktionen, wenn sie zu Entitäten hinzugefügt werden, der Szene hinzugefügt werden und in der Simulation ausgeführt werden. Deine in Verse erstellten Komponenten müssen diese Methoden außer Kraft setzen, um ihre Simulation einzurichten und auszuführen.
Wenn eine Komponente abgeschaltet wird, bewegt sie sich durch die Abschaltversion dieser Funktionen und gibt dir die Möglichkeit, den Zustand der Komponente zu bereinigen, bevor sie entsorgt wird.
Im Folgenden sind die Lebenszeitzustände einer Komponente aufgeführt:
Initialisiert
AddedToScene
BeginSimulation
EndSimulation
RemovingFromScene
Initialisierung wird aufgehoben
Die Lebenszeitfunktionen der Komponenten unterscheiden sich von den Gerätelebenszeiten. Die Komponentenlogik läuft im Bearbeitungs- und Spielmodus. Jedes Verhalten, das du hinzufügst, wird sofort ausgeführt, wenn du deine Sitzung startest. Wenn du möchtest, dass deine Komponentenlogik nur beim Spielstart ausgeführt wird, kannst du in der Funktion OnBegin() eines Verse-Geräts Prefabs spawnen.
Abfragen von Entitäten und Komponenten mit Verse
Es gibt mehrere Möglichkeit, wie du Entitäten und Komponenten in deinem Verse-Code suchen kannst. Wie du deine Entitäten und Komponenten strukturiert wirkt sich darauf aus, wie du Funktionalität in deinem Verse-Code abfragst und ihn entwickelst.
Das Abfragen von Entitäten und Komponenten in Verse erfordert, dass du mit einer Entität beginnst und Entitäten zurückgibst, die in der Hierarchie darüber oder darunter geschachtelt sind. Du kannst die direkten Parent- oder Children-Entitäten einer Entität abfragen, sowie alle ihre Ancestor- und Descendant-Entitäten.
Abrufen von Entitäten mit dem Komponententyp
Du kannst alle Entitäten mit einer Komponente eines bestimmten Typs suchen, indem du die Entität aufrufst, die du abfragen möchtest. Wenn die Entität, die du abfragst, die Simulationsentität ist, gibt sie alle Entitäten zurück, die Komponenten dieses Typs in der Szene haben.
In dem folgenden Beispiel holt die Verse-Komponente alle Entitäten, an die die Komponente light_component angekoppelt ist. Für jede Entität, die sie findet, spawnt sie eine particle_sytem_component und koppelt sie an sie an. Hier ist BlowingParticles ein Niagara-Emitter, auf den in der Datei Assets.digest.Verse verwiesen wird.
Die light_component ist eine Superklasse für alle verschiedenen Lichtkomponenten-Typen, die du zu deinen Entitäten hinzufügen kannst. In der folgenden Abfrage wird LightComponent verwendet, um Entitäten mit einer beliebigen Lichtkomponente zu suchen.
# Runs when the component should start simulating in a running game.
OnBeginSimulation<override>():void =
# Run OnBeginSimulation from the parent class before
# running this component's OnBeginSimulation logic
(super:)OnBeginSimulation()
for:
LightComponent : Entity.GetSimulationEntity[].FindDescendantEntitiesWithComponent(light_component)
do:
# Create a particle system component and add it to the entity.
Das nächste Beispiel zeigt, wie mit der Funktion FindDescendantEntitiesWithComponent() alle Entitäten abgefragt werden, die Partikelsysteme unter der Entität geschachtelt haben, an die die Verse-Komponente angekoppelt ist. Auf die gleiche Weise kannst du mit FindAncestorEntitiesWithComponent() auch alle Ancestor-Entitäten mit einer bestimmten Komponente holen.
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically canceled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void=
# Get all entities that have particle system components nested under the entity this component is attached to.
ParticleSystemEntities := Entity.FindDescendantEntitiesWithComponent(particle_system_component)
# Get all entities with particle system components that are ancestors of this entity.
Wenn du Entitäten für deine gesamte Szene abfragen musst, kannst du dazu die Simulationsentität holen und deine Abfragen mit ihr durchführen. Dies beginnt in der obersten Ebene der Entitätsstruktur und sucht alle geschachtelten Entitäten, die der Abfrage entsprechen. Um auf die Simulationsentität zuzugreifen, rufe die fehleranfällige Funktion GetSimulationEntity[] für die Entität auf, an die die Verse-Komponente angehängt ist.
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically canceled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void=
# Get the simulation entity.
if:
SimulationEntity := Entity.GetSimulationEntity[]
then:
Wenn deine Verse-Komponente dauernd den Entitätenbaum auf- und abwärts durchsuchen muss, kann das teuer sein. Wenn dein Verhalten von einer bestimmten Entitätsstruktur abhängt, können kleinste Änderungen an deiner Entitätsstruktur dazu führen, dass sich dein Verhalten unbeabsichtigt ändert oder überhaupt nicht funktioniert.
Andererseits bedeutet dies auch, dass deine Komponente weniger Einrichtung erfordert, da du nur deine Verse-Komponente hinzufügen und die richtige Entitätsstruktur verwenden musst. Bedenke diese Abstriche, wenn du deine Entitäten erstellst und die Logik deiner Verse-Komponente entwickelst.
Sieh dir das SceneGraph-Modul genauer an, um dich über alle Methoden zu informieren, wie du mit Entitäten und Komponenten in Verse arbeiten kannst. Im Folgenden werden einige gängige Methoden zur Abfrage von Entitäten und Komponenten in deinem Code beschrieben.
Abrufen von Komponenten einer Entität
Du kannst mit Verse eine Komponente eines bestimmten Typs auf einer Entität holen, indem du GetComponent[] aufrufst. Das ist praktisch für das Erstellen benutzerdefinierter Logik, die vom Verhalten einer anderen Komponente abhängt. So kannst du z. B. die sound_component verwenden, um Audio abhängig von der Farbe eines Lichts abzuspielen, oder eine particle_system_component abrufen, um Effekte abhängig davon anzuwenden, ob sich die Entität innerhalb einer bestimmten Zone befindet.
In dem folgenden Beispiel lässt die Verse-Komponente in einer Schleife eine Plattform wiederholt erscheinen und verschwinden. Dazu ruft sie die Mesh-Komponente der Entität ab und deaktiviert sie, um sie dann nach einer bestimmten Dauer wieder zu aktivieren.
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SceneGraph }
# A Verse-authored component that can be added to entities.
# This component will make the entity appear and disappear on loop.
disappear_on_loop_component := class<final_super>(component):
# How long in seconds the entity should be hidden.
@editable
In einem anderen Beispiel holt die Verse-Komponente die light_component auf der Entität und ändert ihre Farbe in Dunkelorange. Die light_component ist eine Superklasse für alle Lichtarten, die du zu deinen Entitäten hinzufügen kannst, sodass dieses Beispiel jede Komponente findet, die eine Subklasse davon ist.
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically canceled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void=
# Find any component on this entity that subclasses from light_component.
if:
LightComponent := Entity.GetComponent[light_component]
then:
Alle Komponenten holen
Du kannst die Funktion GetComponents() verwenden, um alle Komponenten der Entität zurückzugeben. Da dein Code nicht weiß, um welchen Komponententyp es sich handelt, kannst du das Casting verwenden, um je nach Typ der Komponente verschiedene Operationen durchzuführen. Das folgende Beispiel holt ein Array mit allen Komponenten einer Entität und versucht, sie in den Typ enableable umzuwandeln. Wenn der Cast erfolgreich ist, implementiert die Komponente das enableable Interface. Es deaktiviert dann jede Komponente, die dieses Interface implementiert.
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically canceled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void=
# Get a list of all components on the entity.
ComponentList := Entity.GetComponents()
for:
Component:ComponentList
Suchen von Entitäten mit Gameplay-Tags
Du kannst eine Tag-Komponente zu deinen Entitäten hinzufügen, damit du bestimmte Entitäten innerhalb deiner Szene finden kannst, ähnlich wie du Gameplay-Tags zu deinen Actors hinzufügen kannst. Das ist praktisch, um herauszufinden, mit welchen Entitäten du arbeiten möchtest, statt dich auf änderbare Dinge zu verlassen, wie zum Beispiel welche Komponenten sie haben oder wo in der Szene sie sich befinden.
Das liegt daran, dass das Vertrauen auf änderbare Dinge zu unerwünschtem Verhalten in deinem Spiel führen kann, wenn du neue Entitäten zu einem Projekt hinzufügst und sie änderst. Du kannst Tags im Editor hinzufügen, indem du eine tag_component zu deiner Entität hinzufügst und Tags aus der Dropdown-Liste Tags auswählst, oder im Verse-Code, indem du die Funktion AddTag() verwendest.
Das folgende Beispiel fragt die Simulationsentität für alle Descendants ab, die mit dem Tag my_tag auf ihrer tag_component gekennzeichnet sind.
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically cancelled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void =
# Get the simulation entity.
if:
SimulationEntity := Entity.GetSimulationEntity[]
then:
Entitäten mit Überlappungen suchen
Kollisions-Volumes sind Volumes, die Kollisionsformen von Meshs darstellen. Du kannst sie verwenden, um nach überlappenden Objekten innerhalb einer bestimmten Form zu suchen, z. B. nach beschädigten Objekten, wenn sie einem Turm zu nahe kommen, oder nach der Erkennung, wenn ein Fußball ins Tor geht. In Verse kannst du die Funktion FindOverlapHits() verwenden, um alle Entitäten in einem bestimmten Bereich zu suchen.
Dieser Bereich kann entweder die Entität selbst sein, ein bestimmtes Kollisions-Volume, wie z. B. eine Kugel oder ein Kasten, oder eine Position, von der aus die Entität simuliert werden soll. Sie gibt dann eine Liste von overlap_hit zurück. Jeder overlap_hit gibt dir Informationen über die Komponente oder das Volume, das vom Quell-Volume überlappt wird, und du kannst diese Komponenten abfragen, um ihre assoziierte Entität zu finden.
Das folgende Beispiel erstellt eine Kugel mit einem Radius von 256.0 Einheiten, die auf der Transformation der Entität zentriert ist. Es findet dann alle Überlappungen innerhalb der Kugel und gibt eine Liste von overlap_hit zurück. Da jeder overlap_hit entweder eine Komponente oder ein Volume ist, kannst du entweder die TargetComponent oder das TargetVolume abfragen, um zu wissen, welchen Typ es hat. Wenn der overlap_hit eine Komponente ist, holt der Code dann die Entität der Komponente. Schließlich wird geprüft, ob die Entität eine Lichtkomponente hat. Wenn dies der Fall ist, ändert es die Farbe des Lichts in blau. Mit einem ausreichend großen Volume kannst du die Farbe jedes Lichts auf deiner Insel mit nur ein paar Zeilen Code ändern!
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically canceled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void=
# Define a volume to find entities within.
# This is a sphere whose radius is 256.
CollisionSphere:collision_sphere = collision_sphere:
Radius := 256.0
Dieser Code simuliert ein überlappendes collision_sphere-Volume, das durch den Kreis mit einem Radius von 256,0 Einheiten bezeichnet wird, der auf der Würfelentität zentriert ist, und das alle Komponenten oder Volumes zurückgibt, mit denen sich die Kugel überlappt. Es holt dann die Parent-Entität jeder überlappenden Komponente ab und macht alle Lichtkomponenten darauf blau. Da sich der Würfel innerhalb der collision_sphere befindet, wenn die Überlappung beginnt, wird er in der Liste der overlap_hits aufgenommen und wird ebenfalls blau. Beachte, dass sich die rote Kegel-Entität ganz rechts außerhalb der collision_sphere befindet und sich daher nicht überlappt und blau wird.
Suchen von Entitäten mit Sweeps
Eine weitere wichtige Methode zur Abfrage von Entitäten sind Sweeps. Beim Sweeping wird ein Objekt über eine bestimmte Entfernung entlang eines bestimmten Vektors bewegt. So kannst du zum Beispiel einen Block über eine Plattform bewegen, um Spieler in eine Lücke zu stoßen, oder eine Rakete direkt nach vorne starten, um eine Wand zu zerstören.
In Verse kannst du Sweeps simulieren, um Kollisionen zwischen Objekten mit der Funktion FindSweepHits() abzufragen. Diese Funktion nimmt einen Verschiebungsvektor, um das Bewegen eines Objekts zu simulieren. Du kannst Sweeps entweder mit der Parent-Entität oder einem bestimmten Kollisions-Volume durchführen und die globale Start-Transformation angeben, von der aus du den Sweep starten möchtest.
Die Funktion FindSweepHits() gibt eine Liste von sweep_hit zurück. Jeder sweep_hit liefert dir dieselben Informationen wie ein overlap_hit, z. B. die Komponente oder den Volume-Treffer und das Quell-Volume oder die Komponente, die den Sweep durchführt. Es liefert zusätzlich Informationen über die Kontaktposition, Normale, Flächennormale und den Abstand entlang des Sweeps, wo der Treffer aufgetreten ist.
Das folgende Beispiel nimmt eine Entität, ruft FindSweepHit() auf und übergibt einen Vektor mit einer Länge von 1000.0 für den Vorwärtswert. Der Code simuliert dann, welche Kollisionen auftreten würden, wenn die Entität um 1000.0 Einheiten in die positive Vorwärtsrichtung verschoben würde, und gibt eine Liste von sweep_hit zurück.
Da jeder sweep_hit entweder eine Komponente oder ein Volume ist, kannst du entweder die TargetComponent oder das TargetVolume abfragen, um zu wissen, welchen Typ es hat. Wenn der Treffer eine Komponente ist, ruft der Code dann die Parent-Entität der Komponente ab. Schließlich wird geprüft, ob die Entität eine Lichtkomponente hat. Wenn dies der Fall ist, ändert es die Farbe des Lichts in blau.
for:
# Simulate sweeping this entity 1000 units in the positive X direction, and return any components and volumes it overlaps with.
SweepHit : Entity.FindSweepHits(vector3{Left := 0.0, Up := 0.0, Forward := 1000.0}, Entity.GetGlobalTransform(), CollisionBox)
# Check that the overlap is a component, and if so get its parent entity.
# Then if the entity has a light component, change its color filter.
TargetComponent := SweepHit.TargetComponent
TargetEntity := TargetComponent.Entity
LightComponent := TargetEntity.GetComponent[light_component]
do:
Dieser Code simuliert das Sweepen der Würfelentität um 1000,0 Einheiten in der positiven X-Richtung und gibt alle Komponenten zurück, mit denen sie sich überlappt. Es holt dann die Parent-Entität der überlappenden Komponente und macht alle Lichtkomponenten darauf blau. Beachte, dass die Entität, die den Sweep durchführt, in der Trefferliste nicht enthalten ist, sodass der Würfel selbst nicht blau wird. Die rote Kegel-Entität ganz rechts wird auch nicht blau, da sie außerhalb des Sweeps liegt.
Dieses nächste Beispiel ähnelt dem vorherigen, außer dass zuerst ein collision_box-Volumen konstruiert und dann 1000.0 Einheiten in positiver Vorwärtsrichtung gesweept werden, beginnend von der Mitte der Würfelentität. Da sich der Würfel innerhalb der collision_box befindet, wenn der Sweep beginnt, wird er in der Liste der sweep_hits aufgenommen und wird ebenfalls blau.
# Define a volume to sweep over entities.
# This box is 1/4th the size of a standard 512x512 grid tile.
CollisionBox:collision_box = collision_box:
Extents := vector3:
Left := 128.0,
Up := 128.0,
Forward := 128.0
for:
# Simulate sweeping the CollisionBox 1000 units in the positive X direction, and return any components and volumes it overlaps with.
SweepHit : Entity.FindSweepHits(vector3{Left := 0.0, Up := 0.0, Forward := 1000.0}, Entity.GetGlobalTransform(), CollisionBox)
Dieser Code simuliert das Sweepen eines collision_box-Volumens, das durch das gelbe Quadrat mit 1000,0 Einheiten in positiver Vorwärtsrichtung gekennzeichnet wird, und gibt alle Komponenten zurück, mit denen es sich überlappt. Es holt dann die Parent-Entität der überlappenden Komponente und macht alle Lichtkomponenten darauf blau. Da sich der Würfel innerhalb der collision_box befindet, wenn der Sweep beginnt, wird er in der Liste der sweep_hits aufgenommen und wird blau. Die rote Kegel-Entität ganz rechts wird nicht blau, da sie außerhalb des Sweeps liegt.
Spawnen und Entfernen von Entitäten mit Verse
Du kannst eine Entität aus der Szene entfernen, indem du RemoveFromParent() für die Entität aufrufst. Du kannst eine Entität zu der Szene hinzufügen, entweder eine neue oder zuvor entfernte, indem du AddEntities() auf die Entität aufrufst, die zur Parent-Entität wird.
In dem folgenden Beispiel sucht die Verse-Komponente alle Entitäten, die mit dem Gameplay-Tag my_tag versehen sind. Sie entfernt jede gefundene Entität von ihrem Parent, wodurch die Entität aus der Szene entfernt wird und die gleiche Entität nach fünf Sekunden wieder zu ihrem Parent hinzugefügt wird, um sie wieder in der Szene zu spawnen.
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically cancelled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void =
# Find all entities tagged and get their parent entity.
for:
TaggedEntity : Entity.GetSimulationEntity[].FindDescendantEntitiesWithTag(my_tag{})
Parent := TaggedEntity.GetParent[]
Auf die gleiche Weise kannst du einer Entität Komponenten hinzufügen, indem du AddComponents() aufrufst und die Liste der Komponenten übergibst, die du hinzufügen möchtest. Du kannst eine Entität auch entfernen, indem du RemoveFromParent() auf der Entität aufrufst, und du kannst eine Komponente aus einer Entität entfernen, indem du RemoveFromEntity() auf der Komponente aufrufst. Entfernte Entitäten und Komponenten können mit AddEntities() bzw. AddComponents() wieder zur Szene hinzugefügt werden. Beachte, dass du die Parent-Entität von entfernten Komponenten nicht ändern kannst, wenn du sie der Szene wieder hinzufügst.
Prefabs
Prefabs, die du in deinem Projekt erstellst, werden in der Datei Assets.digest.verse in deinem Projekt als Klasse für Verse verfügbar gemacht. Entitäten und Komponenten, die in deinem Prefab definiert sind, sind in Verse über die Aufrufe GetEntities() und GetComponents() an ein Prefab zugänglich.
Du kannst Instanzen deiner Prefabs spawnen, indem du die Prefab-Klasse instanziierst und sie zu einer Entität in der Szene hinzufügst. In dem folgenden Beispiel erstellt die Verse-Komponente im Editor eine Instanz des Prefab mit der Bezeichnung loop_disappearing_platform_prefab und fügt sie zur Szene hinzu.
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically cancelled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void =
if:
SimulationEntity := Entity.GetSimulationEntity[]
then:
# Create an instance of the disappearing on loop platform from its prefab.
DisappearingPlatform:disappearing_platform_prefab = disappearing_platform_prefab{}
Best Practices und Tipps
Wenn du eigene Komponenten in Verse erstellst, solltest du die folgenden optimalen Vorgehensweisen und Tipps berücksichtigen:
Verse-Komponenten, die sich auf andere Komponenten stützen, sollten im Allgemeinen zur gleichen Entität gehören.
Komponenten geben sowohl Pre- als auch Post-Physik-Tick-Events frei, die in jedem Frame auftreten. Das ist praktisch, wenn du bestimmte Logiken ausführen musst, bevor die Physik angewendet wird, wie zum Beispiel das Ändern der Transformation, oder nach ihrer Anwendung, wie das Lesen der Positionen von Objekten nach der Physik.
Wenn du die Pre- oder Post-Physik-Events nicht speziell benötigst, solltest du weiterhin die Verse-Gleichzeitigkeitsausdrücke verwenden, um den Zeitfluss basierend auf einer bestimmten Logik zu steuern. Weitere Informationen findest du unter Zeitfluss und Gleichzeitigkeit.
Die Lebenszeitfunktionen der Komponenten unterscheiden sich von den Gerätelebenszeiten. Die Komponentenlogik läuft im Bearbeitungs- und Spielmodus. Wenn du möchtest, dass deine Komponentenlogik nur beim Spielstart ausgeführt wird, kannst du Prefabs in der Funktion
OnBegin()eines Verse-Geräts spawnen.