Ich fange gerade erst an, mich wirklich mit komponentenbasiertem Design zu beschäftigen. Ich weiß nicht, wie ich das "richtig" anstellen soll.
Hier ist das Szenario. Der Spieler kann einen Schild ausrüsten. Der Schild wird als Blase um den Spieler gezogen, hat eine eigene Kollisionsform und verringert den Schaden, den der Spieler durch Flächeneffekte erleidet.
Wie wird ein solcher Schild in einem komponentenbasierten Spiel aufgebaut?
Ich bin verwirrt, dass der Schild offensichtlich drei Komponenten aufweist.
- Schadensminderung / Filterung
- Ein Sprite
- Ein Collider.
Um es noch schlimmer zu machen, könnten verschiedene Schildvarianten noch mehr Verhaltensweisen haben, die alle Komponenten sein könnten:
- Steigert die maximale Gesundheit des Spielers
- Gesundheitsregeneration
- Ablenkung des Projektils
- etc
Überdenke ich das? Sollte der Schild nur eine Superkomponente sein?
Ich denke wirklich, das ist eine falsche Antwort. Wenn Sie also denken, dass dies der richtige Weg ist, erklären Sie es bitte.Sollte der Schild eine eigene Entität sein, die den Standort des Spielers verfolgt?
Dies könnte es schwierig machen, die Schadensfilterung zu implementieren. Außerdem verwischen die Linien zwischen angehängten Komponenten und Entitäten.Sollte der Schild eine Komponente sein, die andere Komponenten beherbergt?
Ich habe so etwas noch nie gesehen oder gehört, aber vielleicht ist es üblich und ich bin einfach noch nicht tief genug.Sollte der Schild nur eine Reihe von Komponenten sein, die dem Player hinzugefügt werden?
Möglicherweise mit einer zusätzlichen Komponente, um die anderen zu verwalten, z. B. damit alle als Gruppe entfernt werden können. (Lassen Sie aus Versehen die Schadensreduzierungskomponente hinter sich, das würde jetzt Spaß machen).Noch etwas, das für jemanden mit mehr Erfahrung mit Komponenten offensichtlich ist?
quelle
Antworten:
Edit: Ich denke, es gibt nicht genug "autonomes Verhalten" für eine getrennte Einheit. In diesem speziellen Fall folgt ein Schild dem Ziel, arbeitet für das Ziel und überlebt das Ziel nicht. Während ich der Meinung bin, dass das Konzept eines "Schildobjekts" nichts auszusetzen hat, handelt es sich in diesem Fall um ein Verhalten, das in eine Komponente passt. Ich bin aber auch ein Verfechter rein logischer Entitäten (im Gegensatz zu vollständigen Entitätssystemen, in denen Transform- und Rendering-Komponenten zu finden sind).
Sehen Sie es aus einer anderen Perspektive; Durch Hinzufügen einer Komponente werden auch andere Komponenten hinzugefügt, und beim Entfernen werden auch die zusätzlichen Komponenten entfernt.
Dies könnte eine Lösung sein, würde die Wiederverwendung fördern, ist jedoch auch fehleranfälliger (z. B. für das von Ihnen erwähnte Problem). Es ist nicht unbedingt schlecht. Vielleicht findest du neue Zauberkombinationen mit Versuch und Irrtum :)
Ich werde etwas näher darauf eingehen.
Ich glaube, Sie haben bemerkt, wie einige Komponenten Vorrang haben sollten, unabhängig davon, wann sie zu einer Entität hinzugefügt wurden (dies würde auch Ihre andere Frage beantworten).
Ich gehe auch davon aus, dass wir eine nachrichtenbasierte Kommunikation verwenden (aus Diskussionsgründen handelt es sich derzeit nur um eine Abstraktion über einen Methodenaufruf).
Immer wenn eine Schildkomponente "installiert" wird, werden die Meldungsbehandlungsroutinen für Schildkomponenten mit einer bestimmten (höheren) Reihenfolge verkettet.
Die "Statistik" -Komponente installiert einen "Schadens" -Meldungshandler im In / Invariant / Normal-Index. Verringern Sie die HP jedes Mal, wenn eine Schadensmeldung eingeht, um den entsprechenden Wert.
Ziemlich normales Verhalten (setzen Sie natürliche Schadensresistenz und / oder Rassenmerkmale ein, was auch immer).
Die Schildkomponente installiert einen "Schadens" -Meldungshandler im In / Pre / High-Index.
Sie sehen, dass dies ziemlich flexibel ist, obwohl dies eine sorgfältige Planung beim Entwerfen der Komponenteninteraktion erfordern würde, da Sie bestimmen müssen, in welchem Teil der Nachrichtenbehandlungspipeline Komponenten-Ereignishandler für Nachrichten installiert sind.
Macht Sinn? Lassen Sie mich wissen, ob ich weitere Details hinzufügen kann.
Bearbeiten: In Bezug auf Instanzen mit mehreren Komponenten (zwei Rüstungskomponenten). Sie können entweder die Gesamtzahl der Instanzen in nur einer Entitätsinstanz nachverfolgen (dies beendet jedoch den Status pro Komponente) und einfach weitere Ereignishandler hinzufügen oder sicherstellen, dass Ihre Komponentencontainer im Voraus doppelte Komponententypen zulassen.
quelle
Vielleicht hängt es davon ab, wie wiederverwendbar der Code sein soll und ob er sinnvoll ist.
Es sei denn, dieser Schild ist eine Art Kreatur, die sich irgendwann selbständig bewegen kann.
Das klingt sehr nach Einheit, daher lautet die Antwort nein.
Es ist wahrscheinlich.
quelle
Ein Schild als physische Einheit unterscheidet sich nicht von einer anderen physischen Einheit, z. B. einer Drohne, die Sie umkreist (und die tatsächlich selbst eine Art Schild sein könnte!). Machen Sie den Schild zu einer separaten logischen Einheit (damit er seine eigenen Komponenten aufnehmen kann).
Geben Sie Ihrem Schild ein paar Komponenten: eine physische / räumliche Komponente, um die Kollisionsform darzustellen, und eine DamageAffector-Komponente, die einen Verweis auf eine Entität enthält, der jedes Mal erhöhten oder verringerten Schaden zufügt (z. B. Ihren Spielercharakter) Halten des DamageAffector nimmt Schaden. So erleidet Ihr Spieler Schaden "per Proxy".
Setze die Position der Schildeinheit bei jedem Tick auf die Position des Spielers. (Schreiben Sie eine wiederverwendbare Komponentenklasse, die dies ausführt: einmal schreiben, mehrmals verwenden.)
Sie müssen das Schildelement erstellen, z. beim Sammeln eines Powerups. Ich verwende ein allgemeines Konzept namens Emitter, eine Art Entitätskomponente, die neue Entitäten erzeugt (normalerweise durch die Verwendung einer EntityFactory, auf die sie verweist). Wo Sie sich entscheiden, den Sender zu lokalisieren, liegt bei Ihnen - z. Schalten Sie das Gerät ein und lassen Sie es auslösen, sobald es eingesammelt ist.
Zwischen logischen Unterkomponenten (Raum-, KI-, Waffensteckplätze, Eingabeverarbeitung usw. usw.) und physischen Unterkomponenten besteht eine feine Grenze. Sie müssen entscheiden, auf welcher Seite Sie stehen, da dies stark definiert, welche Art von Entitätssystem Sie haben. Für mich behandelt die Physik-Unterkomponente meiner Entität die physik-hierarchischen Beziehungen (wie Gliedmaßen in einem Körper - denken Sie an Szenegraphenknoten), während die oben genannten Logik-Controller in der Regel die von Ihren Entitätskomponenten repräsentierten sind - und nicht diese repräsentieren individuelle physische "Spielpaarungen".
quelle
Enthält möglicherweise keine anderen Komponenten, steuert jedoch die Lebensdauer der Unterkomponenten. In einem groben Pseudocode würde Ihr Client-Code diese "Schild" -Komponente hinzufügen.
quelle
this
in Ihrer Antwort bedeutet. Beziehtthis
sich das auf die Schildkomponente oder meintest du die Entität, die den Schild verwendet, dessen Eltern? Die Verwirrung könnte meine Schuld sein. "Komponentenbasiert" ist ein bisschen vage. In meiner Version komponentenbasierter Entitäten ist eine Entität einfach ein Komponentencontainer mit einer eigenen minimalen Funktionalität (Objektname, Tags, Messaging usw.).gameObject
etwas benutzte oder so. Es ist ein Verweis auf das aktuelle Spielobjekt / Objekt / was auch immer die Komponenten besitzt.Wenn Ihr Komponentensystem Scripting zulässt, kann die Shield-Komponente fast eine Superkomponente sein, die nur ein Script für den Parameter "effect" aufruft. Auf diese Weise behalten Sie die Einfachheit einer einzelnen Komponente für Shields bei und verlagern die gesamte Logik dessen, was sie tatsächlich tut, in benutzerdefinierte Skriptdateien, die von Ihren Entitätsdefinitionen an Shields übergeben werden.
Ich mache etwas Ähnliches für meine Moveable-Komponente. Sie enthält ein Feld mit einem Schlüsselreaktionsskript (eine Unterklasse von Skripten in meiner Engine). Dieses Skript definiert eine Methode, die meiner Eingabenachricht folgt. Als solches kann ich einfach so etwas in meiner Tempalte-Definitionsdatei machen
dann registriere ich in meiner beweglichen Komponente während der Nachrichtenregistrierung die Do-Methode der Skripte (Code in C #)
Dies hängt natürlich von meiner Do-Methode ab, die dem Funktionsmuster meines RegisterHandlers folgt. In diesem Fall sein (IComponent Absender, ref Typ Argument)
also mein "script" (in meinem fall auch nur c # laufzeit kompiliert) definiert
und meine Basisklasse KeyReactionScript
dann später, wenn eine Eingabekomponente eine Nachricht vom Typ MessageTypes.InputUpdate mit dem Typ als solchem sendet
Die Methode im Skript, die an diese Nachricht und diesen Datentyp gebunden war, wird ausgelöst und verarbeitet die gesamte Logik.
Der Code ist ziemlich spezifisch für meine Engine, aber die Logik sollte auf jeden Fall funktionieren. Ich mache dies für viele Typen, um die Komponentenstruktur einfach und flexibel zu halten.
quelle