Scene Graph offre plusieurs façons de créer un gameplay flexible et dynamique grâce aux nombreuses interactions différentes entre les entités et les composants. Ce modèle présente quelques-unes des façons dont vous pouvez manipuler des entités à l'exécution en les faisant apparaître ou en les supprimant, ainsi que la manière d'utiliser les événements de composants et les requêtes pour créer une communication entre entités et concevoir des éléments de gameplay réutilisables.
Génération et suppression de préfabriqués
Par exemple, découvrez la plaque interactive dans la deuxième salle. Lorsque le joueur marche sur la plaque, une lanterne de préfabriqué est ajoutée à la scène et retirée lorsque le joueur descend de la plaque.
Ouvrez SpawnPrefabDevice.verse pour découvrir comment ce code fonctionne. Il s'agit d'un appareil du mode Créatif qui utilise un appareil Volume pour savoir quand un joueur y entre. Il génère ensuite un préfabriqué et le supprime lorsque le joueur descend du déclencheur. En haut de la définition de classe SpawnPrefabDevice, un TriggerVolume modifiable fait référence au volume de déclenchement dans le niveau.
SpawnPrefabDevice := class(creative_device):
@editable
TriggerVolume:volume_device = volume_device{}Lorsque le jeu commence, l'appareil définit un nouveau préfabriqué de lampadaire à générer, ainsi que la position où il doit apparaître.
OnBegin<override>()<suspends>:void =
PrefabToSpawn:entity = LightPost.P_LightPost{}
SpawnTransform:transform = transform:
Translation := vector3:
Left := -9084.0
Up := -8.0
Forward := -919.0Il obtient ensuite l'entité de simulation et crée une série de contextes d'échec dans lesquels travailler. Il utilise d'abord une boucle pour exécuter le code à l'intérieur de manière répétée, suivie d'une course entre deux instructions de bloc.
if:
SimulationEntity := GetSimulationEntity[]
then:
loop:
race:
block:
TriggerVolume.AgentEntersEvent.Await()
SimulationEntity.AddEntities(array{ PrefabToSpawn })
PrefabToSpawn.SetGlobalTransform(SpawnTransform)
La première instruction de bloc attend qu'un agent entre dans l'appareil Volume. Lorsqu'il le fait, il ajoute le préfabriqué de la lanterne à la scène à l'aide de AddEntities() et le positionne correctement à l'aide de SetGlobalTransform(). Le deuxième bloc attend que l'agent quitte le volume et supprime les préfabriqués de son parent, dans ce cas, l'entité de simulation.
Comme ces blocs sont en compétition dans une boucle, ils s'exécutent en continu, permettant ainsi au joueur d'entrer et de sortir du volume pour faire réapparaître le lampadaire autant de fois que nécessaire !
Script complet
Voici le script complet pour ajouter et supprimer une entité de la scène.
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):
Chevauchements d'entités
La deuxième salle présente également une série d'exemples qui illustrent certaines des différentes manières dont les entités peuvent interagir. Par exemple, les entités avec un composant de maillage peuvent être alertées lorsqu'elles chevauchent une autre entité via EntityEnteredEvent et EntityExitedEvent du composant de maillage. Extrayez l'exemple d'OVNI dans la deuxième salle pour voir cette fonctionnalité en action.
Lorsque le jeu commence, la vache joue une animation pour se soulever comme si elle était enlevée par l'OVNI. La vache utilise l'EntityEnteredEvent du composant de maillage pour savoir quand elle chevauche l'OVNI, puis se définit comme invisible pour apparaître comme si elle avait été enlevée.
Le code de cet exemple est défini dans EntityEnteredExampleComponent.verse. Ouvrez ce fichier à partir de l'explorateur Verse.
La classe entity_entered_example_component commence par définir une matrice d'images clés modifiable à partir de laquelle créer l'animation d'enlèvement de la vache. Elle définit également une variable StartTransform pour indiquer la position de départ de la vache dans la scène.
entity_entered_example_component<public> := class<final_super>(component):
@editable
Keyframes:[]keyframed_movement_delta = array{}
var StartTransform:transform = transform{}Dans OnBeginSimulation(), le script commence par récupérer le fort_round_manager. Il s'agit d'une interface du gestionnaire de manches Fortnite que vous pouvez utiliser pour vous abonner à des événements au début et à la fin d'une manche. Dans cet exemple, les scripts s'abonnent à la fonction OnRoundStarted() au début de la manche à l'aide de SubscribeRoundStarted(), ce qui signifie que la fonction s'exécutera uniquement lorsque le jeu commence au lieu du moment où l'entité commence la simulation.
OnBeginSimulation<override>():void =
(super:)OnBeginSimulation()
if:
FortRoundManager := Entity.GetFortRoundManager[]
then:
FortRoundManager.SubscribeRoundStarted(OnRoundStarted)Ensuite, dans OnRoundStarted(), le script commence par définir StartTransform sur la transformation globale de l'entité, puis en abonnant l'EntityEnteredEvent du composant de maillage à une nouvelle fonction OnEntityEntered(), qui sera déclenchée chaque fois qu'un autre maillage chevauche l'entité. Il génère ensuite une fonction PlayCowAbductionAnimation() pour commencer à soulever la vache.
OnRoundStarted():void =
set StartTransform = Entity.GetGlobalTransform()
if:
Mesh := Entity.GetComponent[mesh_component]
then:
Mesh.EntityEnteredEvent.Subscribe(OnEntityEntered)
spawn { PlayCowAbdcutionAnimation() }La fonction PlayCowAbductionAnimation() définit simplement la transformation globale de la vache à sa transformation de départ et attend un court laps de temps. Il récupère ensuite les composants de déplacement du maillage et des images clés de l'entité, active le maillage pour rendre la vache visible, puis définit l'animation sur le composant de déplacement associé à une image clé et la lit.
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()Enfin, la fonction OnEntityEntered() est utilisée pour exécuter du code chaque fois que la vache chevauche une autre entité, dans ce cas l'OVNI. À ce moment-là, elle récupère à nouveau les composants de maillage et de déplacements associés à une image clé, mais les utilise plutôt pour arrêter la lecture des animations en cours et rendre la vache invisible pour donner l'impression qu'elle a été enlevée. Elle génère ensuite une nouvelle instance de PlayCowAbductionAnimation() pour redémarrer le processus.
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 complet
Vous trouverez ci-dessous le script complet pour enlever un maillage de vache avec un maillage d'OVNI.
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):
Interrogation des impacts de chevauchement
Les entités peuvent également interroger d'autres entités à l'aide de requêtes de chevauchement. Au lieu de détecter quand une entité entre ou sort d'un maillage particulier, les requêtes de chevauchement peuvent être utilisées pour renvoyer chaque entité et composant chevauchant une zone particulière.
Cette zone peut être l'entité elle-même, un volume de collision donné, comme une sphère ou une boîte, ou une position à partir de laquelle simuler l'entité. Elles renvoient ensuite une liste de overlap_hit. Chaque overlap_hit vous donne des informations sur le composant ou le volume chevauché par le volume source, et vous pouvez interroger ces composants pour trouver leur entité associée.
Par exemple, essayez de marcher sur la plaque interactive devant l'exemple FindOverlapHits. Lorsque vous interagissez avec la plaque, l'OVNI génère un volume de collision invisible et l'utilise pour effectuer une requête de chevauchement. Si le volume de collision chevauche la vache, l'OVNI l'enlève !
Le code de cet exemple est défini dans FindOverlapHitsExampleComponent.verse. Ouvrez ce fichier à partir de l'explorateur Verse .
La classe commence par définir un appareil Volume modifiable nommé Trigger pour référencer la plaque interactive sur laquelle le joueur marche, ainsi qu'une variable logique IsAbducting pour savoir si la vache est enlevée ou non. Lorsque le composant commence la simulation, il abonne la fonction OnRoundStarted() au début de manche déclenché par le gestionnaire de manches. De même, la fonction OnRoundStarted() abonne simplement AgentEntersEvent et AgentExitsEvent du déclencheur aux fonctions OnTriggerEntered() et OnTriggerExited() respectivement.
find_overlaphits_example_component<public> := class<final_super>(component):
@editable
Trigger:volume_device = volume_device{}
var IsAbducting:logic = false
OnBeginSimulation<override>():void =
Lorsque le joueur marche sur la plaque, si l'OVNI n'est pas en train d'enlever une vache, la fonction OnTriggerEntered() est appelée pour mettre en pause tout déplacement que l'OVNI peut effectuer en récupérant son keyframed_movement_component et en appelant Pause(). Elle appelle ensuite EnableAbductionBeam() et PerformOverlapCheck() pour activer le faisceau d'enlèvement et vérifier si la vache se trouve sous l'OVNI.
OnTriggerEntered(Agent:agent):void=
if:
not IsAbducting?
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
MovementComponent.Pause()
EnableAbductionBeam()
PerformOverlapCheck()La logique réelle de vérification des chevauchements est stockée dans la fonction PerformOverlapCheck(). Pour simuler un faisceau d'enlèvement, cette fonction génère une capsule de collision et définit un CollisionTransform qui est défini juste en dessous de l'OVNI.
PerformOverlapCheck():void =
CollisionCapsule := collision_capsule{Radius := 36.0, Length := 328.0}
var CollisionTransform:transform = Entity.GetGlobalTransform()
set CollisionTransform.Translation.Up = CollisionTransform.Translation.Up - 248.0Ensuite, dans une expression for, la fonction appelle FindOverlapHits() pour rechercher et renvoyer tous les composants ou volumes. Elle transmet la CollisionCapsule comme volume dans lequel vérifier les collisions et la CollisionTransform comme lieu à partir duquel simuler cette collision. Il itère ensuite sur chaque chevauchement et vérifie si le composant superposé était un composant de maillage, en particulier le maillage SM_Toy_Cow de l'entité vache. Si c'est le cas, cela génère une fonction AbductCow() qui transmet la vache à enlever.
# 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) }Pour simuler l'enlèvement de la vache, l'entité crée une animation, puis la lit sur l'entité vache de manière similaire à l'exemple de chevauchement d'entités ci-dessus. Étant donné que ce code est appelé depuis l'entité OVNI et non depuis la vache, le code doit récupérer les composants de l'entité vache, puis lui transmettre une animation à lire. Elle commence par récupérer le maillage et les composants de déplacement d'images clés de la vache, puis définit IsAbducting sur true.
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 = trueÉtant donné que les images clés utilisées dans l'animation d'enlèvement de vache ne sont pas définies dans l'organiseur, le code doit les construire en fonction de la différence de position entre l'OVNI et la vache. Il le fait en obtenant la différence de traduction entre la vache et l'OVNI, puis en construisant un nouveau keyframed_movement_delta à partir de ces valeurs. Il définit ensuite cette image clé unique comme matrice dans le composant de déplacement associé à une image clé, puis appelle la fonction de lecture pour que la vache s'anime entre sa position de départ et l'OVNI.
# 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 vache peut s'animer vers l'OVNI lorsqu'elle est enlevée, mais le code doit également la faire disparaître lorsqu'elle présente un chevauchement avec l'OVNI lui-même. Pour ce faire, le code attend l'EntityEnteredEvent du composant de maillage de la vache, puis appelle RemoveFromParent() pour supprimer l'entité vache de la scène. Comme la vache a disparu, l'OVNI peut à nouveau patrouiller. Le code appelle donc la fonction de lecture sur le composant de déplacement associé à une image clé de l'OVNI pour qu'il se redéplace.
# 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]
Enfin, les fonctions EnableAdbuctionBeam() et DisableAbductionBeam() agissent comme de simples assistants qui activent et désactivent respectivement le maillage du faisceau d'enlèvement et les composants du projecteur sur l'OVNI chaque fois qu'elles sont appelées.
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 complet
Vous trouverez ci-dessous le script complet pour interroger les impacts de chevauchement.
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 }
Interrogation des balayages
Les balayages fournissent un autre moyen important d'interroger les chevauchements entre les entités. Le balayage fait référence au déplacement d'un objet sur une distance définie le long d'un vecteur particulier. Par exemple, déplacer un bloc sur une plateforme pour faire tomber les joueurs ou lancer un missile directement contre mur.
La fonction FindSweepHits() renvoie une liste de sweep_hit. Chaque sweep_hit vous donne les mêmes informations qu'un overlap_hit, comme le composant ou le volume touché, et le volume ou le composant source effectuant le balayage. Ils fournissent également des informations sur la position de contact, sur la normale, sur la normale de face et la distance le long du balayage où l'impact s'est produit.
Le modèle utilise des balayages pour créer une version plus avancée de l'exemple précédent basé sur les impacts par chevauchement. Essayez de monter sur la dernière plaque interactive dans la deuxième salle pour découvrir cet exemple. Lorsque vous marchez sur la plaque, l'OVNI génère un rayon d'enlèvement. Il effectue ensuite un balayage depuis le maillage d'OVNI vers le bas, en vérifiant la première entité avec laquelle le balayage se chevauche. Si l'entité est une vache, l'OVNI l'enlève normalement. Cependant, si la vache est protégée par une entité globe, le balayage touchera le globe en premier et sera bloqué, empêchant l'OVNI d'enlever la vache.
Ouvrez FindOverlapHitsExampleComponent.verse depuis l'explorateur Verse pour examiner le code. La configuration ici est très similaire à l'exemple d'impacts de chevauchement ci-dessus, avec la même logique utilisée pour enlever la vache et activer et désactiver le faisceau d'enlèvement. La principale différence réside dans la fonction OnTriggerEntered() qui s'exécute lorsque le joueur marche sur la plaque interactive devant l'exemple.
Le code de cette fonction démarre de manière similaire à l'exemple d'impact de chevauchement, en récupérant le composant de déplacement associé à l'image clé de l'entité et en activant le faisceau d'enlèvement.
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()Cependant, comme la fonction utilise des balayages au lieu de chevauchements, la logique pour savoir si la vache se trouve sous le faisceau d'enlèvement est un peu différente. Le code commence par obtenir le premier enfant de l'entité OVNI, dans ce cas l'entité de maillage de l'OVNI. Il crée ensuite un vecteur de déplacement à partir duquel effectuer le balayage, pointant directement vers le bas à partir de l'OVNI.
# Perform the sweep from the UFO Mesh
if (Child := Entity.GetEntities()[0]):
DisplacementVector := vector3{Left:=0.0, Up:=-300.0, Forward:=0.0}Il utilise ensuite ce vecteur de déplacement pour appeler la fonction d'assistance FindFirstSweepHit(), en transmettant le maillage de l'OVNI et le vecteur. Si le premier composant est le composant de maillage de la vache, il génère la fonction AbductCow() pour simuler l'enlèvement de la vache.
# 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 fonction FindFirstSweepHit() prend l'entité à balayer et le vecteur de déplacement pour la balayer. Elle appelle FindSweepHits() pour simuler un balayage, puis parcourt chaque résultat de balayage renvoyé dans une expression « for ». Chaque sweep_hit étant un composant ou un volume, vous pouvez interroger le TargetComponent ou le TargetVolume pour savoir de quel type il s'agit. Dans ce cas, le code récupère l'entité propriétaire du TargetComponent et la renvoie en option, ce qui signifie qu'il renverra « true » si le balayage touche un composant et false dans le cas contraire.
# 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 complet
Vous trouverez ci-dessous le script complet pour interroger les balayages.
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 }