Znaczniki celów są używane w wielu grach, aby poprowadzić gracza do następnego celu lub miejsca na mapie. W tym samouczku dowiesz się, jak utworzyć znacznik celu wielokrotnego użytku za pomocą urządzenia znacznika na mapie i Verse.
Używane funkcje języka Verse
-
Metoda rozszerzająca: Specjalny typ funkcji, która działa jak członek istniejącej klasy lub typu, ale nie wymaga tworzenia nowego typu lub podklasy. Z tym poradnikiem utworzysz metodę rozszerzającą dla struktury.
-
nazwany argument: Argument, który jest przekazywany do wywołania funkcji z podaną nazwą parametru.
Używane interfejsy API Verse
-
Rekwizyt API: Rekwizyt API dostarcza metod umożliwiających ruch rekwizytów.
-
Edytowalne właściwości: Kilka właściwości służy zarówno do odwoływania się do urządzeń, jak i aktualizowania wartości zmiennych w celu szybkiego testowania.
Instrukcje
Wykonaj poniższe kroki, aby dowiedzieć się, jak skonfigurować pojedyncze urządzenie znacznika celu, które może poruszać się do wielu celów lub miejsc na mapie. Kompletne skrypty znajdują się na końcu niniejszego poradnika.
Ustawianie poziomu
W tym przykładzie wykorzystano następujące rekwizyty i urządzenia:
-
1 x rekwizyt budowli: Rekwizyt, który będzie używany do poruszania urządzeniem znacznika na mapie.
-
1 x urządzenie znacznika na mapie: Urządzenie wyświetlające niestandardowe znaczniki na minimapie i mapie ogólnej.
-
1 x urządzenie panelu startowego gracza: Dodaj je w pobliżu rekwizytu, aby gracz odradzał się przy nim.
Korzystanie z API rekwizytu
Pierwszym krokiem do wprowadzenia urządzenia w ruch za pomocą Verse jest przeniesienie rekwizytu za pomocą API rekwizytu. Wykonaj poniższe kroki, aby poruszać rekwizytem w obrębie poziomu.
-
Utwórz nowe urządzenie Verse o nazwie objective_coordinator_device.
-
Pod domyślnymi wyrażeniami
using
na górze pliku Verse, dodaj wyrażenieusing
dla modułuSpatialMath
. Ten moduł zawiera kod, który będzie używany do przenoszenia rekwizytów.using { /UnrealEngine.com/Temporary/SpatialMath }
-
Dodaj dwie edytowalne właściwości:
-
Stała
creative_prop
o nazwieRootProp
do przechowywania odwołania do ruchomego rekwizytu. -
Stała
transform
o nazwieDestination
do przechowywania lokalizacji, do której rekwizyt będzie przenoszony.
objective_coordinator_device<public> := class<concrete>(creative_device): @editable RootProp<public> : creative_prop = creative_prop{} @editable Destination<public> : transform = transform{}
-
-
Jeśli uruchomisz ten kod i przeciągniesz urządzenie objective_coordinator_device na swój poziom, zobaczysz dwie właściwości w panelu Szczegóły.
-
Metoda
TeleportTo[]
jest tym, co faktycznie przenosi rekwizyt. Wywołaj ją w wyrażeniuif
i użyj nawiasów kwadratowych zamiast okrągłych, ponieważTeleportTo[]
jest wyrażeniem zawodnym. Parametrif
tworzy kontekst niepowodzenia.if(RootProp.TeleportTo[Destination.Translation, Destination.Rotation]): Print("Przeniesiono rekwizyt") else: Print("Nie udało się przenieść rekwizytu")
-
Argumentami dla
TeleportTo[]
są Translacja i Obrót. Oba pochodzą z właściwości Cel. -
Wróć do edytora i przeciągnij rekwizyt z Fortnite > Galerie > Rekwizyty w Przeglądarce zawartości. Rekwizyt użyty w tym poradniku nazywa się Coastal Buoy 02B (Boja przybrzeżna 02B), ale każda pozycja z folderu Rekwizyty powinna działać.
-
Wybierz swoje urządzenie koordynatora celu w panelu Outliner. W panelu Szczegóły ustaw w pozycji RootProp swój rekwizyt. W tym przykładzie w pozycji RootProp ustawiona jest boja przybrzeżna 02B.
-
W panelu Szczegóły rozwiń Cel. Cel ma przypisany typ
transform
, dlatego składa się z komponentów Skala, Obrót i Translacja. Aby przenieść rekwizyt, wystarczy zmienić ustawienie Translacja, więc je rozwiń. Ustaw pole kończące się na X na 5000.0.Podczas testowania kodu dobrym rozwiązaniem jest wprowadzanie dużych zmian w wartościach, aby efekty były oczywiste. Niewielkie zmiany mogą utrudnić stwierdzenie, czy kod robi to, czego oczekujesz.
using { /Verse.org/Simulation } using { /Fortnite.com/Devices } using { /UnrealEngine.com/Temporary/SpatialMath } objective_coordinator_device<public> := class<concrete>(creative_device): @editable RootProp<public> : creative_prop = creative_prop{} # Gdzie zostanie przeniesiony znacznik @editable Destination<public> : transform = transform{} OnBegin<override>()<suspends> : void = if(RootProp.TeleportTo[Destination.Translation, Destination.Rotation]): Print("Przeniesiono rekwizyt") else: Print("Nie udało się przenieść rekwizytu")
-
Kliknij Verse, następnie Skompiluj kod Verse, po czym kliknij Uruchom sesję. Na koniec kliknij Uruchom grę. Rekwizyt powinien się poruszyć.
Klasa bazowa i struktury
Masz teraz rekwizyt poruszający się po twoim poziomie, ale prawdziwym celem jest poruszanie się urządzenia znacznika na mapie, aby gracze mogli go użyć jako punktu nawigacyjnego. Wykonaj poniższe kroki, aby dodać rekwizyt budowli i urządzenie znacznika na mapie do poziomu, a następnie dołączyć je do rekwizytu budowli.
-
Kliknij prawym przyciskiem myszy wewnątrz Przeglądarki zawartości, aby otworzyć menu kontekstowe.
-
Wybierz opcję Klasa Blueprint z menu kontekstowego.
-
W oknie Wybierz klasę bazową kliknij Rekwizyt budowli.
-
W Przeglądarce zawartości pojawi się nowa klasa Blueprint. Zmień jej nazwę na BuildingProp.
-
Przeciągnij rekwizyt budowli na swój poziom. Ten rekwizyt nie ma siatki, więc zobaczysz tylko jego gizmo przekształcenia.
-
W panelu Outliner przeciągnij urządzenie znacznika na mapie na rekwizyt budowli. W ten sposób rekwizyt budowli staje się klasą bazową urządzenia znacznika na mapie. Teraz, gdy rekwizyt budowli się porusza, urządzenie znacznika na mapie porusza się wraz z nim.
Wiesz już, jak utworzyć urządzenie za pomocą Verse, ale możesz także tworzyć pliki Verse, które nie mają własnych urządzeń.
-
Utwórz nowy plik Verse i nazwij go objective_marker. Ten plik nie utworzy urządzenia. Zamiast tego będzie zawierał definicję
struct
, która będzie dostępna dla urządzenia Verse utworzonego wcześniej. -
Zacznij od zadeklarowania elementu
struct
o nazwie objective_marker. Będzie on miał dwa elementy członkowskie:RootProp
iMapIndicator
. Oba powinny mieć specyfikator@editable
.objective_marker<public> := struct<concrete>: @editable RootProp<public> : creative_prop = creative_prop{} @editable MapIndicator<public> : map_indicator_device = map_indicator_device{}
Metody rozszerzające i argumenty nazwane
Zadeklaruj pojedynczą metodę, MoveMarker
, która przesunie element RootProp
i dołączone do niego urządzenie znacznika na mapie. Metoda ta wprowadza dwie cechy języka: metody rozszerzające i argumenty nazwane.
(Marker : objective_marker).MoveMarker<public>(Transform : transform, ?OverTime : float)<suspends> : void =
-
Metody rozszerzające: Dodajesz metodę
MoveMarker()
do strukturyobjective_marker
. Metoda rozszerzająca jest deklarowana przy użyciu nawiasów otaczających identyfikator i typ oddzielone dwukropkiem. W tym przypadku:(Marker : objective_marker)
. -
Argumenty nazwane: Drugi argument
?OverTime
używa?
do wskazania, że musi być nazwany w wywołaniu funkcjiMoveMarker
. Pomaga to każdemu twórcy czytającemu lub piszącemu wywołanieMoveMarker
zrozumieć, co robi argumentfloat
.
Funkcja MoveMarker()
wywoła jedną z dwóch metod z API rekwizytu: TeleportTo[]
, która została użyta wcześniej, lub MoveTo()
. Utwórz blok if..else
, aby sprawdzić, czy parametr OverTime
jest większy niż 0.0
. Jeśli tak, wywołaj MoveTo()
. Spowoduje to, że zamiast natychmiastowej teleportacji, twój cel przeniesiony zostanie do następnej lokalizacji w określonym przez ciebie czasie.
(Marker : objective_marker).MoveMarker<public>(Transform : transform, ?OverTime : float)<suspends> : void =
if (OverTime > 0.0):
Marker.RootProp.MoveTo(Transform.Translation, Transform.Rotation, OverTime)
else:
if:
Marker.RootProp.TeleportTo[Transform.Translation, Transform.Rotation]
Jeśli teraz skompilujesz kod, powinno się to udać, ale nie zobaczysz nowego urządzenia w folderze CreativeDevices w Przeglądarce zawartości. Dzieje się tak, ponieważ objective_marker jest obiektem struct
, a nie klasą dziedziczącą po creative_device
.
Aktualizacja urządzenia koordynatora celu
Teraz, gdy masz nowy typ do odwołania, musisz zaktualizować urządzenie objective_coordinator_device, aby się do niego odwoływać.
-
Usuń właściwość
RootProp
i zastąp ją właściwością o nazwiePickupMarker
typuobjective_marker
. Jest to typ utworzony przez ciebie. -
Funkcja
MoveMarker()
wymaga argumentu typufloat
, więc utwórz go jako edytowalną właściwość o nazwieMoveTime
. -
Usuń wywołanie
TeleportTo[]
. Zamiast tego wywołaj metodęMoveMarker()
utworzoną dlaobjective_marker
. Wymaga ona podania argumentu?OverTime
.
objective_coordinator_device<public> := class<concrete>(creative_device):
@editable
PickupMarker<public> : objective_marker = objective_marker{}
# Gdzie zostanie przeniesiony znacznik
@editable
Destination<public> : transform = transform{}
# Ile czasu powinno zająć znacznikowi dotarcie do nowej lokalizacji
@editable
MoveTime<public> : float = 0.0
OnBegin<override>()<suspends> : void =
PickupMarker.MoveMarker(Destination, ?OverTime := MoveTime)
Skompiluj ten kod i sprawdź szczegóły urządzenia koordynatora celu. Widoczne powinny być właściwości PickupMarker i MoveTime, a właściwość PickupMarker powinna zawierać elementy RootProp i MapIndicator.
-
Ustaw pole RootProp na BuildingProp, a pole MapIndicator na Urządzenie znacznika na mapie.
-
Skompiluj swój kod i kliknij Uruchom sesję. Na minimapie powinien pojawić się znacznik, który poruszy się wkrótce po rozpoczęciu gry. Wypróbuj go z
MoveTime
ustawionym na różne wartości, w tym0.0
. Zastanów się, który ruch byłby najlepszy dla różnych scenariuszy.
GetPlayers() i ActivateObjectivePulse()
Istnieje sposób na zapewnienie graczom dodatkowej pomocy w dotarciu do następnego celu. Nazywa się on impulsem celu. Gdy funkcja ta jest aktywna, pokazuje kropkowaną linię, która przesuwa się od gracza w kierunku urządzenia znacznika na mapie. Postępuj zgodnie z poniższymi instrukcjami, aby dodać impuls celu do urządzenia koordynatora celu.

Metoda potrzebna do aktywacji impulsu celu nazywa się ActivateObjectivePulse()
i wymaga jednego argumentu typu agent
. Zacznij od stworzenia metody pobierającej instancję agent
reprezentującą postać gracza.
-
Zadeklaruj funkcję o nazwie
FindPlayer()
ustawioną na<private>
, z wartością zwracanąvoid
. -
Pobierz tablicę z wszystkimi graczami na twoim poziomie za pomocą
Self.GetPlayspace().GetPlayers()
. Przechowaj tablicę w zmiennej o nazwieAllPlayers
.FindPlayer<private>() : void = AllPlayers := Self.GetPlayspace().GetPlayers()
-
Aby uzyskać odwołanie tylko do jednego gracza na poziomie, przypisz pierwszy element tablicy do jego własnej zmiennej. Uzyskanie dostępu do tablicy jest wyrażeniem zawodnym, więc umieść je w wyrażeniu
if
.if (FirstPlayer := AllPlayers[0]):
-
Przypisanie
player
do zmiennej może się nie powieść, dlatego należy użyć zmiennej typuoption
podczas odwoływania się do gracza w kodzie. Zadeklaruj opcjonalną zmienną gracza?player
. Powinna być ona zgodna z innymi zmiennymi elementami członkowskimi.objective_coordinator_device<public> := class<concrete>(creative_device): var PlayerOpt<private> : ?player = false @editable PickupMarker<public> : objective_marker = objective_marker{} # Gdzie zostanie przeniesiony znacznik @editable Destination<public> : transform = transform{} # Ile czasu powinno zająć znacznikowi dotarcie do nowej lokalizacji @editable MoveTime<public> : float = 0.0
-
Ustaw nową zmienną i utwórz blok
else
z wyrażeniemPrint()
, które poinformuje cię, jeśli gracz nie został znaleziony. Twoja funkcjaFindPlayer()
jest teraz ukończona.FindPlayer<private>() : void = # Gra odbywa się w trybie jednego gracza, dlatego pierwszy gracz [0] # powinien być jedynym dostępnym graczem. AllPlayers := Self.GetPlayspace().GetPlayers() if (FirstPlayer := AllPlayers[0]): set PlayerOpt = option{FirstPlayer} Print("Znaleziono gracza") else: # Zarejestruj błąd, jeśli nie można znaleźć gracza. Print("Nie znaleziono prawidłowego gracza")
Wracając do funkcji OnBegin()
, musisz wprowadzić jeszcze dwie zmiany:
-
Wywołaj funkcję
FindPlayer()
.OnBegin<override>()<suspends> : void = FindPlayer()
-
Po wywołaniu
MoveMarker()
użyj kolejnego wyrażeniaif
, aby ustawić opcjonalną zmienną gracza na nową zmienną i przekaż ją jako argument doPickupMarker.MapIndicator.ActivateObjectivePulse()
.if (FoundPlayer := PlayerOpt?): PickupMarker.MapIndicator.ActivateObjectivePulse(FoundPlayer)
Jeśli uruchomisz teraz swój kod, zobaczysz impuls celu skierowany od twojej postaci do lokalizacji znacznika celu na poziomie!
Kompletne skrypty
Objective_marker.verse
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Fortnite.com/Devices/CreativeAnimation }
objective_marker<public> := struct<concrete>:
# Rekwizyt, który zostanie przeniesiony
@editable
RootProp<public> : creative_prop = creative_prop{}
# Element podrzędny rekwizytu, który będzie się z nim poruszał
@editable
MapIndicator<public> : map_indicator_device = map_indicator_device{}
# Metoda rozszerzająca dla objective_marker
# ? przed OverTime określa go jako argument nazwany
(Marker : objective_marker).MoveMarker<public>(Transform : transform, ?OverTime : float)<suspends> : void =
if (OverTime > 0.0):
Marker.RootProp.MoveTo(Transform.Translation, Transform.Rotation, OverTime)
else:
if:
Marker.RootProp.TeleportTo[Transform.Translation, Transform.Rotation]
Objective_coordinator_device.verse
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /Fortnite.com/Playspaces }
using { /UnrealEngine.com/Temporary/SpatialMath }
objective_coordinator_device<public> := class<concrete>(creative_device):
var PlayerOpt<private> : ?player = false
@editable
PickupMarker<public> : objective_marker = objective_marker{}
# Gdzie zostanie przeniesiony znacznik
@editable
Destination<public> : transform = transform{}
# Ile czasu powinno zająć znacznikowi dotarcie do nowej lokalizacji
@editable
MoveTime<public> : float = 0.0
OnBegin<override>()<suspends> : void =
FindPlayer()
PickupMarker.MoveMarker(Destination, ?OverTime := MoveTime)
# jeśli Player nie ma wartości false, aktywuj impuls celu dla znalezionego gracza
if (FoundPlayer := PlayerOpt?):
PickupMarker.MapIndicator.ActivateObjectivePulse(FoundPlayer)
FindPlayer<private>() : void =
# Gra odbywa się w trybie jednego gracza, dlatego pierwszy gracz [0]
# powinien być jedynym dostępnym graczem.
AllPlayers := Self.GetPlayspace().GetPlayers()
if (FirstPlayer := AllPlayers[0]):
set PlayerOpt = option{FirstPlayer}
Print("Znaleziono gracza")
else:
# Zarejestruj błąd, jeśli nie można znaleźć gracza.
Print("Nie znaleziono prawidłowego gracza")
Praca własna
Pamiętaj, że napisany tutaj kod ruchu działa dla każdego rekwizytu. Jeśli ruchomy rekwizyt stanie się elementem nadrzędnym urządzenia, to urządzenie będzie poruszać się wraz z nim. Spróbuj poruszać innymi rekwizytami i urządzeniami oraz sprawdź, czy uda ci się wymyślić inne gry, w których można by wykorzystać te efekty.
Następne kroki
Jeśli korzystasz z tego poradnika do stworzenia gry bazującej na odbiorze i dostarczeniu, następnym krokiem jest nauczenie się, jak utworzyć funkcję licznika czasu odliczania.