Ich erstelle gerade ein kleines Hobbyprojekt, um wieder in die Spieleentwicklung einzusteigen, und ich habe beschlossen, meine Entitäten mithilfe eines ECS (Entity Component System) zu strukturieren. Diese Implementierung eines ECS ist wie folgt strukturiert:
- Entität : In meinem Fall handelt es sich um eine eindeutige
int
Kennung, die als Schlüssel für eine Liste von Komponenten verwendet wird. - Komponente : Enthält nur Daten, z. B. enthält die
Position
Komponente einex
und einey
Koordinate und dieMovement
Komponente einespeed
und einedirection
Variable. - System : Behandelt Komponenten, z. B. nimmt es die
Position
undMovement
Komponenten und fügt dasspeed
unddirection
zu den Positionenx
undy
Koordinaten hinzu.
Das funktioniert gut, aber jetzt möchte ich Skripte in Form einer Skriptsprache in meine Spiele implementieren. In früheren Projekten habe ich eine OOP-Implementierung von Spielobjekten verwendet, was bedeutete, dass die Skripterstellung ziemlich einfach war. Ein einfaches Skript könnte beispielsweise so aussehen:
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
Wenn Sie jedoch ein ECS verwenden, hat die Entität selbst keine Funktionen wie moveTo
oder getInventory
, stattdessen würde das obige Skript im ECS-Stil ungefähr so aussehen:
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
Dies ist im Vergleich zur OOP-Version viel ausführlicher, was nicht wünschenswert ist, wenn sich das Scripting hauptsächlich an Nicht-Programmierer (Spieler des Spiels) richtet.
Eine Lösung wäre, eine Art Wrapper-Objekt zu haben, das ein kapselt Entity
und Funktionen wie moveTo
direkt bereitstellt und den Rest intern erledigt, obwohl eine solche Lösung nicht optimal erscheint, da es viel Arbeit erfordert, alle und alle Komponenten abzudecken Wenn eine neue Komponente hinzugefügt wird, müssen Sie das Wrapper-Objekt mit neuen Funktionen ändern.
An alle Spieleentwickler, die zuvor Skripte in einem ECS implementiert haben - wie haben Sie das gemacht? Das Hauptaugenmerk liegt hier auf der Benutzerfreundlichkeit für den Endbenutzer bei möglichst geringen "Wartungskosten" (vorzugsweise müssen Sie diese nicht jedes Mal ändern, wenn Sie eine Komponente hinzufügen).
moveTo
Methode in Ihrem Anwendungsfall nicht als Teil des zugrunde liegenden Systems verfügbar, z. B. MovementSystem? Auf diese Weise können Sie es nicht nur in den von Ihnen geschriebenen Skripten verwenden, sondern auch dort, wo Sie es benötigen, als Teil des C ++ - Codes verwenden. Ja, Sie müssen neue Methoden verfügbar machen, wenn neue Systeme hinzugefügt werden, aber das ist zu erwarten, da diese Systeme ohnehin ein völlig neues Verhalten einführen.
System
Klasse (n) spezialisiert sein, damit die Komponenten Datenstrukturen bleiben können.