Attualmente sto creando un piccolo progetto di hobby per tornare allo sviluppo del gioco e ho deciso di strutturare le mie entità utilizzando un ECS (Entity Component System). Questa implementazione di un ECS è strutturata in questo modo:
- Entità : nel mio caso è un
int
identificatore univoco utilizzato come chiave per un elenco di componenti. - Componente : contiene solo dati, ad es. Il
Position
componente contiene unax
ey
coordinate e ilMovement
componente contiene unaspeed
e unadirection
variabile. - Sistema : componenti Maniglie, ad esempio riprende le
Position
eMovement
componenti e aggiunge laspeed
edirection
alla posizione delx
ey
coordinate.
Funziona bene, ma ora desidero implementare lo scripting nei miei giochi, sotto forma di un linguaggio di scripting. In progetti precedenti ho usato un'implementazione OOP di oggetti di gioco, il che significava che lo scripting era piuttosto diretto. Ad esempio, un semplice script potrebbe assomigliare a questo:
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
Tuttavia, quando si utilizza un ECS, l'entità stessa non ha alcuna funzione simile moveTo
o getInventory
, invece lo script sopra scritto in stile ECS sarebbe simile a questo:
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
Questo è molto più dettagliato rispetto alla versione OOP, il che non è auspicabile quando lo scripting è rivolto principalmente a non programmatori (giocatori del gioco).
Una soluzione sarebbe quella di avere una sorta di oggetto wrapper che incapsula Entity
e fornisce funzioni come moveTo
direttamente e gestisca il resto internamente, sebbene tale soluzione sembri non ottimale poiché ci vuole molto lavoro per coprire tutti i componenti, e ogni ogni volta che viene aggiunto un nuovo componente, è necessario modificare l'oggetto wrapper con nuove funzioni.
A tutti gli sviluppatori di giochi che hanno già implementato lo scripting in un ECS: come hai fatto? L'obiettivo principale qui è l'usabilità per l'utente finale, con il minor costo di "manutenzione" possibile (preferibilmente non è necessario cambiarlo ogni volta che si aggiunge un componente).
moveTo
metodo come parte del sistema sottostante nel tuo caso d'uso, ad esempio MovementSystem? In questo modo non solo puoi usarlo negli script che scrivi ma puoi anche usarlo come parte del codice C ++ dove ne hai bisogno. Quindi sì, dovrete esporre nuovi metodi man mano che vengono aggiunti nuovi sistemi, ma ci si deve aspettare dal suo comportamento completamente nuovo introdotto da questi sistemi.
System
classi per consentire ai componenti di rimanere strutture di dati.