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).
quelle
System
Klasse (n) spezialisiert sein, damit die Komponenten Datenstrukturen bleiben können.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.Antworten:
Sie können ein System ScriptExecutionSystem erstellen, das auf allen Entitäten mit einer Skriptkomponente ausgeführt wird. Es erhält alle Komponenten der Entität, die nützlich sein könnten, um sie dem Skriptsystem zugänglich zu machen, und übergibt diese an die Skriptfunktion.
Ein anderer Ansatz wäre, Ihre Benutzer dazu zu bringen, auch ECS zu nutzen und ihnen zu ermöglichen, ihre eigenen Komponenten zu definieren und ihre eigenen Systeme mithilfe der Skriptsprache zu implementieren.
quelle
ECS hat seine Vor- und Nachteile. Benutzerfreundliches Scripting gehört nicht zu seinen Profis.
Das Problem, das ECS löst, ist die Fähigkeit, eine große Anzahl ähnlicher Dinge gleichzeitig in Ihrem Spiel zu haben und gleichzeitig die Leistung beizubehalten. Diese Lösung hat jedoch ihre Kosten - die Kosten einer benutzerfreundlichen Architektur. Es ist nicht die beste Architektur für jedes Spiel.
Zum Beispiel wäre ECS eine gute Wahl für Space Invaders gewesen , aber nicht so sehr für PacMan .
Also nicht genau die Antwort, nach der Sie gesucht haben, aber es ist möglich, dass ECS einfach nicht das richtige Werkzeug für Ihren Job ist.
Wenn Sie einen Wrapper hinzufügen, beachten Sie die Gemeinkosten. Wenn Sie am Ende die Leistungssteigerung von ECS in Ihrem Wrapper entfernen, haben Sie das Schlimmste aus beiden Welten.
Aber um Ihre Frage direkt zu beantworten: "An alle Spieleentwickler, die zuvor Skripte in einem ECS implementiert haben - wie haben Sie das gemacht?"
Ziemlich genau so, wie Sie es tun, ohne Wrapper. Entitäten haben nichts als eine Kennung. Komponenten haben nichts als Daten. Systeme haben nichts als Logik. Systeme, die Entitäten mit den erforderlichen Komponenten akzeptieren, werden durchlaufen. Fügen Sie Systeme, Entitäten und Komponenten frei hinzu.
Ich habe einmal ein Framework mit einem vierten Aspekt verwendet, eine Tafel. Es war im Grunde eine Möglichkeit für Systeme, miteinander zu kommunizieren. Es hat mehr Probleme geschaffen als gelöst.
Verwandte: Sollte ich Entity Component System in allen meinen Projekten implementieren?
quelle
Mit ECS können Sie auf eine einzelne Verantwortung aufteilen, sodass jede sich bewegende Entität zwei Datenkomponenten benötigt: eine MoveComponent und eine MoveSpeedComponent.
Jetzt fügen Sie bei Ihrer Konvertierung diese Komponenten Ihren Entitäten hinzu
Nachdem wir nun Konvertierung und Daten haben, die wir in das System verschieben können, habe ich das Eingabesystem aus Gründen der Lesbarkeit entfernt. Wenn Sie jedoch mehr über das Eingabesystem erfahren möchten, werden alle in meinem Artikel nächste Woche über Unity Connect aufgelistet.
Beachten Sie, dass die obige Klasse Unity.Mathmatics verwendet. Dies ist ideal, um verschiedene mathematische Funktionen verwenden zu können, mit denen Sie in normalen Systemen gewohnt sind. Damit können Sie jetzt am Verhalten der Entitäten arbeiten. Ich habe die Eingabe hier wieder entfernt, aber das alles wird im Artikel viel besser erklärt.
Jetzt können Sie Entitäten einführen, die sich mit einer Geschwindigkeit vorwärts bewegen.
Dadurch wird jedoch auch jede Entität mit diesem Verhalten verschoben, sodass Sie Tags einführen können. Wenn Sie beispielsweise ein PlayerTag hinzugefügt haben, kann nur die Entität mit dem PlayerTag IComponentData den MoveForward ausführen, wenn ich den Player nur wie im Beispiel verschieben möchte unten.
Darauf werde ich auch im Artikel näher eingehen, aber in einem typischen ComponentSystem sieht es so aus
Vieles davon wird in der Angry Dots-Präsentation mit Mike Geig ziemlich gut erklärt. Wenn Sie es noch nicht gesehen haben, empfehle ich, es sich anzusehen. Ich werde auch auf meinen Artikel verweisen, wenn er fertig ist. Sollte wirklich hilfreich sein, um einige der Dinge zu bekommen, mit denen Sie es gewohnt sind, so zu arbeiten, wie Sie es in ECS möchten.
quelle