Je suis en train de créer un petit projet de loisir pour me remettre au développement de jeux, et j'ai décidé de structurer mes entités à l'aide d'un ECS (Entity Component System). Cette implémentation d'un ECS est structurée comme suit:
- Entité : dans mon cas, il s'agit d'un
int
identifiant unique utilisé comme clé d'une liste de composants. - Composant : ne
Position
contient que des données, par exemple, le composant contient unex
et uney
coordonnée, et leMovement
composant contient une variablespeed
etdirection
. - Système : composants Poignées, par exemple , il prend les
Position
etMovement
composants et ajoute lespeed
etdirection
à la position dex
ety
coordonnées.
Cela fonctionne bien, mais maintenant je souhaite implémenter des scripts dans mes jeux, sous la forme d'un langage de script. Dans les projets précédents, j'ai utilisé une implémentation OOP d'objets de jeu, ce qui signifiait que les scripts étaient assez simples. Par exemple, un script simple pourrait ressembler à ceci:
function start()
local future = entity:moveTo(pos1)
wait(future)
local response = entity:showDialog(dialog1)
if wait(response) == 1 then
local itemStack = entity:getInventory():removeItemByName("apple", 1)
world:getPlayer():getInventory():addItemStack(itemStack)
else
entity:setBehavior(world:getPlayer(), BEHAVIOR_HOSTILE)
end
end
Cependant, lors de l'utilisation d'un ECS, l'entité elle-même n'a aucune fonction comme moveTo
ou getInventory
, à la place, le script ci-dessus écrit en style ECS ressemblerait à ceci:
function start()
local movement = world:getComponent(MOVEMENT, entity)
movement:moveTo(pos1)
local position = world:getComponent(POSITION, entity)
local future = Future:untilEquals(position.pos, pos1)
wait(future)
local dialogComp = world:getComponent(DIALOG, entity)
local response = dialogComp:showDialog(dialog1)
if wait(response) == 1 then
local entityInventory = world:getComponent(INVENTORY, entity)
local playerInventory = world:getComponent(INVENTORY, world:getPlayer())
local itemStack = entityInventory:removeItemByName("apple", 1)
playerInventory:addItemStack(itemStack)
else
local entityBehavior = world:getComponent(BEHAVIOR, entity)
local playerBehavior = world:getComponent(BEHAVIOR, world:getPlayer())
entityBehavior:set(playerBehavior, BEHAVIOR_HOSTILE)
end
end
C'est beaucoup plus verbeux par rapport à la version OOP, ce qui n'est pas souhaitable lorsque les scripts sont destinés principalement à des non-programmeurs (joueurs du jeu).
Une solution serait d'avoir une sorte d'objet wrapper qui encapsule une Entity
et fournit des fonctions telles que moveTo
directement, et gère le reste en interne, bien qu'une telle solution semble sous-optimale car il faut beaucoup de travail pour couvrir tous les composants, et chaque chaque fois qu'un nouveau composant est ajouté, vous devrez modifier l'objet wrapper avec de nouvelles fonctions.
À tous les développeurs de jeux qui ont déjà implémenté des scripts dans un ECS - comment avez-vous procédé? L'objectif principal ici est la convivialité pour l'utilisateur final, avec le moins de frais de "maintenance" possible (de préférence, vous n'avez pas besoin de le changer chaque fois que vous ajoutez un composant).
la source
System
/ les classe (s) pour permettre aux composants de rester des structures de données.moveTo
méthode en tant que partie du système sous-jacent dans votre cas d'utilisation, par exemple MovementSystem? De cette façon, vous pouvez non seulement l'utiliser dans les scripts que vous écrivez, mais vous pouvez également l'utiliser dans le code C ++ là où vous en avez besoin. Alors oui, vous devrez exposer de nouvelles méthodes au fur et à mesure que de nouveaux systèmes seront ajoutés, mais c'est normal car son tout nouveau comportement introduit de toute façon ces systèmes.Réponses:
Vous pouvez créer un système ScriptExecutionSystem qui fonctionne sur toutes les entités avec un composant Script. Il obtient tous les composants de l'entité qui pourraient être utiles pour être exposés au système de script et les transmet à la fonction scriptée.
Une autre approche consisterait à faire en sorte que vos utilisateurs adoptent également ECS et leur permettent de définir leurs propres composants et d'implémenter leurs propres systèmes à l'aide du langage de script.
la source
ECS a ses avantages et ses inconvénients. Les scripts conviviaux ne sont pas l'un de ses avantages.
Le problème résolu par ECS est la possibilité d'avoir un grand nombre de choses similaires dans votre jeu en même temps tout en conservant les performances. Mais cette solution a un coût - le coût d'une architecture facile à utiliser. Ce n'est pas la meilleure architecture pour chaque jeu.
Par exemple, ECS aurait été un bon choix pour Space Invaders , mais pas tant pour PacMan .
Ce n'est donc pas exactement la réponse que vous cherchiez, mais il est possible qu'ECS ne soit tout simplement pas le bon outil pour votre travail.
Si vous ajoutez un wrapper, surveillez les frais généraux. Si vous finissez par supprimer l'amélioration des performances d'ECS dans votre wrapper, alors vous avez le pire des deux mondes.
Mais pour répondre directement à votre question - "À tous les développeurs de jeux qui ont déjà implémenté des scripts dans un ECS - comment avez-vous procédé?"
À peu près exactement comme vous le faites, sans emballage. Les entités n'ont qu'un identifiant. Les composants n'ont que des données. Les systèmes n'ont que de la logique. Les systèmes qui acceptent des entités avec les composants requis sont exécutés. Ajoutez librement des systèmes, des entités et des composants.
J'ai utilisé une fois un cadre avec un quatrième aspect, appelé un tableau noir. C'était essentiellement un moyen pour les systèmes de communiquer entre eux. Il a créé plus de problèmes qu'il n'en a résolu.
Connexes: Dois-je implémenter Entity Component System dans tous mes projets?
la source
Avec ECS, vous pouvez diviser en une seule responsabilité, donc toute entité qui se déplace voudrait deux composants de données: un MoveComponent et un MoveSpeedComponent.
maintenant dans votre conversion, vous ajoutez ces composants à vos entités
Maintenant que nous avons la conversion et les données que nous pouvons déplacer vers le système, j'ai supprimé le système d'entrée pour plus de lisibilité, mais si vous souhaitez en savoir plus sur le système d'entrée, je les ai toutes répertoriées dans mon article de la semaine prochaine sur Unity Connect.
notez que la classe ci-dessus utilise les Unity.Mathmatics. C'est idéal pour pouvoir utiliser différentes fonctions mathématiques avec lesquelles vous avez l'habitude de travailler dans les systèmes normaux. Avec tout cela en ligne, vous pouvez maintenant travailler sur le comportement des entités - encore une fois, j'ai supprimé l'entrée ici, mais cela est beaucoup mieux expliqué dans l'article.
Vous pouvez maintenant introduire des entités qui avanceront à grande vitesse.
Mais cela déplacera également chaque entité avec ce comportement afin que vous puissiez introduire des balises, par exemple si vous avez ajouté un PlayerTag, alors seule l'entité avec le playerTag IComponentData pourra effectuer le MoveForward si je veux seulement déplacer le joueur comme l'exemple au dessous de.
Je vais approfondir cela également dans l'article, mais cela ressemble à ceci dans un ComponentSystem typique
Une grande partie de cela est assez bien expliquée dans la présentation d'Angry Dots avec Mike Geig, si vous ne l'avez pas encore vu, je vous recommande de le vérifier. Je signalerai également mon article une fois qu'il sera terminé. Cela devrait vraiment être utile pour obtenir plusieurs de ces choses avec lesquelles vous êtes habitué à travailler comme vous le souhaitez dans ECS.
la source