Za pomocą Verse możesz utworzyć własne komponenty, które dodasz do jednostek. Dzięki niestandardowym komponentom Verse możesz spawnować i usuwać jednostki ze sceny, dodawać komponenty do jednostek lub usuwać je z nich, tworzyć własne zachowania, takie jak znikająca jednostka w pętli dowolne inne, które przychodzą ci do głowy!
Tworzenie nowego komponentu Verse
Nowy komponent Verse możesz utworzyć z pliku szablonu Verse.
Tworzenie nowego komponentu Verse:
W panelu Szczegóły swojej jednostki wybierz kolejno opcje Dodaj komponent > Nowy komponent Verse.
Nowy komponent Verse można również utworzyć, dodając nowy plik Verse za pomocą Eksploratora Verse.
Z listy szablonów kodu Verse wybierz opcję Komponent Scene Graph.
W polu Nazwa komponentu ustaw nazwę swojego komponentu utworzonego w Verse. W tym przykładzie komponent otrzymał nazwę
my_verse_component.Kliknij opcję Utwórz, aby utworzyć plik komponentu Verse. Twój komponent utworzony w Verse będzie teraz widoczny na liście komponentów po wybraniu opcji dodania komponentu do twojej jednostki.
Można dodać tylko jedną z podanych klas lub podklas komponentów. Na przykład w jednostce możesz mieć tylko jeden element mesh_component. Dotyczy to również podklas komponentów, co oznacza, że jeśli dodasz capsule_light_component do jednostki, nie możesz również dodać rect_light_component, ponieważ obie to podklasy z light_component. To samo ograniczenie obowiązuje w przypadku komponentów niestandardowych utworzonych w Verse.
Czas życia komponentów
Komponenty dodawane do jednostek, dodawane do sceny i uruchamiane w symulacji przechodzą przez szereg funkcji czasu życia. Twoje komponenty utworzone w Verse powinny zastępować te metody konfiguracji i uruchamiania swoich symulacji.
W trakcie wyłączania komponentu będzie on przechodził przez szereg wersji wyłączeniowych tych funkcji, umożliwiając wyczyszczenie wszelkich zachowanych stanów komponentu przed jego usunięciem.
Poniżej znajduje się lista stanów życia komponentu:
Rozpoczęto
AddedToScene
BeginSimulation
EndSimulation
RemovingFromScene
Uninitializing
Funkcje czasu życia komponentu różnią się od czasów życia urządzenia. Logika komponentów działa zarówno w trybie edycji, jak i odtwarzania. Każde dodane zachowanie będzie wykonywane niezwłocznie po uruchomieniu Twojej sesji. Jeśli chcesz, aby logika komponentu była wykonywana tylko w momencie rozpoczęcia gry, możesz zespawnować gotowce w funkcji OnBegin() urządzenia Verse.
Wysyłanie zapytań o jednostki i komponenty za pomocą Verse
Jednostki i komponenty można wyszukiwać w kodzie Verse na wiele sposobów. Sposób tworzenia zapytań oraz programowania funkcjonalności w kodzie Verse zależy od struktury jednostek i komponentów.
Tworzenie zapytań o jednostki i komponenty w Verse wymaga rozpoczęcia od jednostki, a następnie zwrócenia jednostek zagnieżdżonych w hierarchii nad nią lub pod nią. Można tworzyć zapytania o bezpośredni element nadrzędny lub elementy podrzędne jednostki, a także o wszystkie jej elementy nadrzędne i potomne.
Pobieranie jednostek według typu komponentu
Wszystkie jednostki zawierające komponent określonego typu możesz wyszukać, wywołując funkcję dla jednostki, o którą chcesz zapytać. Jeśli jednostka, o którą tworzysz zapytanie, jest jednostką symulacyjną, zwraca wszystkie jednostki zawierające komponenty danego typu w scenie.
W poniższym przykładzie komponent Verse pobiera wszystkie jednostki, które mają dołączony komponent light_component. Dla każdej znalezionej jednostki spawnuje się komponent particle_sytem_component, który zostaje dołączony do takiej jednostki. W tym przykładzie BlowingParticles jest emiterem Niagara, do którego istnieje odwołanie w pliku Assets.digest.Verse.
Komponent light_component jest nadklasą wszystkich różnych typów komponentów oświetlenia, które możesz dodać do swoich jednostek. W poniższym zapytaniu użyto LightComponent do znalezienia jednostek z dowolnym rodzajem komponentu oświetlenia.
# 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.
Poniższy przykład pokazuje, jak stworzyć zapytanie o wszystkie jednostki, w których pod jednostką, do której komponent Verse jest dołączony, zagnieżdżone są systemy cząsteczkowe, przy użyciu funkcji FindDescendantEntitiesWithComponent(). W ten sam sposób możesz również pobrać wszystkie jednostki nadrzędne z konkretnym komponentem przy użyciu FindAncestorEntitiesWithComponent().
# 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.
Jeśli musisz wykonać zapytanie o jednostki na poziomie całej sceny, możesz to zrobić, pobierając jednostkę symulacyjną i wykonując na niej zapytania. Proces ten rozpoczyna się od szczytu struktury jednostek i pozwala wyszukać wszystkie zagnieżdżone jednostki spełniające kryteria wyszukiwania. Aby uzyskać dostęp do jednostki symulacyjnej, wywołaj funkcję zawodną GetSimulationEntity[] na jednostce, do której dołączony jest komponent Verse.
# 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:
Ciągłe wyszukiwanie komponentu Verse w górę i w dół drzewa jednostek bywa kosztowne. Jeśli zachowanie jest zależne od konkretnej struktury jednostek, nawet drobne zmiany w tej strukturze mogą spowodować niezamierzone zmiany zachowania lub całkowicie uniemożliwić jego działanie.
Z drugiej strony oznacza to również, że komponent może mieć mniejsze wymagania konfiguracyjne, ponieważ wystarczy, że dodasz komponent Verse, a otrzymasz prawidłową strukturę jednostki. Tworząc jednostki i opracowując logikę swojego komponentu Verse, warto mieć na uwadze te kwestie.
Zapoznaj się z modułem SceneGraph, aby poznać wszystkie sposoby pracy z jednostkami i komponentami w Verse. Poniżej opisano popularne sposoby tworzenia zapytań o jednostki i komponenty w kodzie.
Pobieranie komponentów jednostki
Za pomocą Verse możesz pobrać komponent określonego typu należące do jednostki, wywołując GetComponent[]. Jest to przydatne przy tworzeniu niestandardowej logiki zależnej od innego zachowania komponentu. Na przykład użycie sound_component do odtwarzania dźwięku zależnie od koloru światła lub uzyskanie particle_system_component w celu zastosowania efektów zależnie od tego, czy jednostka znajduje się w określonej strefie.
W poniższym przykładzie komponent Verse sprawia, że platforma wielokrotnie pojawia się i znika w pętli. Efekt ten uzyskuje się poprzez pobieranie komponentu siatki do jednostki i wyłączanie go, a następnie ponowne włączanie po ustawionym czasie trwania.
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
W innym przykładzie komponent Verse pobiera przypisany do jednostki element light_component i zmienia jej kolor na ciemnopomarańczowy. Komponent light_component jest nadklasą wszystkich rodzajów oświetlenia, jakie możesz dodać do swoich jednostek, więc ten przykład pozwoli wyszukać dowolny komponent, który będzie jej podklasą.
# 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:
Pobieranie wszystkich komponentów
Za pomocą funkcji GetComponents() można zwrócić wszystkie komponenty w jednostce. Ponieważ kod nie wie, jakiego typu są to komponenty, możesz używać rzutowania, aby wykonywać różne operacje w oparciu o typ każdego komponentu. W poniższym przykładzie zostaje pobrana tablica wszystkich komponentów należących do jednostki i następuje próba rzutowania ich na typ enableable. Jeśli rzutowanie się powiedzie, komponent implementuje interfejs enableable. Następnie wyłącza każdy komponent implementujący ten interfejs.
# 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
Wyszukiwanie jednostek według tagów rozgrywki
Do jednostek można dodać komponent tagu, aby wyszukać konkretne jednostki w obrębie sceny, podobnie jak w przypadku dodawania tagów rozgrywki do aktorów. Jest to przydatne przy wybieraniu jednostek, nad którymi chce się pracować, zamiast bazowania na rzeczach zmiennych, takich jak komponenty, które zawiera jednostka lub jej umiejscowienie w scenie.
Bazowanie na rzeczach zmiennych może powodować niepożądane zachowanie w grze, gdy w projekcie zmieni się lub doda nowe jednostki. Możesz dodać tagi w edytorze, dodając tag_component do swojej jednostki i wybierając tagi z menu rozwijanego Tagi lub w kodzie Verse za pomocą funkcji AddTag().
Poniższy przykład wysyła zapytanie do jednostki symulacyjnej o wszystkie elementy podrzędne oznaczone tagiem my_tag w ich tag_component.
# 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:
Znajdowanie jednostek z nakładającymi się jednostkami
Wolumeny kolizji to wolumeny, które reprezentują kształty siatek kolizji. Można ich użyć do wyszukania nakładających się obiektów w ramach określonego kształtu, na przykład niszczenia obiektów, które znajdą się zbyt blisko wieży, lub wykrywania sytuacji, w których piłka wpada do bramki. W Verse można użyć funkcji FindOverlapHits(), aby wyszukać wszystkie jednostki w określonym obszarze.
Obszarem tym może być sama jednostka, określona przestrzeń kolizji, taka jak kula lub sześcian, lub pozycja, z której ma być symulowana jednostka. Następnie jest zwracana lista elementów overlap_hit. Każdy element overlap_hit dostarcza informacje o komponencie lub wolumenie, na który nakłada się wolumen źródłowy. Możesz wykonać zapytanie o te komponenty, aby znaleźć powiązaną z nimi jednostkę.
Poniższy przykład tworzy kulę o promieniu 256.0 jednostek wyśrodkowanych w przekształceniu jednostki. Następnie wyszukuje wszystkie obszary nachodzące na siebie w obrębie kuli, zwracając listę overlap_hit. Każde overlap_hit jest komponentem lub wolumenem, dlatego możesz wysłać zapytanie do TargetComponent lub TargetVolume, aby dowiedzieć się, jaki to typ. Jeśli overlap_hit jest komponentem, kod pobiera wówczas jednostkę komponentu. Na końcu sprawdza, czy jednostka zawiera komponent oświetlenia. Jeśli tak, zmienia kolor światła na niebieski. Przy wystarczająco dużym wolumenie możesz zmienić kolor każdego światła na swojej wyspie za pomocą zaledwie kilku linijek kodu!
# 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
Ten kod symuluje nakładający się wolumen collision_sphere oznaczony okręgiem o promieniu 256.0 jednostek wyśrodkowanych na jednostce sześcianu, zwracając wszelkie komponenty lub wolumeny, na które nakłada się kula. Następnie pobiera jednostkę nadrzędną każdego nakładającego się komponentu i przełącza wszystkie komponenty światła na niebieski. Sześcian znajduje się wewnątrz collision_sphere w momencie rozpoczęcia nakładania, dlatego zostanie uwzględniony na liście overlap_hits, a także zmieni kolor na niebieski. Zwróć uwagę, że skrajna prawa czerwona jednostka stożka znajduje się poza collision_sphere, więc nie będzie się nakładać i nie zmieni koloru na niebieski.
Znajdowanie jednostek z przesunięciem
Innym ważnym sposobem tworzenia zapytań o jednostki jest przesunięcie. Przesunięcie odnosi się do przesuwania obiektu na ustaloną odległość wzdłuż określonego wektora. Przykładem może być przemieszczenie bloku po platformie, aby zepchnąć graczy w szczelinę, lub wystrzelenie pocisku prosto do przodu, aby zniszczyć ścianę.
W Verse za pomocą funkcji FindSweepHits() można symulować przesunięcie, aby utworzyć zapytania o kolizje między obiektami. Funkcja ta wykorzystuje wektor przemieszczenia do symulacji przesunięcia obiektu wzdłuż. Przesunięcie można przeprowadzić z użyciem jednostki nadrzędnej lub określonego wolumenu kolizji, a następnie określić początkowe przekształcenie globalne, od którego ma się rozpocząć przesunięcie.
Funkcja FindSweepHits() zwraca listę elementów sweep_hit. Każdy element sweep_hit dostarcza te same informacje co element overlap_hit, takie jak informacje o trafieniu komponentu lub wolumenu oraz informacje o wolumenie źródłowym lub komponencie wykonującym omiatanie. Ponadto dostarcza informacje o pozycji kontaktu, normalnej, normalnej powierzchni i odległości wzdłuż omiatania, w przypadku którego nastąpiło trafienie.
W poniższym przykładzie zastosowano jednostkę i wywołano FindSweepHit(), przekazując wektor o długości 1000.0 dla wartości do przodu. Następnie kod symuluje, jakie kolizje wystąpiłyby, gdyby jednostka została przesunięta o 1000.0 jednostek w dodatnim kierunku do przodu, i zwraca listę sweep_hit.
Każde sweep_hit jest komponentem lub wolumenem, dlatego możesz wysłać zapytanie do TargetComponent lub TargetVolume, aby dowiedzieć się, jaki to typ. Jeśli trafienie jest komponentem, następnie kod pobierze jednostkę nadrzędną komponentu. Na końcu sprawdza, czy jednostka zawiera komponent oświetlenia. Jeśli tak, zmienia kolor światła na niebieski.
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:
Ten kod symuluje przesunięcie jednostki sześcianu o 1000.0 jednostek w dodatnim kierunku X, zwracając wszystkie komponenty, z którymi jednostka się nakłada. Następnie pobiera jednostkę nadrzędną nakładającego się komponentu i przełącza wszystkie komponenty światła na niebieski. Zwróć uwagę, że lista trafień nie obejmuje jednostki wykonującej przesunięcie, więc sam sześcian nie zmieni koloru na niebieski. Czerwony stożek znajdujący się najbardziej na prawo również nie zmieni koloru na niebieski, ponieważ znajduje się poza przesunięciem.
Następny przykład jest podobny do poprzedniego, z tą różnicą, że najpierw konstruuje wolumen collision_box, a następnie wykorzystuje go do przesunięcia o 1000.0 jednostek w dodatnim kierunku do przodu, zaczynając od środka jednostki sześcianu. Sześcian znajduje się wewnątrz collision_box w momencie rozpoczęcia przesunięcia, dlatego zostanie uwzględniony na liście sweep_hits i również zmieni kolor na niebieski.
# 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)
Ten kod symuluje przesuwanie wolumenu collision_box oznaczonego żółtym kwadratem o 1000.0 jednostek w dodatnim kierunku do przodu, zwracając wszystkie komponenty, z którymi się nakłada. Następnie pobiera jednostkę nadrzędną nakładającego się komponentu i przełącza wszystkie komponenty światła na niebieski. Sześcian znajduje się wewnątrz collision_box w momencie rozpoczęcia przesunięcia, dlatego zostanie uwzględniony na liście sweep_hits i zmieni kolor na niebieski. Czerwony stożek znajdujący się najbardziej na prawo nie zmieni koloru na niebieski, ponieważ znajduje się poza przesunięciem.
Spawnowanie i usuwanie jednostek za pomocą Verse
Jednostkę można usunąć ze sceny, wywołując dla niej funkcję RemoveFromParent(). Aby dodać nową lub uprzednio usuniętą jednostkę do sceny, musisz wywołać funkcję AddEntities() dla jednostki, która staje się jednostką nadrzędną.
W poniższym przykładzie komponent Verse wyszukuje wszystkie jednostki oznaczone tagiem rozgrywki my_tag. Każdą znalezioną jednostkę usuwa z jej jednostki nadrzędnej, co powoduje usunięcie jej ze sceny, a po pięciu sekundach z powrotem dodaje tę samą jednostkę do jej jednostki nadrzędnej, aby ponownie została zespawnowana w scenie.
# 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[]
W ten sam sposób możesz dodawać komponenty do jednostki, wywołując AddComponents() i przekazując listę komponentów, które chcesz dodać. Jednostkę można również usunąć, wywołując dla niej funkcję RemoveFromParent(), albo usunąć komponent z jednostki, wywołując funkcję RemoveFromEntity() dla komponentu. Usunięte jednostki i komponenty można ponownie dodać z powrotem do sceny za pomocą odpowiednio AddEntities() i AddComponents(). Zwróć uwagę, że nie możesz zmienić jednostki nadrzędnej usuniętych komponentów, które z powrotem dodasz do sceny.
Gotowce
Gotowce utworzone w projekcie są widoczne jako klasa w Verse w pliku projektu Assets.digest.verse. Jednostki i komponenty zdefiniowane w gotowcu są dostępne w języku Verse poprzez wywołania GetEntities() i GetComponents() w gotowcu.
Instancje swoich gotowców możesz zespawnować poprzez utworzenie instancji klasy gotowca i dodanie jej do jednostki w scenie. W poniższym przykładzie komponent Verse tworzy instancję gotowca o nazwie loop_disappearing_platform_prefab w edytorze i dodaje ją do sceny.
# 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{}
Dobre praktyki i porady
Podczas tworzenia własnych komponentów w Verse warto mieć na uwadze poniższe dobre praktyki i porady:
Komponenty Verse, które bazują na innych komponentach powinny zasadniczo należeć do tej samej jednostki.
Komponenty ujawniają zdarzenia tyknięcia przed wykonaniem fizyki i po jej wykonaniu, które występują co klatkę. Jest to przydatne, gdy zachodzi konieczność wykonania określonej logiki przed zastosowaniem fizyki, na przykład zmodyfikowania przekształcenia i dalszych czynności, aby przykładowo odczytać położenia obiektów po zastosowaniu fizyki.
Jeśli nie potrzebujesz korzystać ze zdarzeń przed wykonaniem fizyki i po jej wykonaniu, najlepiej dalej używać wyrażeń współbieżności Verse do sterowania przepływem czasu w oparciu o określoną logikę. Szczegółowe informacje zawiera temat Przepływ czasu i współbieżność.
Funkcje czasu życia komponentu różnią się od czasów życia urządzenia. Logika komponentów działa zarówno w trybie edycji, jak i odtwarzania. Jeśli chcesz, aby logika komponentu była wykonywana tylko w momencie rozpoczęcia gry, możesz zespawnować gotowce w funkcji
OnBegin()urządzenia Verse.