Avec Verse, vous pouvez créer vos propres composants à ajouter à vos entités. Avec les composants Verse personnalisés, vous pouvez générer des entités dans la scène ou les en retirer, ajouter des composants aux entités ou les en retirer, et créer vos propres comportements, tels qu'une entité disparaissant en boucle, ou tout ce qui vous passer par la tête !
Créer un nouveau composant Verse
Vous pouvez créer un nouveau composant Verse à partir du fichier de modèle Verse.
Pour créer un composant Verse :
Dans le panneau Détails de votre entité, choisissez Ajouter un composant > Nouveau composant Verse.
Vous pouvez en outre créer un nouveau composant Verse en ajoutant un nouveau fichier Verse via l'explorateur Verse.
Dans la liste des modèles de code Verse, choisissez Composant Scene Graph.
Spécifiez le nom de votre composant créé dans Verse dans le champ Nom du composant. Dans cet exemple, le composant est nommé
my_verse_component.Cliquez sur Créer pour créer votre fichier de composant Verse. Votre composant créé dans Verse apparaît maintenant dans la liste de composants lorsque vous choisissez d'ajouter un composant à votre entité.
Vous ne pouvez ajouter qu'une seule classe ou sous-classe de composant donnée. Par exemple, vous ne pouvez avoir qu'un seul mesh_component sur une entité. Cette consigne s'applique aux sous-classes de composants ; autrement dit, si vous ajoutez un capsule_light_component à votre entité, vous ne pouvez pas également ajouter un rect_light_component, car les deux sous-classes sont issues de light_component. La même limitation s'applique à vos composants personnalisés créés dans Verse.
Durée de vie du composant
Les composants passent par une série de fonctions de durée de vie lorsqu'ils sont ajoutés à des entités, ajoutés à la scène, et commencent à fonctionner dans la simulation. Vos composants créés dans Verse doivent remplacer ces méthodes pour configurer et exécuter leur simulation.
Lorsqu'un composant s'arrête, il passe alors par la version d'arrêt de ces fonctions, ce qui vous donne l'occasion de nettoyer tout état conservé sur le composant avant qu'il ne soit supprimé.
Les états de cycle de vie d'un composant sont les suivants :
Initialized
AddedToScene
BeginSimulation
EndSimulation
RemovingFromScene
Uninitializing
Les fonctions de durée de vie des composants sont différentes des durées de vie des appareils. La logique de composant fonctionne à la fois en mode modification et en mode jeu. Tout comportement que vous ajoutez est immédiatement exécuté lors du lancement de votre session. Si vous souhaitez que votre logique de composant ne soit exécutée qu'au démarrage du jeu, vous pouvez générer des préfabriqués dans la fonction OnBegin() d'un appareil Verse.
Interroger des entités et des composants avec Verse
Il existe plusieurs façons de rechercher des entités et des composants dans votre code Verse. La façon dont vous structurez vos entités et vos composants influe sur la manière dont vous interrogez et développez des fonctionnalités dans votre code Verse.
Pour interroger des entités et des composants dans Verse, vous devez commencer par une entité et renvoyer les entités imbriquées au-dessus ou en dessous de celle-ci dans la hiérarchie. Vous pouvez interroger le parent direct ou les enfants d'une entité, ainsi que toutes ses entités ancêtres et descendantes.
Obtenir des entités avec un type de composant
Vous pouvez trouver toutes les entités avec un type de composant spécifique en appelant l'entité que vous souhaitez interroger. Si l'entité que vous interrogez est l'entité de simulation, elle renvoie toutes les entités avec des composants de ce type dans la scène.
Dans l'exemple suivant, le composant Verse obtient toutes les entités auxquelles est rattaché le composant light_component. Il crée un particle_sytem_component qu'il rattache à chacune des entités qu'il trouve. Ici, BlowingParticles est un émetteur Niagara référencé dans le fichier Assets.digest.Verse.
Le light_component est une super-classe pour tous les différents types de composants lumineux que vous pouvez ajouter à vos entités. Dans la requête ci-dessous, LightComponent permet de rechercher des entités comportant tout type de composant lumineux.
# 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.
Dans l'exemple suivant, nous vous expliquons comment interroger toutes les entités dont les systèmes de particules sont imbriqués sous l'entité à laquelle est rattaché le composant Verse à l'aide de la fonction FindDescendantEntitiesWithComponent(). De la même manière, vous pouvez également obtenir toutes les entités ancêtres avec un composant particulier en utilisant 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.
Pour interroger les entités de l'ensemble de votre scène, vous pouvez obtenir l'entité de simulation et exécuter vos requêtes sur cette entité. Cette opération commence au sommet de la structure de l'entité et recherche toutes les entités imbriquées qui correspondent à la requête. Pour accéder à l'entité de simulation, appelez la fonction faillible GetSimulationEntity[] sur l'entité à laquelle le composant Verse est rattaché.
# 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:
Demander à votre composant Verse d'effectuer constamment des recherches dans l'arborescence des entités peut s'avérer coûteux. Si votre comportement dépend d'une structure d'entité spécifique, toute modification de cette structure, aussi subtile soit-elle, peut entraîner une modification involontaire de votre comportement, voire le rendre totalement inopérant.
En revanche, votre composant peut nécessiter moins de configuration, car vous êtes uniquement tenu d'ajouter votre composant Verse et de créer la structure d'entité correcte. Gardez ces compromis à l'esprit lorsque vous créez vos entités et développez la logique de votre composant Verse.
Explorez le module SceneGraph pour découvrir tous les modes d'utilisation des entités et des composants dans Verse. Dans les paragraphes suivants, nous vous présentons certaines méthodes courantes pour interroger les entités et les composants dans votre code.
Obtenir les composants d'une entité
Vous pouvez utiliser Verse pour obtenir un composant d'un type spécifique sur une entité en appelant GetComponent[]. Cette méthode est utile pour créer une logique personnalisée qui dépend du comportement d'autres composants, Vous pouvez par exemple utiliser le sound_component pour jouer le son en fonction de la couleur d'une lumière ou obtenir un particle_system_component pour appliquer des effets en fonction de la présence de l'entité dans une certaine zone.
Dans l'exemple suivant, le composant Verse fait apparaître et disparaître une plateforme à répétition en boucle. Pour ce faire, il récupère le composant mesh de l'entité et le désactive, puis le réactive au bout d'une durée définie.
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
Dans un autre exemple, le composant Verse obtient le light_component sur l'entité et change sa couleur en orange foncé. Dans la mesure où le composant light_component est une super-classe pour tous les types d'éclairage que vous pouvez ajouter à vos entités, cet exemple recherche tout composant appartenant à l'une de ses sous-classes.
# 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:
Obtenir tous les composants
Vous pouvez utiliser la fonction GetComponents() pour renvoyer tous les composants de l'entité. Étant donné que votre code ne sait pas de quel type de composants il s'agit, vous pouvez utiliser la conversion pour effectuer différentes opérations en fonction du type de chaque composant. Dans l'exemple suivant, nous obtenons une matrice de tous les composants d'une entité et tentons de les convertir en type enableable. Si la conversion réussit, le composant implémente l'interface enableable. Il désactive ensuite chaque composant implémentant cette interface.
# 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
Rechercher des entités par balises de jeu
Vous pouvez ajouter un composant de balise à vos entités afin de pouvoir rechercher des entités spécifiques dans votre scène, de la même manière que vous pouvez ajouter des balises de jeu à vos acteurs. Cette fonction est utile pour choisir les entités avec lesquelles vous souhaitez travailler au lieu de dépendre d'éléments modifiables, par exemple des composants que possède une entité, ou de l'emplacement de ces composants dans la scène.
Dépendre d'éléments modifiables peut en effet entraîner un comportement indésirable dans votre jeu lorsque vous modifiez et ajoutez de nouvelles entités à votre projet. Vous pouvez ajouter des balises dans l'éditeur en ajoutant un tag_component à votre entité et en sélectionnant des balises dans le menu déroulant Balises, ou dans le code Verse en utilisant la fonction AddTag().
Dans l'exemple suivant, l'entité de simulation est interrogée concernant tous les descendants dotés de la balise my_tag sur leur 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:
Rechercher des entités par chevauchement
Les volumes de collision sont des volumes qui représentent les formes de collision des maillages. Vous pouvez les utiliser pour interroger des objets qui se chevauchent dans une forme particulière, comme des objets infligeant des dégâts s'ils s'approchent trop d'une tour ou la détection de l'entrée d'un ballon de football dans un embut. Dans Verse, vous pouvez utiliser la fonction FindOverlapHits() pour rechercher toutes les entités dans 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é. Vous obtenez 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 rechercher leur entité associée.
Dans l'exemple suivant, une sphère d'un rayon de 256,0 unités centrée sur la transformation de l'entité est créée. Ensuite, tous les chevauchements dans la sphère sont recherchés et une liste de overlap_hit est renvoyée. Chaque overlap_hit étant un composant ou un volume, vous pouvez interroger le TargetComponent ou le TargetVolume pour savoir de quel type il s'agit. Si le overlap_hit est un composant, le code obtient alors l'entité du composant. Enfin, il vérifie si l'entité possède un composant light. Si c'est le cas, la couleur de la lumière devient bleue. Avec un volume suffisamment grand, vous changez la couleur de chaque lumière de votre île avec seulement quelques lignes de code !
# 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
Ce code simule un volume collision_sphere en chevauchement désigné par le cercle d'un rayon de 256,0 unités centré sur l'entité cube, renvoyant tout composant ou volume que la sphère chevauche. Il récupère ensuite l'entité parente de chaque composant chevauché et transforme tout composant light en bleu. Étant donné que le cube se trouve dans la collision_sphere lorsque le chevauchement commence, il est inclus dans la liste des overlap_hits et devient également bleu. Notez que, dans la mesure où l'entité à cône rouge la plus à droite est en dehors de la collision_sphere, elle ne se chevauche pas et ne devient pas bleue.
Rechercher des entités par balayage
Une autre façon importante d'interroger des entités est d'utiliser des balayages. 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.
Dans Verse, vous pouvez simuler des balayages pour interroger les collisions entre les objets à l'aide de la fonction FindSweepHits(). Cette fonction prend un vecteur de déplacement pour simuler le balayage d'un objet. Vous pouvez effectuer des balayages avec l'entité parente ou un volume de collision donné et spécifier la transformation globale de départ à partir de laquelle démarrer le balayage.
La fonction FindSweepHits() renvoie une liste de sweep_hit. Chaque sweep_hit vous donne les mêmes informations qu'un overlap_hit, notamment le composant ou le volume touché, ainsi que 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.
L'exemple suivant prend une entité et appelle FindSweepHit(), en transmettant un vecteur d'une longueur de 1 000,0 pour la valeur Forward. Le code simule ensuite les collisions qui se produiraient si l'entité était déplacée de 1 000,0 unités dans la direction avant positive, puis renvoie une liste de sweep_hit.
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. Si l'occurrence est un composant, le code obtient alors l'entité parente du composant. Enfin, il vérifie si l'entité possède un composant light. Si c'est le cas, la couleur de la lumière devient bleue.
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:
Ce code simule le balayage de l'entité cube de 1 000,0 unités dans la direction X positive, et renvoie tous les composants avec lesquels elle se chevauche. Il récupère ensuite l'entité parente du composant chevauché et transforme tout composant light en bleu. Notez que la liste des occurrences n'incluant pas l'entité qui effectue le balayage, le cube lui-même ne deviendra pas bleu. L'entité à cône rouge la plus à droite ne devient pas bleue non plus, car elle se trouve en dehors du balayage.
L'exemple suivant est semblable au précédent, sauf qu'il construit d'abord un volume collision_box, puis l'utilise pour effectuer un balayage de 1 000,0 unités dans la direction avant positive, en commençant par le centre de l'entité cube. Le cube se trouvant à l'intérieur de la collision_box lorsque le balayage démarre, il est inclus dans la liste des sweep_hits et devient également bleu.
# 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)
Ce code simule le balayage d'un volume collision_box indiqué par le carré jaune de 1 000,0 unités dans la direction avant positive, renvoyant tout composant avec lequel il se chevauche. Il récupère ensuite l'entité parente du composant chevauché et transforme tout composant light en bleu. Le cube se trouvant à l'intérieur de la collision_box lorsque le balayage démarre, il est inclus dans la liste des sweep_hits et devient bleu. L'entité à cône rouge la plus à droite ne devient pas bleue, car elle se trouve en dehors du balayage.
Générer et supprimer des entités avec Verse
Vous pouvez supprimer une entité de la scène en appelant RemoveFromParent() sur l'entité. Vous pouvez ajouter une entité à la scène, qu'il s'agisse d'une nouvelle entité ou d'une entité précédemment supprimée, en appelant AddEntities() sur l'entité qui devient l'entité parente.
Dans l'exemple suivant, le composant Verse recherche toutes les entités disposant de la balise de jeu my_tag. Il supprime chaque entité trouvée de son parent, ce qui l'élimine de la scène, et ajoute la même entité à son parent au bout de cinq secondes pour la réintroduire dans la scène.
# 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[]
De la même manière, vous pouvez ajouter des composants à une entité en appelant AddComponents() et en transmettant la liste des composants que vous souhaitez ajouter. Vous pouvez par ailleurs supprimer une entité en appelant RemoveFromParent() sur l'entité et supprimer un composant d'une entité en appelant RemoveFromEntity() sur le composant. Pour rajouter à la scène les entités et composants supprimés, utilisez respectivement AddEntities() et AddComponents(). Notez que vous ne pouvez pas modifier l'entité parente des composants supprimés que vous rajoutez à la scène.
Préfabriqués
Les préfabriqués que vous créez dans votre projet sont exposés à Verse en tant que classe dans le fichier Assets.digest.verse de votre projet. Les entités et composants définis dans votre préfabriqué sont accessibles dans Verse via les appels GetEntities() et GetComponents() sur un préfabriqué.
Vous pouvez générer des instances de vos préfabriqués en instanciant la classe de préfabriqué et en l'ajoutant à une entité de la scène. Dans l'exemple suivant, le composant Verse crée une instance du préfabriqué nommée loop_disappearing_platform_prefab dans l'éditeur et l'ajoute à la scène.
# 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{}
Pratiques recommandées et conseils
Lorsque vous créez vos propres composants dans Verse, gardez à l'esprit les pratiques recommandées et les conseils suivants :
Les composants Verse qui dépendent d'autres composants doivent généralement se trouver sur la même entité.
Les composants exposent les événements de tick avant l'application de la physique et après l'application de la physique qui se produisent à chaque image. Cela s'avère utile si vous devez appliquer une certaine logique avant l'application de la physique (notamment la modification de la transformation), mais aussi après l'application de la physique (par exemple pour lire les positions des objets).
Si vous n'avez pas besoin des événements survenus avant l'application de la physique ou après l'application de la physique, vous devez continuer à utiliser les expressions de concurrence Verse pour contrôler le flux temporel selon une certaine logique. Pour en savoir plus, consultez la page Flux temporel et concurrence.
Les fonctions de durée de vie des composants sont différentes des durées de vie des appareils. La logique de composant fonctionne à la fois en mode modification et en mode jeu. Si vous souhaitez que votre logique de composant ne s'exécute qu'au démarrage du jeu, vous pouvez générer des préfabriqués dans la fonction
OnBegin()d'un appareil Verse.