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
intidentificatore univoco utilizzato come chiave per un elenco di componenti. - Componente : contiene solo dati, ad es. Il
Positioncomponente contiene unaxeycoordinate e ilMovementcomponente contiene unaspeede unadirectionvariabile. - Sistema : componenti Maniglie, ad esempio riprende le
PositioneMovementcomponenti e aggiunge laspeededirectionalla posizione delxeycoordinate.
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 moveToo 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 Entitye fornisce funzioni come moveTodirettamente 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).
moveTometodo 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.
Systemclassi per consentire ai componenti di rimanere strutture di dati.