Entity Component System - Spielfortschritt


8

Ich bin ziemlich neu in der Spieleentwicklung (aber nicht in der Programmierung) und ich versuche herauszufinden, wie ich am besten mit der Kommunikation zwischen den Welten umgehen kann. Was ich meine ist das:

Ich habe über Entity Component Systems (ECS) gelesen und wie Leute vorschlagen, verschiedene Welten / Räume zu verwenden ( http://gamedevelopment.tutsplus.com/tutorials/spaces-useful-game-object-containers--gamedev-14091 ) für ein Unterabschnitt eines Spiels. Zum Beispiel erhalten ein HUD, ein Inventar oder ein Kampf / eine Bewegung jeweils eine separate Welt / einen eigenen Raum (weil sie unterschiedliche Grafiken und zugrunde liegende Logik haben.

Ich habe mich jedoch gefragt, wie das Inventar oder das HUD über die Gesundheit eines Spielers Bescheid weiß, wenn die Gesundheit von einem anderen Raum / einer anderen Welt behandelt wird, beispielsweise im Kampf.

Dies gilt auch für den Spielfortschritt im Allgemeinen, zum Beispiel für den Dialog mit dem NPC (ein Dialog wäre ein separater Bereich, da es sich um einen Popup-Bildschirm handelt). Wie würden Sie jedoch die im Dialog (oder im Status des Dialogs) getroffenen Entscheidungen auf andere Bereiche / Welten übertragen? . Oder im Grunde jede andere Art von Ereignis, die den Spielverlauf in verschiedenen Räumen / Welten beeinflusst (Gesundheit, Mana, Quests, Dialog, Kampf, Inventar, Hud usw.).

Wie würde man mit dieser Art von Design umgehen? Benötigt es ein (in der Implementierung) Singleton-Objekt, das alle diese Informationen enthält? Das wäre seltsam, weil dann die componentsNotwendigkeit besteht, jede Änderung an diesem Singleton-Objekt zu übermitteln, das sich anfühlt, als würde man Dinge zweimal tun (gegen den Haupt-DRY der Programmierung) ...

Ich bin hier irgendwie ratlos in Bezug auf Design, irgendwelche Hinweise?


---BEARBEITEN---

Ich habe ein paar andere Beiträge gelesen, die von Kommentaren vorgeschlagen wurden, und eine allgemeine Vorstellung von den Möglichkeiten bekommen, aber jeder von ihnen scheint einen großen Nachteil zu haben, der sie einfach nicht richtig macht. Es ist sehr wahrscheinlich, dass ich Details überwache, die diese Nachteile lösen würden. Sie können mich also gerne korrigieren. Ich werde versuchen, einen Überblick sowie einige Antworten auf einige Fragen zu geben.

Ich sehe drei Hauptoptionen, um Daten zwischen Leerzeichen zu teilen. Obwohl es in den meisten Beiträgen um den Datenaustausch zwischen Systemen geht, kann dies meines Erachtens auch für den Datenaustausch zwischen Systemen angewendet werden.

1. Abfragen

Beispiel : Wenn die HUD-Welt den aktuellen Zustand des Spielers kennen muss, kann sie eine andere Welt abfragen und nach dem aktuellen Zustand fragen.

Nachteil : Welten müssen voneinander wissen, was ein großes Abhängigkeitsproblem darstellt und der Entkopplung entgegensteht.

2: Direktnachrichten (synchron und asynchron)

Beispiel : Wenn sich während des Kampfes die Gesundheit eines Spielers ändert, kann er Nachrichten (synchronisieren und asynchronisieren, was auch immer benötigt wird) an andere Welten senden, die über diese Änderung Bescheid wissen müssen.

Nachteil : Immer noch das Problem der Entkopplung: Welten müssen voneinander wissen.

3: Indirektes Messaging (synchronisieren und asynchronisieren) <- beste Option

Beispiel : Wenn sich die Gesundheit eines Spielers während des Kampfes ändert, kann er Nachrichten (synchron und asynchron, was auch immer benötigt wird) an den allgemeinen Nachrichten-Hub senden. Andere Welten / Systeme, die über diese Änderung Bescheid wissen müssen, abonnieren den jeweiligen Nachrichtenkanal und lesen die Nachrichten.

Oberseite : Vollständig entkoppelt, leicht handhabbar und erweiterbar.

Nachteil / unklar : Wann weiß der Nachrichtenkanal, dass die Nachrichten gelöscht werden müssen? Oder vielleicht markiert das abonnierte System (nur für sich selbst) die Nachricht als gelesen und wartet auf neue Nachrichten -> Nachrichtenbox wird nach einer Weile enorm. Wie gehen Welten / Systeme mit Ordnung um? Beispiel: Während eines Frames: Wenn das HUD die Integritätsnachricht bereits abgefragt hat und sich danach der Zustand ändert, wird das HUD im nächsten Frame aktualisiert. Für einige Anwendungen ist dies möglicherweise nicht der richtige Weg.

F: Ein einzelnes Spielobjekt kann in mehreren Feldern vorhanden sein

Ich verwende das Artemis ECS-Framework, das integrierte Räume (sogenannte Welten) enthält. Jede Entität (und damit die Daten in Form von Komponenten) wird auf einer Welt erstellt und kann daher nicht zwischen Welten geteilt werden.


Messaging ist hier der Standardansatz: gamedev.stackexchange.com/questions/23834/…
MichaelHouse

Nach dem, was ich im verlinkten Artikel lesen kann, kann ein einzelnes Spielobjekt unter mehreren Leerzeichen existieren. Wenn Sie unterschiedliche Grafiken oder Logik zwischen Leerzeichen haben, trennen Sie die Daten von Grafiken und Logik. Teilen Sie das Datenspielobjekt über mehrere Räume hinweg und aggregieren Sie es mit verschiedenen Grafik- und Logikspielobjekten.
Andreas

Diese Antwort, die ich über Messaging-Systeme gegeben habe, kann Ihnen ebenfalls helfen: gamedev.stackexchange.com/questions/7718/…
James

Ich habe alle drei Lösungen (Abfragen, direkt und indirekt) in meinem Gamedev-Leben implementiert. Und ich kann sagen, dass die dritte Option für mich am besten funktioniert. Sie können Systeme einfach entkoppeln und ihre Logik parallel ausführen. Der einzige Nachteil ist, dass Sie 9 Funktionsaufrufe ausführen müssen, um jede einzelne Nachricht / jedes einzelne Ereignis von einem System zu einem anderen weiterzuleiten. Natürlich können Sie es optimieren und das große Plus ist, dass Sie bei diesem Ansatz keine Mutexe oder Singletons benötigen.
Gregory

@ Gregory Vielen Dank für Ihre Eingabe, ich habe erwartet, dass indirekte Nachrichten die beste Option sind. Die 9 Funktionsaufrufe waren mir nicht bekannt, aber als ich diesen Messagehub plante, wurde mir klar, dass es sich tatsächlich um einige Aufrufe handeln würde. Haben Sie jemals eine gute Lösung / Alternative zum Löschen von Nachrichten gefunden, wenn kein System sie mehr benötigt?
Tim

Antworten:


1

Eine Möglichkeit, dies zu betrachten, besteht darin, dass Sie möglicherweise zu viel in Ihre Spielobjekte stecken.

Es gibt keinen Grund , dass Code, der tatsächlich die HUD Haken bis zu Ihren in-World - Spiel Bedürfnisse in einem Bauteil / System in einem bestimmten Raum, Leben sein. Dieser Code ist möglicherweise besser geeignet, wenn Sie in einem zentralen Manager oder einem globalen Skript leben, das Zugriff auf alle Leerzeichen und alle Objekte hat, und dann mit dem Code interagieren können, der weiß, wann tatsächlich ein Leerzeichen erstellt werden muss und was in diese eingefügt werden muss (z. B. der Code) das bringt den Spieler hervor, speichert seinen Zustand zwischen den Levels usw.).

Sie könnten auch nur einen "Master-Bereich" haben, der Spielobjekte mit Logik oder Daten enthält, die in der Vergangenheit verbleiben oder die für Ebenen und Benutzeroberfläche verwendeten Bereiche manipulieren müssen. Dieser Ansatz ist in Engines üblich, die Entwickler dazu zwingen, alle Skripte / Logik auf Komponenten / Objekte zu setzen (z. B. würden Sie in Unity ein globales Hauptobjekt erstellen und es so einstellen, dass es über das Entladen der Szene hinweg bestehen bleibt. Wenn Unity tatsächlich Leerzeichen hat, haben Sie ' d Verwenden Sie diese anstelle der Flagge.

Denken Sie daran, dass der Missbrauch Ihres ECS mit dem Missbrauch von Entwurfsmustern identisch ist. Nur weil Sie ein geschicktes neues Tool haben, heißt das nicht, dass Sie es verwenden sollten, um jedes Problem zu lösen, auf das Sie stoßen. Bewerten Sie Ihren Problembereich und wählen Sie die am besten geeignete Lösung aus, auch wenn es das alte Ding ist, das Ihre Vorfahren in den dunklen Zeiten der 90er Jahre verwendet haben. : p


0

Ich habe ein paar Prototypen erstellt, aber nichts zu großes, und die Art und Weise, wie ich mit mehreren Feldern umgegangen bin, bestand darin, einfach ein Spielobjekt zu erstellen, das Welt, Spieler usw. enthält, und dann einige Eigenschaften einzurichten, die für einige andere Felder erforderlich sind, z. B. Gesundheit im Spielobjekt

Wann immer es gerufen wird, erhält es die Gesundheit des Spielers. Auf diese Weise konnte ich es an das HUD senden und die Gesundheitsleiste anzeigen.

Es ist nicht das sauberste, aber es erledigt den Job. Ich habe 2013 einige Leistungstests durchgeführt und alles schien reibungslos zu funktionieren. Um solche Abhängigkeiten zu vermeiden, können Sie die Gesundheitsleiste jederzeit löschen, wenn die Gesundheit des Spielers null ist.

Wenn der Player nicht vorhanden ist, bedeutet dies normalerweise, dass sich der Benutzer entweder in einem Menü oder in einer Zwischensequenz befindet.

Codebeispiel:

public float PlayerHealth {
  get {
    if (player !+ null) 
      return player.Health;
    return -1;
  }
}

Hoffe das ist was du gesucht hast.


0

Daran arbeite ich in den letzten Wochen. Ich arbeite an meiner eigenen ECS-Bibliothek (wollte das aus Erfahrung machen und nur ausprobieren, weil ich das schon seit einiger Zeit wollte).

Dies ist der Github-Link: https://github.com/gioragutt/xna-ecs

Für Ihr Problem habe ich immer eine kleine Pubsub-Bibliothek geschrieben, die Sie hier sehen können

Grundsätzlich habe ich eine EmsClientKlasse, aus der Sachen abgeleitet werden können. Derzeit machen meine Komponenten das nicht, aber die höheren Klassen, obwohl es keinen Grund gibt, dies nicht zu tun. Ich abonniere Namen von Nachrichten und versorge einen Rückruf mit der folgenden Signatur : Action<JObject>. Wie Sie bereits verstanden haben, verwende ich Json Objects als Mittel zum Übertragen von Nachrichten. Ich habe dies getan, nachdem ich zuvor nur byte[]'s' verwendet hatte, und festgestellt, dass ich etwas Allgemeineres brauchte, und da ich an so etwas von meinem Arbeitsplatz aus gewöhnt bin (wir haben eine IPCD, die mit Ausnahme des Rückrufs ähnlich funktioniert Methode ist immer die gleiche, da wir normalerweise die Verantwortung auf verschiedene Handler aufteilen).

Es gibt eine EmsServer(eine auf dem Server und eine auf jedem Client), die für das Verschieben von Nachrichten EmsClientin ihrem Bereich verantwortlich ist ( EmsServerauf der Serverseite werden Nachrichten EmsClientsauf der Serverseite zwischen verschoben, und umgekehrt auf der Clientseite).

Für Nachrichten zwischen dem Client und dem Server habe ich eine erstellt, EmsServerEndpointdie EmsClientselbst ist. Er führt lediglich die Logik aus, die gesendeten Nachrichten in seinem Bereich zu puffern und sie in andere Bereiche zu leeren (FE, der Client sendet die Nachricht an den Server, während wann Der Server überträgt jede Nachricht an alle verbundenen Clients.

Sie können die Verwendung an vielen Stellen sehen ClientGameManager, z ServerGameManager.

Wenn ich in Ihrem Beispiel eine GUI-Komponente für einen Spieler hinzufügen möchte, können Sie sich HIER die BeginAllocateLocalund BeginAllocateRemoteMethoden ansehen , die für die GameObjectsErstellung der Spieler verantwortlich sind. Jedes GameObjectenthält ein Entity(aus der ECS-Bibliothek) und ein IComponentContainer(auch aus der ECS-Bibliothek). Jeder GameObjectbekommt automatisch eine Transformation (wie in Unity, von der ich mich inspirieren ließ).

Mein Code spricht so ziemlich für sich selbst, und wenn Sie dazu kommen, suche ich Kritik dafür, also möchte ich konstruktive Kritik :)

Hoffe, dass mein Code Ihnen mit einigen Ideen helfen würde!


0

Betrachten Sie Beobachter- / Motivmusterkomponenten für Ihre Spielkomponenten und UI-Komponenten. Sie schließen sie beim Erstellen / Laden an und vergessen sie dann. Wenn sich die Gesundheit des Charakters ändert, werden alle Beobachter benachrichtigt, die mit den Informationen tun können, was sie wollen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.