Objektaktualisierungen, intern oder extern?

8

Der Titel ist etwas verwirrend, aber ich kann mir nicht vorstellen, wie ich meine Frage in einem kurzen Satz erklären soll. Hier ist es also:

Immer wenn ich Game Engines schreibe, egal ob es sich um Physik / Kacheln usw. handelt, komme ich an den Punkt, an dem ich nicht sicher bin, wie ich die Dinge verwalten soll. Sollten Entitäten auf der Welt alleine handeln oder sollte es ein globales System geben, das sie verwaltet?

Hier ist ein einfaches Beispiel: Dinge bewegen. Sollte jedes Objekt die Welt um sich herum sehen (auf Kollisionen prüfen) und sich darauf basierend bewegen.

[Hinweis: Dies ist ein auf Kacheln basierendes Spiel, bei dem sich Objekte pro Kachel bewegen. Ich verwende also keine Physik, um von Kachel zu Kachel zu wechseln.]

public class Actor : GameObject
    {
        private void MoveTo(Vector2 location)
        {
            if (world.getTile(location) != solid && world.objAtTile(location) == null)
            {
                Tweener.addTween(this, location);
            }
          }
        }

Oder sollte die Bewegung jedes Objekts in der Welt gehandhabt werden, in der die Welt alles überprüft?

public class Actor : GameObject
    {
        private void MoveTo(Vector2 location)
        {
            world.moveTo(location);
          }
        }

public class World
{

    public void moveObject(GameObject obj, Vector2 location)
    {
      //called from object

      if (getTile(location) != solid && objAtTile(location) == null)
         {
                Tweener.addTween(obj, location);
         }
    }
}

Für dieses Beispiel ist es nicht wirklich wichtig, aber ich kann sehen, dass ich später in Schwierigkeiten gerate. Danke im Voraus.

Omgnoseat
quelle
1
Mehr zur Frage, sollte man überhaupt Actorwissen world?
Verlangsamte
1
Ich denke, dass Sie Objekte mit Komponenten in Konflikt bringen, und das verwirrt die Frage, wer was tut. Dies kann ein semantisches Problem sein, da Component ein stark überladener Begriff ist. Ich denke, wenn Sie klarstellen, was ein Objekt ist, was Sie unter Komponente verstehen und wie Sie die kleinen Engines organisieren, mit denen die Arbeit tatsächlich erledigt wird, können Sie Ihre eigene Frage beantworten.
Patrick Hughes
1
Ich muss anderen zustimmen, dass Sie fragen, ob sich das Spielobjekt selbst bewegt oder ob die Welt das Spielobjekt bewegt. Der obige Code lässt mich nicht glauben, dass Sie wirklich ein Komponententypsystem meinen, und kann daher irreführend sein, wenn Sie den Titel Ihrer Frage verwenden.
James
Ich habe jetzt, da ihr es erwähnt, einige irreführende Begriffe verwendet. Ich werde es jetzt anpassen! Edit: Siehe es wurde bereits geändert, danke!
Omgnoseat
@daniel Schauspieler müssen sich mit der Welt um sie herum auskennen, um sich effektiv bewegen zu können, obwohl ich normalerweise um einen Container herum entwerfen würde, der beide einschließt, aber keiner weiß davon. z.B. Die Welt enthält sowohl Schauspieler als auch Level. Der Schauspieler kennt das Level (oder wird bei Bedarf über das Level informiert, aber keine permanente Referenz), aber nicht über die Welt.
Schockieren

Antworten:

2

Ich sehe, dass Ihre Beispiele in Java sind, aber Ihre Tags geben keine Sprache an. In C ++ lautet die Antwort weder - Sie sollten dafür keine Mitgliedsfunktion verwenden!

void move_to(actor *a, world *w, vec2 location);

quelle
4
Ich muss sagen, großer Fan der Bücher, die der Autor des Artikels geschrieben hat. Die Argumentation des Artikels ist jedoch, soweit ich das beurteilen kann, eine gute Theorie, aber kein praktischer Code. Indem Sie Methoden wie diese aus den Objekten entfernen, die Sie betreffen, fügen Sie weitere Speicherorte hinzu, um nach Funktionen zu suchen, die nur Verwirrung stiften und einen höheren Wartungsaufwand verursachen. Nur meine Sicht darauf, also kein + oder -. Nur zwei Cent.
James
1
Außerdem muss gesagt werden, dass C # mit der Vererbung wie folgt aussieht: ClassName anstelle von ClassName.
James
Es ist Pseudocode basierend auf C #, aber das sollte für meine Frage nicht relevant sein :)
Omgnoseat
1
@omgnoseat: Es ist relevant, weil der Rat in einer Sprache ohne bloße Funktionen unmöglich ist und seine Nützlichkeit in jeder Sprache ohne ADL eingeschränkt ist. Gute Architektur ist nicht unabhängig von der Wahl der Sprache.
2
@James: Es gibt nichts Nicht-OO an nackten Funktionen, wenn die Funktionen selbst Methoden aufrufen, die den OO-Entwurfsprinzipien folgen. Darum geht es in dem Artikel. (OO ist auch nicht gleichbedeutend mit gutem Design. Wenn ein klares oder einfaches Design erfordert, dass Sie kein OO verwenden, lassen Sie OO
1

Es gibt keine einfache Antwort.

Wie bei den meisten Dingen in der Programmierung ist es ein Kompromiss. Wenn Sie einzelnen Objekten mehr Leistung verleihen, werden sie größer und damit langsamer, aber der Motor ist leichter zu verstehen und zu erweitern. Eine Mega-Klasse zu haben, die alles zusammen bewältigen kann, ist zwar schneller, aber auf Kosten einer Mega-Klasse. Das heißt, es wird allgemein als schlechte Form angesehen, supermassive Klassen zu machen.

Ich habe einige Artikel über komponenten- und datengesteuertes Design gelesen, in denen Sie eine einzige Klasse haben, die alle Objekte eines bestimmten Typs darstellt, deren Daten in Listen speichert und nur Indizes weitergibt, um Eigenschaften eines einzelnen Objekts abzurufen. Obwohl ich dies als eine realisierbare Art von Architektur ansehen kann, denke ich, dass sie eher mit dem ganzen Punkt der Objektorientierung zusammenhängt, der auch einen angemessenen Anteil an Kritik erhalten hat.

Ich persönlich empfehle, den Objekten mehr Kraft zu geben. In einer objektorientierten Sprache ist dies sinnvoller und (wie ich mir vorstellen kann) im Laufe der Zeit leichter zu verstehen und zu pflegen.

Quasiperfekt
quelle
Sie unterschätzen die Dinge etwas, indem Sie sagen, dass der von Ihnen beschriebene Stil des komponenten- und datengesteuerten Designs "Schrauben mit dem ganzen Sinn von OO" ist - es ist überhaupt nicht OO . Zumindest wie in "Entitätssysteme sind die Zukunft der MMOG-Entwicklung" beschrieben (in der Antwort von pek verknüpft), sind Entitätssysteme ein völlig anderes Programmierparadigma. Wenn Sie versuchen, Ihre Komponenten oder Entitäten über den einfachen Verpackungskomfort hinaus zu OO zu machen, Es falsch machen.
Dave Sherohman
6
Sie sind perfekt objektorientiert. Es ist einfach nicht das OO, an das die meisten Leute gewöhnt sind, aber das ist in Ordnung. Bei der Objektorientierung geht es darum, den Zustand mit den Methoden zu koppeln, die mit diesem Zustand arbeiten. Das ist alles. Es ist nicht erforderlich, dass jedes Objekt ein reales Objekt oder ähnliches sein muss.
Kylotan
@Kylotan: Auch hier spreche ich speziell von Modellen wie den in "Entity Systems sind die Zukunft der MMOG-Entwicklung" beschriebenen, bei denen Komponenten dumme Daten sind, die von externen "Systemen" bearbeitet werden. Sie sind keine Objekte, da sie kein Verhalten / keine Methoden haben; Sie sind Beziehungen (denken Sie an Datenbankzeilen oder C-Strukturen). Eine falsche Äquivalenz zwischen OOP und alltäglichem Gebrauch von "Objekt" hat nichts damit zu tun. Aus Teil 3: "Ich bin entschieden dagegen, OOP irgendwo in einer ES-Implementierung zu verwenden. Es ist zu einfach, OOP wieder dorthin zu schleichen, wo es unangemessen ist, und sich zu täuschen, dass dies eine gute Idee ist."
Dave Sherohman
Ich bin mir des Artikels bewusst, aber es ist nur die Meinung einer Person, wie diese Dinge zu tun sind - es ist nicht der Standardweg oder notwendigerweise der beste Weg, daher ist es nicht richtig zu sagen, dass komponentengesteuertes Design ein völlig anderes ist Programmierparadigma. Es ist durchaus möglich - und weitaus häufiger -, eine komponentenbasierte Architektur mit OO-Code zu implementieren, und die Verwendung von OO-Code schadet der "Komponente" nicht in irgendeiner Weise mehr als die Verwendung gespeicherter Prozeduren den relationalen Aspekt einer Datenbank .
Kylotan
@ Dave, diese Frage bezieht sich keineswegs auf Entitätskomponentensysteme. Weder ES noch datengesteuertes Design sind nicht OO, das ist ein häufiger, aber großer Fehler, dies anzunehmen. Wenn Sie danach googeln, finden Sie einige gute Artikel, die diesen Mythos aufdecken. Bei dieser Frage geht es jedoch um allgemeine OO-Prinzipien für das Entwerfen Ihrer Objekte. Übergeordnete Entwurfsmuster beantworten diese Frage überhaupt nicht.
Maik Semder
0

Ich aktualisiere meine Antwort, weil viele Dinge vor den Kommentaren nicht klar waren. Bitte entblößen Sie mich, während ich meine Gedanken erkläre.

Im Allgemeinen sind Kohäsion und Kopplung zwei Schlüsselaspekte, die bei jedem Entwurf berücksichtigt werden müssen . Wir alle wissen, dass wir eine hohe Kohäsion und eine geringe Kopplung benötigen, um ein wiederverwendbareres und erweiterbares Design zu erhalten.

Wenn die Welt also alles verwalten muss, bedeutet dies, dass sie einen geringen Zusammenhalt und eine enge Kopplung aufweist (weil sie alles wissen und tun muss). Dies ist jedoch auch dann der Fall, wenn eine Spieleinheit alles tun muss. Aktualisieren Sie seinen Standort, rendern Sie seine Textur usw. usw.

Was Sie wirklich interessiert, ist die Erstellung von Systemen, die sich auf einen Aspekt der Entität konzentrieren. Zum Beispiel könnte eine Spielentität eine Textur haben, aber ein Renderer wäre dafür verantwortlich, diese Textur auf dem Bildschirm zu rendern. Dem Renderer ist es egal, über welche anderen Eigenschaften die Entität verfügt.

Wenn man etwas weiter geht, ist eine Spieleinheit einfach eine Tüte mit Eigenschaften. Diese Eigenschaften werden von Systemen manipuliert, die sich auf bestimmte Eigenschaften konzentrieren. Und hier kommen komponentenbasierte Entitätssysteme (CBES) ins Spiel, bei denen Eigenschaften = Komponenten sind.

Insbesondere CBES mit Systemen (oder Subsystemen). Bei diesem Entwurf gibt es in der Regel einige Systeme, die sich auf bestimmte Komponenten einer Entität konzentrieren, ohne sich um die anderen Komponenten der Entität zu kümmern. Außerdem sind die Systeme nur mit den Informationen gekoppelt, die sie zur Verarbeitung dieser Komponenten benötigen.

Nehmen wir Ihr Beispiel. Da die Eingabe, wohin die Entität verschoben werden soll, auf dem Controller des Players basiert, hätten Sie wahrscheinlich ein PlayerControllerSystem. Dieses System würde neben vielen anderen Dingen die PositionComponent der Entität steuern. In diesem Fall müsste das PlayerControllerSystem die Ebene und die PositionComponent kennen. Wenn Sie sich später dazu entschließen, die Kollisionserkennung hinzuzufügen, würden Sie ein CollisionSystem erstellen, das erneut die Position der Entitäten verwendet, diesmal jedoch zur Berechnung der Begrenzungsrahmen (oder Sie könnten eine BoundingBoxComponent, Ihren Aufruf, haben). Tatsache ist, dass Sie das Verhalten einfach ein- oder ausschalten können (auch im laufenden Betrieb), indem Sie einfach Komponenten hinzufügen / entfernen. Mehr Verhalten bedeutet also, dass mehr Systeme die Komponenten einer Entität manipulieren, aber alle gehören zu einer genau definierten Klasse mit geringer Kopplung. Willst du Skripte? Fügen Sie eine ScriptComponent hinzu. BAM! Sie haben gerade Skriptfunktionen mit 2 Klassen hinzugefügt. Physik? Klang? Das selbe nochmal.

Der Grund, warum ich CBES mit SubSystems befürworte, ist, dass es perfekt OO und ein insgesamt leicht zu wartendes / erweiterbares System ist. Das Hinzufügen eines Verhaltens zu einer Entität ist so einfach wie die Entscheidung, welche Daten dieses Verhalten benötigt und welche Entitäten es benötigen.

Für weitere Informationen zu komponentenbasierten Entity-Systemen mit SubSystems gibt es eine hervorragende Reihe von Blog-Posts von T = Machine bei Entity Systems, die die Zukunft der MMOG-Entwicklung darstellen . Der Autor ging sogar so weit, ein Wiki zum Sammeln verschiedener Implementierungen mit dem Namen Entity Systems Project zu erstellen

Ein allgemeiner (und bekannter) Beitrag über komponentenbasierte Entitätssysteme im Allgemeinen ist die Evolve your hierarchy , die das System für Tony Hawk Pro erstellt hat.

Wenn Sie nach einer Bibliothek mit Beispielcode suchen, gehen Sie nicht weiter als bis zur Artemis- Bibliothek. Artemis ist hauptsächlich in Java, aber hier ist ein Port in C # (den ich derzeit in meinem XNA-Projekt verwende).

pek
quelle
2
-1, da dies das Problem nicht löst, verschiebt es einfach. Er wird immer noch keine klare Antwort darauf haben, welches Objekt die Entscheidung trifft, ob es aus Komponenten besteht oder nicht.
Kylotan
1
Der zweite Link enthält ein ausführliches Tutorial, warum das Verhalten von Spielentitäten an Entitätssysteme ausgelagert werden sollte. Artemis folgt genau der gleichen Architektur.
Pek
Der zweite Link ist eine Stellungnahme. Komponenten werden von vielen bevorzugt, aber sie sind nicht eine Lösung für alle Probleme, und sie haben definitiv keine intrinsische Lösung für dieses Problem.
Kylotan
Die am häufigsten gewählte Antwort in dieser Frage beginnt mit "Es gibt keine einfache Antwort. Wie bei den meisten Dingen in der Programmierung ist es ein Kompromiss." Der zweite Link bietet eine von vielen Lösungen. Tatsächlich befürwortet es das genaue Gegenteil dieser Antwort. Vielleicht sollte ich meine Antwort bearbeiten, um es klarer zu machen. Aber andererseits, vielleicht ändert dies nichts an den negativen Stimmen: P
pek
@pek: Meine Ablehnung ist, weil "Komponenten verwenden" keine Antwort auf diese Frage ist. Ich verwende zwar Komponenten und empfehle das Komponentenmuster, aber "ein komponentenbasiertes Entitätssystem verwenden" ist nicht die Antwort auf jedes Problem in der Spieleentwicklung oder sogar in der Spielesoftwarearchitektur - diese Frage stellt sich auch bei Komponenten oder Vererbung Hierarchien oder völlig unabhängige statische Entitätstypen!
0

Normalerweise gehe ich mit einem Design um, bei dem Objekte sich selbst behandeln (dafür sind schließlich Methoden gedacht), aber die Basiswelt hat Listen aller Objekte und verwendet diese Listen, um sie zu koordinieren. Also so etwas wie:

class World {
  var walls = [];
  var actors = [];
  function update() {
    for each (actor in actors) {
      actor.update(walls);
    }
  }
}

class Actor {
  function update(walls) {
    this.move();
    for each (wall in walls) {
      this.collide(wall);
    }
  }
}
jhocking
quelle
0

Halten Sie es trocken, schüchtern und sagen Sie es dem anderen.

Es ist besser, die Welt zu bitten, Ihren Schauspieler zu bewegen, als die Welt zu fragen, ob Sie dorthin ziehen können, wo Sie hin möchten. Auf diese Weise können Sie den Pfadfindungsalgorithmus in der Weltklasse und dergleichen leicht ändern. Natürlich könnte man dies einfach einer Grundklasse für den Schauspieler überlassen, aber das ist die Richtung, die ich einschlagen würde, und der Grund dafür.

Daniel Carlsson
quelle
-1

EDIT: Umgeschrieben aufgrund des Feedbacks, dass ich die Grenze für diese Antwort auf die ursprüngliche Frage nicht klar genug gezogen habe.

Ich möchte die Frage nach Möglichkeit etwas näher erläutern. Sollte die Logik, die auf ein Objekt einwirkt, intern oder extern zu diesem Objekt sein? Der letzte Teil des Beitrags erwähnt speziell die Langlebigkeit des Designs als Grundlage für die Beantwortung der Frage, um später Probleme zu vermeiden.

Meine einfache Antwort auf hoher Ebene besteht darin, die gesamte Logik, die auf ein Objekt wirkt, in diesem Objekt beizubehalten.

Sie brauchen nicht die Welt, um ein Objekt zu bewegen. Alles, was Sie brauchen, ist das zu bewegende Objekt und der Vektor, der den Ort darstellt, an den Sie sich bewegen möchten. Die Welt kann ins Spiel kommen, wenn ein Ziel ausgewählt wird oder wenn aufgrund einer Kollision eine Reaktion auf die Umgebung erforderlich ist, diese jedoch außerhalb des Beispiels liegen, an dem gearbeitet werden soll.

Um den zugrunde liegenden Teil der Frage anzusprechen und eine lange Lebensdauer des Designs zu erreichen, würde ich eine komponentenorientierte Architektur vorschlagen. Komponentenarchitekturen zerlegen ein Objekt in diskrete Datensätze und Funktionen (vorausgesetzt, Sie gehen mit meiner Antwort darüber, welche Zustände die Logik mit den Daten beibehalten sollen). Wenn Ihre Struktur wie CEntity-> CHuman-> CSoldier-> CPlayerCharacter aussieht, werden Sie immer auf Probleme stoßen, bei denen Sie eine Logik ändern müssen und je nachdem, wohin sie in diesem Baum geht (wie viele andere Arten von Menschen gibt es zum Beispiel?) Es kann weitreichende Auswirkungen auf mehrere Objekttypen haben.

Ein Komponentensystem verfügt stattdessen über eine Reihe von Schnittstellen, die definieren, woraus das CEntity-Objekt besteht, z. B. ICompRenderable, ICompMoveable, ICompHealth, ICompInventory usw. Wo Sie CCompMoveableHuman und möglicherweise einen CCompMoveableSoldier und CCompMoveablePlayer haben würden, um ihre individuellen Bewegungsmuster getrennt zu halten. Sagen wir also, der Soldat ist so verändert, dass er in Formationen läuft. Diese Änderung wirkt sich nur auf Entitäten aus, die mit dieser Komponente erstellt wurden.

Zusammenfassend schlage ich vor, dass Sie die Logik mit den Daten enthalten, für die die Logik gilt. Ich empfehle außerdem, Objekte in einzelne Komponenten zu zerlegen, um die Option "Wo lege ich das ab?" Zu reduzieren. Fragen und bieten Stabilität in die Zukunft mit einfacher Wartung und erweiterbar.

Hoffe das hilft.

James
quelle
Ich habe mich in den letzten Stunden mit dem Komponentenmuster befasst. (Pek's Links sind sehr nett). Aber es ist immer noch nicht klar, wo das eigentliche "Verhalten" stattfinden soll. Es ist klar, dass eine Entität unterschiedliche Komponenten für Eingabe, Rendering und Physik hat. Wenn ich das richtig verstanden habe, enthalten die Komponenten nur die Daten, die für die Funktionsfähigkeit erforderlich sind, enthalten jedoch nicht die Implementierung. Kehren wir wieder zum Verschieben zurück: Wie soll ich eine Entität mithilfe der Komponenten verschieben? Soll ich die Eingabekomponente erweitern und die Implementierung dort codieren? Oder etwas anderes die Implementierung verwalten?
Omgnoseat
Für mich behalte ich die Logik in den Objekten, aber ich mache tatsächlich ein Komponentensystem über datenorientiertem Design, was bedeutet, dass die meisten meiner Objekte sowieso nur die Logik sind, die für eine zusammenhängende schnelle Verarbeitung auf Positionen innerhalb von Daten abgebildet wird ... Und am Ende des Tages lernen Sie jeweils ein Entwurfsmuster: D Was die -1 betrifft, würde ich es begrüßen zu wissen, warum das so markiert wurde, auch wenn es nur "Ich mag meine Antwort besser" ist, möchte ich immer noch weiß warum.
James
1
@ James, fair genug. Ich habe abgestimmt, weil Entity-Component-Systeme die ursprüngliche Frage nicht beantworten. Eine Positionskomponente kann sich immer noch selbst bewegen , oder die Welt könnte alle Positionskomponenten durchlaufen und sie verschieben. Der gleiche Grund, warum Kylotan die Antwort von pek abgelehnt hat, also dachte ich, der -1-Grund sei offensichtlich. Und selbst wenn ein Komponentensystem sich selbst verschieben müsste (was nicht der Fall ist), wäre dies keine Antwort, sondern nur ein Anwendungsfall für eine der Optionen. Aber die Frage war nach den Gründen, den einen oder anderen zu benutzen, ihren Vor- und Nachteilen. Die Antwort deckt das nicht ab.
Maik Semder
@ Maik Semder. Danke für die Rückmeldung. Ich habe die Antwort umgeschrieben, um zu versuchen, die Linien klarer zu der ursprünglichen Frage und dem allgemeinen Antrieb dafür zu ziehen, wie am Ende der Frage angegeben.
James
@ James, danke, dass du dir die Zeit genommen hast, die Antwort als Antwort auf meinen Kommentar zu bearbeiten. Sehr geschätzt. Die vorgeschlagene Antwort auf "Behalte alle Logik, die auf ein Objekt innerhalb dieses Objekts einwirkt", besiegt ein Kernmerkmal von OO, nämlich die Kapselung. Wie Joes Link zeigt, erhöht die Verwendung von Funktionen, die keine Freunde sind, die Kapselung eines Objekts. Darüber hinaus wird eine klare Methode zur tatsächlichen Messung der Einkapselung vorgestellt. Wenn Sie die Verschiebungsfunktion entweder in eine Welt oder in einen Akteur einfügen, wird die Kapselung des Designs verringert, was zu weniger flexiblem und schwieriger zu wartendem Code führt.
Maik Semder
-1

Ich empfehle Ihnen, sich über komponentenbasierte Architekturen zu informieren. Es gibt eine ganze Reihe von Blog-Posts, Präsentationen und Aufzeichnungen darüber, die einen besseren Job machen können als ich es jemals könnte.

AltDevBlogADay hat einige Posts darüber, ein sehr guter ist diese Serie über Spieleentitäten: http://altdevblogaday.com/2011/07/10/the-game-entity-–-part-ia-retrospect/ Es gibt Mehrere Teile, die sich auf das Problem konzentrieren, das Sie lösen möchten.

Kyle
quelle