Les lanternes du tutoriel Scene Graph utilisent plusieurs composants conçus avec Verse pour créer des préfabriqués dynamiques et interactifs. Ceci est rendu possible par la programmation du déplacement et de l'interactivité dans un composant conçu par Verse.
Simulation d'un déplacement simple
Le lampadaire préfabriqué utilise deux composants différents pour simuler le déplacement de la lanterne. Ces deux éléments sont liés à l'entité Pivot, qui fournit un point de pivot pour déplacer la lanterne. Tout d'abord, le keyframed_movement_component vous permet de créer des animations à partir d'images clés, puis de les lire pour animer une entité. Ce composant ne peut cependant pas agir seul et nécessite un autre bout de code pour fournir les images clés et démarrer l'animation. C'est là qu'intervient le simple_movement_component personnalisé. Ce script est réutilisé dans tout le projet pour contrôler la manière dont les différents objets du jeu se déplacent dans la scène.
Pour en savoir plus sur l'utilisation des animations pour déplacer des objets, consultez Animation du déplacement des accessoires.
Découvrons plus en détail le simple_movement_component. Ouvrez le script SimpleMovementComponent.verse depuis l'explorateur Verse dans Visual Studio Code pour commencer.
Les animations utilisent différents modes de lecture pour définir ce que l'animation doit faire une fois terminée. Ceux-ci sont définis en haut du fichier dans l'enum movement_mode :
Exacteur - L'objet arrête de se déplacer lorsqu'il termine l'animation.
Boucle - L'objet redémarre l'animation depuis le début lorsqu'il atteint la fin.
Aller-retour - L'objet joue l'animation à l'envers, en faisant des allers-retours du début à la fin.
movement_mode<public> := enum:
OneShot
Loop
PingPongLa classe basic_movement_component définit également les variables nécessaires à la création d'animations. Chacune possède l'attribut @editable pour vous permettre de les modifier à partir de l'organiseur :
Images clés: matrice dekeyframed_movement_deltautilisée pour créer l'animation. Chacune de ces pistes suit le changement de transformation à cette image clé particulière, la durée de l'image clé et le type d'adoucissement du déplacement utilisé.Lecture automatique: variablelogiquequi détermine si l'entité doit s'animer lorsqu'elle commence la simulation.MovementMode: comportement de déplacement présenté par l'entité.
# A Verse-authored component that can be added to entities
basic_movement_component<public> := class<final_super>(component):
@editable
var Keyframes<public>: []keyframed_movement_delta = array{}
@editable
var AutoPlay: logic = true
Le code dans OnSimulate() commence à s'exécuter chaque fois que le composant est ajouté à la scène et lorsque le jeu commence. Ici, le code utilise d'abord un court appel Sleep() pour s'assurer que toutes les entités et tous les composants sont correctement initialisés avant de continuer. Ensuite, dans une expression if, il vérifie si l'entité possède un keyframed_movement_component.
Si tel est le cas, il appelle la fonction InitializeKeyFramedMovementComponent() pour configurer le composant avec les valeurs appropriées. Que se passe-t-il si l'entité n'a pas de keyframed_movement_component ? Dans ce cas, vous pouvez en créer un dynamiquement au moment de l'exécution et l'ajouter à l'entité à l'aide de la fonction AddComponents(). De cette façon, vous garantissez que votre entité est correctement configurée lorsque le jeu commence !
OnSimulate<override>()<suspends>:void =
Sleep (0.1)
if:
KeyframedMovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
InitializeKeyframedMovementComponent(KeyframedMovementComponent)
else:
NewKeyFramedMovementComponent := keyframed_movement_component { Entity := Entity }
Entity.AddComponents of array { NewKeyFramedMovementComponent }
InitializeKeyframedMovementComponent(NewKeyFramedMovementComponent)La fonction InitializeKeyframedMovementComponent() définit le composant de déplacement associé à une image clé avec des valeurs basées sur celles que vous attribuez dans l'éditeur. Tout d'abord, elle crée une nouvelle variable keyframed_movement_playback_mode pour déterminer le mode de lecture utilisé pendant cette animation. Ceci est initialisé avec la valeur oneshot_keyframed_movement_playback_mode, ce qui signifie que l'animation ne sera lue qu'une seule fois.
InitializeKeyframedMovementComponent(InKeyframedMovementComponent:keyframed_movement_component):void =
var PlaybackMode:keyframed_movement_playback_mode = oneshot_keyframed_movement_playback_mode{}Elle utilise ensuite une instruction case pour définir le PlaybackMode en fonction de la valeur MovementMode définie dans l'éditeur. Chaque valeur de l'enum MovementMode correspond à un mode de lecture différent défini dans le module SceneGraph/KeyframedMovement.
case (MovementMode):
movement_mode.OneShot =>
set PlaybackMode = oneshot_keyframed_movement_playback_mode{}
movement_mode.Loop =>
set PlaybackMode = loop_keyframed_movement_playback_mode{}
movement_mode.PingPong =>
set PlaybackMode = pingpong_keyframed_movement_playback_mode{}Enfin, la fonction définit les images clés et le mode de déplacement sur le composant de déplacement associé à une image clé, et l'animation est maintenant prête à lue. Si la variable AutoPlay est définie sur true, l'animation démarre immédiatement en appelant la fonction Play() du composant de déplacement associé à une image clé.
InKeyframedMovementComponent.SetKeyframes(Keyframes, PlaybackMode)
if:
AutoPlay?
then:
InKeyframedMovementComponent.Play()De retour dans l'éditeur, le simple_movement_component est ajouté à l'entité Pivot et défini avec les valeurs suivantes. Maintenant, lorsque le jeu démarrera, la lanterne commencera à se balancer d'avant en arrière !
Script complet
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph/KeyframedMovement }
movement_mode<public> := enum:
OneShot
Loop
Interaction avec la lanterne
La lanterne peut osciller d'avant en arrière, mais vous aurez besoin d'un autre élément pour permettre à votre joueur d'interagir avec elle. Cela se fait en associant un composant interactif à l'entité Lanterne et un lantern_interaction_component Verse personnalisé au lampadaire qui vous permet d'allumer et d'éteindre la lanterne.
Ouvrez le script LanternInteractionComponent.verse à partir de l'explorateur Verse. Ce script commence par importer le module LightPost défini dans Assets.digest.verse. C'est ici que sont stockés toutes les ressources liées au lampadaire, comme les préfabriqués. Il importe également le module LightPost.Materials pour accéder aux matériaux et aux maillages du lampadaire et de la lanterne.
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
LightPost := module:
Materials<public> := module:
La classe lantern_interaction_component commence par définir une variable nommée MaterialInstance, qui est initialisée à LightPost.Materials.MI_Lantern_01. Il s'agit de l'instance de matériau qui contient toutes les variables liées au matériau de la lanterne.
lantern_interaction_component<public> := class<final_super>(component):
var MaterialInstance:LightPost.Materials.MI_Lantern_01 = LightPost.Materials.MI_Lantern_01{}Dans OnBeginSimulation(), le script commence par rechercher chaque composant sur ses entités descendantes avec un interactable_component associé. Étant donné que le lantern_interaction_component est associé au lampadaire, cela retourne le interactable_component de l'entité Lanterne puisqu'il s'agit d'un descendant du lampadaire.
OnBeginSimulation<override>():void =
(super:)OnBeginSimulation()
InteractabeleComponents := Entity.FindDescendantComponents(interactable_component)Ensuite, dans une expression for, le script parcourt chaque composant interactif trouvé et abonne son SucceededEvent à la fonction OnInteractFinished() définie plus loin dans ce fichier. Désormais, lorsqu'un joueur arrête d'interagir avec la lanterne, OnInteractFinished() sera déclenché.
InteractabeleComponents := Entity.FindDescendantComponents(interactable_component)
for (InteractableComponent : InteractabeleComponents):
InteractableComponent.SucceededEvent.Subscribe(OnInteractFinished)La fonction OnBeginSimulation() appelle ensuite à nouveau FindDescendantComponents pour rechercher chaque entité à laquelle est associé un composant LightPost.SM_Lightpost_Lantern_01. Il s'agit du composant de maillage associé à la lanterne. Il définit ensuite le composant de maillage sur le MaterialInstance défini précédemment, assurant ainsi que le maillage de la lanterne est correctement initialisé.
MeshComponents := Entity.FindDescendantComponents(LightPost.SM_Lightpost_Lantern_01)
for (MeshComponent : MeshComponents):
set MeshComponent.M_Lantern = MaterialInstanceLa fonction OnInteractFinished() prend l'agent, ou le joueur, comme instigateur de l'interaction. Cette fonction appelle simplement la fonction ToggleLight() pour allumer et éteindre la lanterne.
La fonction ToggleLight() fait le gros du travail en matière d'allumage et d'extinction de la lanterne. Tout d'abord, dans une expression if, elle vérifie si le niveau émissif du MaterialInstance est 0,0, indiquant que la lumière est éteinte. Si tel est le cas, elle définit le niveau émissif sur 1,0. Ensuite, dans deux expressions for, elle trouve chaque light_component et particle_system_component sur les entités descendantes et les active en appelant respectivement Enable() et Play().
ToggleLight():void =
if (MaterialInstance.Emissive_Multiply = 0.0):
set MaterialInstance.Emissive_Multiply = 1.0
for:
Light : Entity.FindDescendantComponents(light_component)
do:
Light.Enable()
for:
Si la lumière était déjà allumée, la fonction fait l'inverse à l'intérieur de l'instruction else. Elle définit les niveaux émissifs du matériau sur 0,0 et désactive tous les composants du système de lumière et de particules sur les entités descendantes.
else:
set MaterialInstance.Emissive_Multiply = 0.0
for:
Light : Entity.FindDescendantComponents(light_component)
do:
Light.Disable()
for:
Particle : Entity.FindDescendantComponents(particle_system_component)
Les fonctions de ce script se combinent pour rendre la lanterne dynamique, activant et désactivant plusieurs composants lorsqu'un joueur interagit avec elle. Notez que même si la lanterne est celle qui est allumée et éteinte, son entité parent de lampadaire est celle qui fournit le lantern_interaction_component.
Pensez à ce que vous auriez besoin de faire pour avoir un lampadaire avec plusieurs lumières, en allumant et en éteignant chacune d'elles en appuyant sur un seul bouton. Étant donné que ce code fonctionne en recherchant des entités descendantes avec un type de composant particulier, vous n'aurez pas besoin de code Verse supplémentaire pour implémenter cette fonctionnalité. Tant que chaque entité Lanterne enfant possède un composant de lumière ou un composant de particules, vous êtes prêt !
Script complet
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
LightPost := module:
Materials<public> := module: