Scene Graph offre diversi modi per creare un gameplay flessibile e dinamico attraverso le molte interazioni diverse tra entità e componenti. Questo modello mostra alcuni dei modi in cui puoi manipolare le entità in fase di runtime attraverso la generazione e la rimozione di entità e come puoi utilizzare gli eventi dei componenti e l'esecuzione di query per creare comunicazione tra entità e costruire elementi di gameplay riutilizzabili.
Generazione e rimozione dei prefab
Ad esempio, dai un'occhiata alla pedana interattiva nella seconda sala. Quando il giocatore sale sulla pedana, una lanterna prefab viene aggiunta alla scena e viene rimossa quando il giocatore esce dalla pedana.
Apri SpawnPrefabDevice.verse per vedere come funziona questo codice. Si tratta di un dispositivo Fortnite Creativo che utilizza un dispositivo Volume per sapere quando un giocatore vi entra. Quindi genera un prefab e lo rimuove quando il giocatore scende dall'attivatore. All'inizio della definizione della classe SpawnPrefabDevice, un TriggerVolume modificabile fa riferimento al volume dell'attivatore nel livello.
SpawnPrefabDevice := class(creative_device):
@editable
TriggerVolume:volume_device = volume_device{}All'inizio del gioco, il dispositivo definisce un nuovo prefab lampione da generare e la posizione in cui generarlo.
OnBegin<override>()<suspends>:void =
PrefabToSpawn:entity = LightPost.P_LightPost{}
SpawnTransform:transform = transform:
Translation := vector3:
Left := -9084.0
Up := -8.0
Forward := -919.0Quindi ottiene l'entità di simulazione e crea una serie di contesti di errore in cui lavorare. Utilizza prima il loop per eseguire ripetutamente il codice all'interno, seguito da una corsa tra due istruzioni di blocco.
if:
SimulationEntity := GetSimulationEntity[]
then:
loop:
race:
block:
TriggerVolume.AgentEntersEvent.Await()
SimulationEntity.AddEntities(array{ PrefabToSpawn })
PrefabToSpawn.SetGlobalTransform(SpawnTransform)
La prima istruzione di blocco attende che un agente entri nel dispositivo volume. Quando lo fa, aggiunge il prefab lanterna alla scena utilizzando AddEntities() e lo posiziona correttamente utilizzando SetGlobalTransform(). Il secondo blocco attende che l'agente lasci il volume e rimuova il prefab dal suo genitore, in questo caso l'entità di simulazione.
Dato che questi blocchi corrono in loop, ognuno di essi è sempre in esecuzione in modo che il giocatore possa entrare e uscire dal volume per rigenerare il lampione un numero qualsiasi di volte!
Script completo
Di seguito è riportato il completo per l'aggiunta e la rimozione di un'entità dalla scena.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph }
using { /Verse.org/SpatialMath }
using { /UnrealEngine.com/Temporary/Diagnostics }
SpawnPrefabDevice := class(creative_device):
Sovrapposizioni di entità
La seconda sala presenta anche una serie di esempi che mostrano alcuni dei diversi modi in cui le entità possono interagire. Ad esempio, le entità con un componente mesh possono essere avvisate quando si sovrappongono a un'altra entità tramite EntityEnteredEvent ed EntityExitedEvent. Dai un'occhiata all'esempio di UFO nella seconda sala per vedere questa funzionalità in azione.
Quando inizia il gioco, viene riprodotta un'animazione per sollevare la mucca come se fosse stata rapita dall'UFO. La mucca utilizza il EntityEnteredEvent del componente mesh per sapere quando si sovrappone all'UFO, quindi si imposta come invisibile per apparire come se fosse stata rapita.
Il codice per questo esempio è definito in EntityEnteredExampleComponent.verse. Apri questo file da Verse Explorer.
La classe entity_entered_example_component inizia con la definizione di un array di keyframe modificabile da cui costruire l'animazione del rapimento della mucca. Definisce anche una variabile StartTransform per indicare la posizione iniziale della mucca nella scena.
entity_entered_example_component<public> := class<final_super>(component):
@editable
Keyframes:[]keyframed_movement_delta = array{}
var StartTransform:transform = transform{}In OnBeginSimulation(), lo script inizia recuperando il fort_round_manager. Si tratta di un'interfaccia per la gestione dei round di Fortnite che puoi utilizzare per l'iscrizione agli eventi all'inizio e alla fine di un round. In questo esempio, lo script sottoscrive la funzione OnRoundStarted() all'inizio del round utilizzando SubscribeRoundStarted(), il che significa che la funzione verrà eseguita solo all'inizio del gioco invece che quando l'entità inizia la simulazione.
OnBeginSimulation<override>():void =
(super:)OnBeginSimulation()
if:
FortRoundManager := Entity.GetFortRoundManager[]
then:
FortRoundManager.SubscribeRoundStarted(OnRoundStarted)Quindi, in OnRoundStarted(), lo script inizia impostando StartTransform sulla trasformazione globale dell'entità, quindi sottoscrivendo EntityEnteredEvent del componente mesh a una nuova funzione OnEntityEntered(), che verrà attivata ogni volta che un'altra mesh si sovrappone all'entità. Quindi genera una funzione PlayCowAbductionAnimation() per iniziare a sollevare la mucca.
OnRoundStarted():void =
set StartTransform = Entity.GetGlobalTransform()
if:
Mesh := Entity.GetComponent[mesh_component]
then:
Mesh.EntityEnteredEvent.Subscribe(OnEntityEntered)
spawn { PlayCowAbdcutionAnimation() }La funzione PlayCowAbductionAnimation() imposta semplicemente la trasformazione globale della mucca sulla trasformazione iniziale e attende un breve periodo di tempo. Quindi ottiene i componenti di movimento della mesh e del keyframe dall'entità, abilita la mesh per rendere visibile la mucca, quindi imposta l'animazione sul componente di movimento del keyframe e la riproduce.
PlayCowAbdcutionAnimation()<suspends>:void =
Entity.SetGlobalTransform(StartTransform)
Sleep(2.0)
if:
Mesh := Entity.GetComponent[mesh_component]
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
set Mesh.Visible = true
MovementComponent.SetKeyframes(Keyframes, oneshot_keyframed_movement_playback_mode{})
MovementComponent.Play()Infine, la funzione OnEntityEntered() viene utilizzata per eseguire il codice ogni volta che la mucca si sovrappone a un'altra entità, in questo caso l'UFO. Quando lo fa, ottiene di nuovo i componenti di movimento mesh e keyframe, ma li utilizza invece per interrompere la riproduzione delle animazioni in corso e rendere la mucca invisibile in modo che appaia come se fosse stata rapita. Quindi genera una nuova istanza di PlayCowAbductionAnimation() per ricominciare il processo.
OnEntityEntered(OtherEntity:entity):void =
if:
Mesh := Entity.GetComponent[mesh_component]
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
MovementComponent.Stop()
set Mesh.Visible = false
spawn { PlayCowAbdcutionAnimation() }Script completo
Di seguito è riportato lo script completo per il rapimento di una mesh Mucca con una mesh UFO.
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/SpatialMath }
using { /Fortnite.com/Game }
entity_entered_example_component<public> := class<final_super>(component):
Interrogazione dei riscontri di sovrapposizione
Le entità possono anche eseguire query per altre entità utilizzando query di sovrapposizione. Invece di rilevare quando un'entità entra o esce da una determinata mesh, è possibile utilizzare le query di sovrapposizione per restituire ogni entità e componente che si sovrappone a una determinata area.
Questa area può essere l'entità stessa, un dato volume di collisione come una sfera o una scatola o una posizione da cui simulare l'entità. Quindi restituisce un elenco di overlap_hit. Ogni overlap_hit fornisce informazioni sul componente o sul volume sovrapposto dal volume di origine e puoi interrogare questi componenti per trovare l'entità associata.
Ad esempio, prova a salire sulla piattaforma interattiva prima dell'esempio FindOverlapHits. Quando interagisci con la pedana, l'UFO genera un volume di collisione invisibile e lo utilizza per eseguire una query di sovrapposizione. Se il volume di collisione si sovrappone alla mucca, l'UFO la rapisce!
Il codice per questo esempio è definito in FindOverlapHitsExampleComponent.verse. Apri questo file da Verse Explorer .
La classe inizia definendo un dispositivo a volume modificabile denominato Trigger per fare riferimento alla pedana interattiva su cui il giocatore calpesta e una variabile logica IsAbducting per stabilire se la mucca viene rapita o meno. Quando il componente inizia la simulazione, sottoscrive la funzione OnRoundStarted() all'inizio del round del gestore round. Allo stesso modo, la funzione OnRoundStarted() si limita a sottoscrivere AgentEntersEvent e AgentExitsEvent dal trigger rispettivamente alle funzioni OnTriggerEntered() e OnTriggerExited().
find_overlaphits_example_component<public> := class<final_super>(component):
@editable
Trigger:volume_device = volume_device{}
var IsAbducting:logic = false
OnBeginSimulation<override>():void =
Quando il giocatore cammina sulla pedana, se l'UFO non sta rapendo una mucca, la funzione OnTriggerEntered() viene chiamata per mettere in pausa qualsiasi movimento che l'UFO stia eseguendo recuperando il suo keyframed_movement_component e chiamando Pause(). Quindi chiama EnableAbductionBeam() e PerformOverlapCheck() per abilitare il raggio di rapimento e verificare se la mucca si trova sotto l'UFO.
OnTriggerEntered(Agent:agent):void=
if:
not IsAbducting?
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
MovementComponent.Pause()
EnableAbductionBeam()
PerformOverlapCheck()La logica effettiva del controllo delle sovrapposizioni è memorizzata nella funzione PerformOverlapCheck(). Per simulare un raggio di rapimento, questa funzione genera una capsula di collisione e definisce una CollisionTransform che è impostata proprio sotto l'UFO.
PerformOverlapCheck():void =
CollisionCapsule := collision_capsule{Radius := 36.0, Length := 328.0}
var CollisionTransform:transform = Entity.GetGlobalTransform()
set CollisionTransform.Translation.Up = CollisionTransform.Translation.Up - 248.0Successivamente, in un'espressione for, la funzione chiama FindOverlapHits() per trovare e restituire qualsiasi componente o volume. Passa la CollisionCapsule come volume in cui controllare le collisioni e la CollisionTransform come luogo da cui simulare la collisione. Quindi itera ogni sovrapposizione e controlla se il componente sovrapposto era un componente mesh, in particolare la mesh SM_Toy_Cow dell'entità mucca. In caso affermativo, genera una funzione AbductCow() che passa la mucca da rapire.
# Perform the overlap check from the entity that contains the mesh_component
for:
Overlap : Entity.FindOverlapHits(CollisionTransform, CollisionCapsule)
# Cast to see if what was overlapped was the Cow
CowMesh := Meshes.SM_Toy_Cow[Overlap.TargetComponent]
CowPrefab := CowMesh.Entity
do:
spawn { AbductCow(CowPrefab) }Per simulare il rapimento della mucca, l'entità crea un'animazione e la riproduce sull'entità mucca in modo simile all'esempio di sovrapposizioni di entità sopra. Dato che questo codice viene chiamato dall'entità UFO e non dalla mucca, il codice deve ottenere i componenti dall'entità mucca e quindi passare un'animazione per essere riprodotto. Inizia ottenendo la mesh e i componenti di movimento keyframe dalla mucca, quindi imposta IsAbducting su vero.
AbductCow(CowEntity:entity)<suspends>:void =
# Get the components on the Cow Prefab
if:
CowMesh := CowEntity.GetComponent[mesh_component]
MovementComponent := CowEntity.GetComponent[keyframed_movement_component]
then:
set IsAbducting = trueDato che i keyframe utilizzati nell'animazione del rapimento della mucca non sono impostati nell'outliner, il codice deve costruirli in base alla differenza di posizione tra l'UFO e la mucca. Lo fa ottenendo la differenza di traslazione tra la mucca e l'UFO, quindi costruendo un nuovo keyframed_movement_delta da questi valori. Quindi imposta quel singolo keyframe come array nel componente di movimento keyframe e chiama la riproduzione per far animare la mucca tra la sua posizione di partenza e l'UFO.
# Get the delta between Cow and UFO
DeltaTransform:transform = transform:
Translation:= Entity.GetGlobalTransform().Translation - CowEntity.GetGlobalTransform().Translation
Scale := vector3{Left:= 0.0, Up:= 0.0, Forward:= 0.0}
# Create a key frame
Keyframe := keyframed_movement_delta:
Transform := DeltaTransform
Duration := 2.0
Easing := ease_in_cubic_bezier_easing_function{}
La mucca può animarsi all'UFO quando viene rapita, ma il codice deve anche farla scomparire quando si sovrappone all'UFO stesso. A tale scopo, il codice attende EntityEnteredEvent del componente mesh dalla mucca e quindi chiama RemoveFromParent() per rimuovere l'entità mucca dalla scena. Dato che la mucca non c'è più, l'UFO può ricominciare a pattugliare, quindi il codice chiama la riproduzione del componente di movimento con keyframe dell'UFO per farlo muovere.
# Wait for Entity Entered Event
CowMesh.EntityEnteredEvent.Await()
# Remove Cow from world
CowEntity.RemoveFromParent()
# Resume UFO Patrol
set IsAbducting = false
if:
UFOMovementComponent := Entity.GetComponent[keyframed_movement_component]
Infine, le funzioni EnableAdbuctionBeam() e DisableAbductionBeam() agiscono come semplici aiutanti che attivano e disattivano rispettivamente la mesh del fascio di rapimento e i componenti luce spot sull'UFO ogni volta che viene chiamato.
EnableAbductionBeam():void =
for:
Mesh : Entity.FindDescendantComponents(Meshes.S_EV_SimpleLightBeam_01)
do:
Mesh.Enable()
for:
Light : Entity.FindDescendantComponents(spot_light_component)
do:
Light.Enable()
Script completo
Di seguito è riportato lo script completo per interrogare i riscontri di sovrapposizione.
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/Colors }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/SpatialMath }
using { /Fortnite.com/Game }
using { /Fortnite.com/Devices }
Interrogare i riscontri di sweep
I riscontri di sweep forniscono un altro modo importante per interrogare le sovrapposizioni tra le entità. Lo sweeping si riferisce allo spostamento di un oggetto per una determinata distanza lungo un determinato vettore. Ad esempio, puoi spostare un blocco su una piattaforma per spingere i giocatori in un varco o lanciare un missile in avanti per distruggere un muro.
La funzione FindSweepHits() restituisce un elenco di sweep_hit. Ogni sweep_hit fornisce le stesse informazioni di un overlap_hit, come ad esempio i riscontri di volume o componente e il volume o il componente di origine che esegue lo sweep. Fornisce inoltre informazioni sulla posizione di contatto, sulla normale, normale della faccia e sulla distanza lungo lo sweep dove si è verificato il riscontro.
Il modello utilizza i risultati di sweep per creare una versione più avanzata del precedente esempio di riscontri di sovrapposizione. Prova a salire sulla piattaforma interattiva finale nella seconda sala per dare un'occhiata a questo esempio. Quando si calpesta la pedana, l'UFO genera un raggio di rapimento. Quindi esegue uno sweep dalla mesh dell'UFO verso il basso, controllando la prima entità con cui lo sweep si sovrappone. Se l'entità è una mucca, l'UFO la rapisce come normale. Tuttavia, se la mucca è protetta da un'entità globo, lo sweep colpirà per primo il globo e verrà bloccato, impedendo all'UFO di rapire la mucca.
Apri FindOverlapHitsExampleComponent.verse da Verse Explorer per esaminare il codice. L'impostazione qui è molto simile all'esempio dei riscontri di sovrapposizione sopra, con la stessa logica utilizzata per rapire la mucca e abilitare e disabilitare il raggio di rapimento. La differenza principale è nella funzione OnTriggerEntered() che viene eseguita quando il giocatore cammina sulla piattaforma interattiva di fronte all'esempio.
Il codice per questa funzione inizia in modo simile all'esempio di riscontro di sovrapposizione, recuperando il componente di movimento del keyframe dall'entità e abilitando il raggio di rapimento.
OnTriggerEntered(Agent:agent):void=
# When a cow is inside the abduction area, stop the ship moving and start the abduction beam.
if:
not IsAbducting?
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
MovementComponent.Pause()
EnableAbductionBeam()Tuttavia, dato che la funzione utilizza gli sweep invece delle sovrapposizioni, la logica per stabilire se la mucca si trova sotto il raggio di rapimento è leggermente diversa. Inizia ottenendo il primo figlio dell'entità UFO, in questo caso l'entità mesh dell'UFO. Quindi crea un vettore di spostamento da cui eseguire lo sweep, puntando direttamente verso l'UFO.
# Perform the sweep from the UFO Mesh
if (Child := Entity.GetEntities()[0]):
DisplacementVector := vector3{Left:=0.0, Up:=-300.0, Forward:=0.0}Quindi utilizza questo vettore di spostamento per chiamare la funzione helper FindFirstSweepHit(), passando la mesh dell'UFO e il vettore. Se il primo componente è il componente mesh della mucca, genera la funzione AbductCow() per simulare il rapimento della mucca.
# Perform the sweep from the UFO Mesh
if (Child := Entity.GetEntities()[0]):
DisplacementVector := vector3{Left:=0.0, Up:=-300.0, Forward:=0.0}
FirstSweepHitEntity := FindFirstSweepHit(Child, DisplacementVector)
# If the First Hit Entity is the Cow Mesh, then abduct the Cow
if (HitEntity := FirstSweepHitEntity?; HitEntity.GetComponent[Meshes.SM_Toy_Cow]):
spawn { AbductCow(HitEntity) }La funzione FindFirstSweepHit() prende l'entità da eseguire e il vettore di spostamento la percorre. Chiama FindSweepHits() per simulare uno sweep, quindi scorre ogni riscontro di sweep restituito in un'espressione `for`. Dato che ogni sweep_hit contiene un componente o un volume, puoi interrogare TargetComponent o TargetVolume per sapere di quale tipo si tratta. In questo caso, il codice ottiene l'entità proprietaria del TargetComponent e la restituisce come opzione, il che significa che restituirà `true` se lo sweep ha rilevato un componente e falso altrimenti.
# Returns the first Entity hit by FindSweepHits
FindFirstSweepHit(InEntity:entity, DisplacementVector:vector3):?entity =
for (SweepHit : InEntity.FindSweepHits(DisplacementVector)):
return option{ SweepHit.TargetComponent.Entity }
return falseScript completo
Di seguito è riportato lo script completo per interrogare i riscontri di sweep.
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/Colors }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/SpatialMath }
using { /Fortnite.com/Game }
using { /Fortnite.com/Devices }