Ich weiß, dass diese Frage mehrmals gestellt wurde, bin mir aber immer noch nicht sicher, wie die Eingabebehandlung in einer komponentenbasierten Engine implementiert werden soll.
Das komponentenbasierte Design, das ich verwendet habe, basierte auf der Blogserie von T = Machine und auf Artemis, in denen Entities nur IDs sind.
Bei der Implementierung der Eingabehandhabung habe ich drei Hauptideen:
- Die Eingabekomponente enthält Ereignisse, an denen sie interessiert ist. Das Eingabesystem übersetzt Schlüssel- und Mausereignisse in Spielereignisse und durchläuft die Entitäten mit der Eingabekomponente. Wenn sie an dem Ereignis interessiert sind, ergreift das Eingabesystem eine geeignete Aktion. Diese Aktion wäre für das Eingabesystem fest codiert.
- Keine Eingabekomponente. Sie würden Entitäten mit bestimmten Ereignissen im Eingabesystem registrieren. Das Eingabesystem sendet dann Nachrichten (mit der Entitäts-ID und dem Ereignistyp) an andere Systeme, damit diese die entsprechenden Maßnahmen ergreifen können. Oder wie im ersten Fall würden die Aktionen für das Eingabesystem fest codiert.
- Ähnlich wie bei der ersten Methode, aber anstatt die Aktion fest in das Eingabesystem zu codieren, würde die Komponente eine Zuordnung von Ereignissen zu Funktionen (dh
std::map<std::function>
) enthalten, die vom Eingabesystem aufgerufen würden. Dies hat den zusätzlichen Effekt, dass dasselbe Ereignis mit verschiedenen Aktionen gekoppelt werden kann.
Würden Sie eine der oben genannten Methoden empfehlen oder haben Sie Vorschläge, die mir bei der Implementierung eines flexiblen Eingabeverarbeitungssystems helfen könnten? Außerdem bin ich noch nicht mit Multithreading vertraut, aber auch Vorschläge, die die Implementierung threadfreundlich machen würden, sind willkommen.
Hinweis: Eine zusätzliche Anforderung, die die Implementierung erfüllen soll, besteht darin, dass ich die gleiche Eingabe an viele Entitäten übergeben kann, z. B. das gleichzeitige Verschieben einer Kameraentität und des Players.
quelle
Antworten:
Ich denke, genau wie bei meiner Antwort auf Materialien in einem Komponentensystem stoßen Sie auf ein Problem, bei dem Sie versuchen, alles in eine "Komponente" zu verschieben. Sie müssen dies nicht tun, und dabei erstellen Sie wahrscheinlich eine wirklich umständliche Oberfläche, indem Sie versuchen, ein paar quadratische Stifte in runde Löcher zu stecken.
Es hört sich so an, als hätten Sie bereits ein System, das die Erfassung von Eingaben vom Player übernimmt. Ich würde mich für einen Ansatz entscheiden, der diese Eingaben dann in Aktionen ("vorwärts bewegen" oder "rückwärts bewegen") oder Ereignisse übersetzt und diese an interessierte Parteien weiterleitet. In der Vergangenheit habe ich es Komponenten untersagt, sich für diese Ereignisse zu registrieren , und einen Ansatz bevorzugt, bei dem das übergeordnete System die "kontrollierte Entität" explizit ausgewählt hat. Wenn Sie es vorziehen, könnte es auch anders funktionieren, insbesondere wenn Sie dieselben Nachrichten erneut verwenden, um Aktionen auszuführen, die nicht direkt durch Eingaben angeregt wurden.
Ich würde jedoch nicht unbedingt vorschlagen, das Verhalten der Kameraverfolgung zu implementieren, indem sowohl die Kameraentität als auch die Spielerentität auf die Nachricht "Vorwärts bewegen" (usw.) reagieren. Dies schafft eine extrem starre Verbindung zwischen den beiden Objekten, die sich für den Spieler wahrscheinlich nicht gut anfühlt, und es macht es auch etwas schwieriger, Dinge wie die Umlaufbahn der Kamera mit dem Spieler zu handhaben, wenn sich der Spieler nach links oder rechts dreht: Sie haben eine Entität Antworten auf "Nach links drehen", indem angenommen wird, dass es dem Spieler unterstellt ist, aber das bedeutet, dass es nicht richtig reagieren kann, wenn es jemals versklavt wurde ... es sei denn, Sie führen dieses Konzept als einen Zustand ein, den Sie überprüfen können. Und wenn Sie dies tun möchten, können Sie auch ein geeignetes System zum Zusammenfügen von zwei physischen Objekten implementieren, einschließlich geeigneter Elastizitätsanpassungen und so weiter.
In Bezug auf Multithreading sehe ich hier keine Notwendigkeit, es einzusetzen, da es wahrscheinlich mehr Komplikationen verursachen würde, als es wert ist, und Sie haben es mit einem inhärent seriellen Problem zu tun, sodass Sie nur viel Thread einbeziehen müssen Synchronisationsprimitive.
quelle
Meine Erfahrung mag voreingenommen sein, aber in Projekten mit mehreren Plattformen sind die Eingabegeräte nicht direkt dem Entitätssystem ausgesetzt.
Die Eingabegeräte werden von einem untergeordneten System verwaltet, das die Ereignisse von den Tasten, Tasten, Achsen, Mäusen, Berührungsflächen, Beschleunigungsmessern ... empfängt.
Diese Ereignisse werden dann über eine Schicht kontextabhängiger Absichtsgeneratoren gesendet.
Jeder Generator registriert für Zustandsänderungen von Komponenten, Entitäten und Systemen, die für seine Funktionen relevant sind.
Diese Generatoren senden dann Nachrichten / Absichten zum Weiterleiten an das Absichtssystem, in dem Entitäten eine Komponente haben, oder direkt an die richtigen Komponenten.
Auf diese Weise können Sie sich einfach darauf verlassen, dass "immer" dieselbe Eingabe vorliegt, dh JUMP_INTENT (1), JUMP_INTENT (0), AIM_INTENT (1) ...
Und "alle" schmutzigen plattformabhängigen Eingabearbeiten bleiben außerhalb Ihres Entitätssystems.
Wenn Sie die Kamera im Player bewegen möchten, kann sie ihre eigene Absichtskomponente registrieren und die von Ihnen gesendeten Absichten anhören.
Andernfalls sollte der Player, wenn er ihm folgt, niemals Eingaben hören, die für den Player bestimmt sind. Es sollte die vom Player ausgegebenen Statusänderungen abhören (ENTITY_MOVED (transformieren)) ... und sich entsprechend bewegen. Wenn Sie ein Physiksystem verwenden, können Sie die Kamera sogar über eines der verschiedenen Gelenke am Player befestigen.
quelle
Welchen Nutzen bringt eine InputComponent? Sicherlich ist es das Vorrecht des Eingabebefehls, zu entscheiden, für welche Entitäten eine Aktion ausgeführt wird. Das klassische Beispiel ist das, den Spieler zum Springen zu bringen. Anstatt eine InputComponent für jede Entität zu haben, die auf "Sprung" -Ereignisse wartet, lassen Sie den Sprungbefehl die Entität mit der Bezeichnung "Spieler" suchen und die erforderliche Logik selbst ausführen.
Ein weiteres Beispiel aus dem OP:
quelle