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).
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.
System
/ les classe (s) pour permettre aux composants de rester des structures de données.