Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

TGGC

1x Rätselkönig

Beiträge: 1 799

Beruf: Software Entwickler

  • Private Nachricht senden

21

09.08.2017, 14:44


TGGC kannst du das gleiche Verhalten einmal ohne das Command Pattern beschreiben, wie würdest du es machen?
Das hatte ich in Beitrag 6 ja schon getan, was konkret ist dir da unklar?

Der Code für das Verhalten kann auch in einer Datei geschrieben werden, oder in mehreren Klassen. Die Frage ist, was ist wartbar und erweiterbar.
Der Code muss auf jeden Fall irgendwo existieren, in welcher Form auch immer.
Ja klar, das Verhalten muss existieren sonst kann man es logischerweise nicht executen. Nur beim Command Pattern muss das gesamte Verhalten inkl. dessen Context den execute Methoden der Commands bekannt sein, damit diese das dann korrekt aufrufen können. Daher der Trick dem execute einen actor zu uebergeben, das geht vlt. noch gut in einem FPS wo man wirklich nur einen einzigen Actor beeinflussen muss. Aber stell dir mal ein RTS vor, was da eine allgemeine execute Methode als Parameter bräuchte.

Das schöne am Command Pattern ist, das man damit auf einfachste Art und Weise ein Spiel nachstellen und testen kann. Man sieht sofort welche Eingaben gemacht worden sind und kann ganze Spielpartien erneut ablaufen lassen. Damit kann man gemeldete Bugs ausfindig machen oder einfach eine Aufnahmefunktionalität schaffen so das sich Spieler ihr Spiel im nachhinein nochmal anschauen können. Gleichzeitig können einzelne Commands vom Server validiert werden.
Ja, in der Theorie kann man das. Ist aber auch nicht besser als wenn man ein Replay irgendwie anders macht. Allein diese Vererbungshierachie abzuspeichern ist genauso pain in the ass. In der Praxis funktioniert es nicht so problemlos wie du dir das vorstellst, denn diese Daten sind total fragil. Eine minimale Aenderung von Code oder Spieledaten und man kann das nicht mehr benutzen. Starcraft hat das so gemacht, vermutlich weil sie das auch fürs Netzwerkprotokoll brauchten. Im ersten Teil haben sie ewig gebraucht das überhaupt reinzupatchen und nach 10 Jahren Entwicklung hats immer noch nicht richtig funktionert, bei Coop oder pausieren gabs immer noch Fehler. Beim zweiten haben sie dann ein extrem kompliziertes System gebaut, mit dem sich das Spiel in genau die richtige Version inkl. Daten zurückpatchen kann, um ein Replay korrekt abzuspielen. Sowas wie in der Quakeengine ist wesentlich robuster für Replays.


Wie viele diese Projekte waren dabei Games? Und in welchen Sprachen wurden diese umgesetzt?
Um die 80% bestimmt. C++, Java, Pascal, Basic so ungefaehr in Reihenfolge der Häufigkeit.


Die nächste Frage ist, hast du das Pattern schon mal ausprobiert?
Ja. Auch erfolgreich. Nur eben nicht für das hier genannte Problem.

22

09.08.2017, 19:54

Ich finde die Diskussion spannend. Das Problem mit der Eingabegerätverarbeitung kenne ich auch, allerdings nur von kleineren Hobbyprojekten.

Ich finde das Command-Pattern eigentlich auch ziemlich schick. Ich kenne es vor allem aus dem Java-Enterprise-Bereich für verschiedene Frameworks und Bibliotheken.

Ich habe gerade ein bisschen die Vermutung, dass das Pattern mit Nicht-GC-Sprachen ohne Reflexion wesentlich weniger flexibel ist.

So richtig unterscheiden tut sich das Erzeugen eines Kommandos von Methodenaufrufen ja nicht. Eine Vererbungshierarchie o.Ä. sehe ich beim Command-Pattern jetzt nicht unbedingt. Normalerweise hat man ja lediglich ein Interface für die Kommandos. Wenn man dann noch das Interceptor-Pattern nutzt ist das bei Bibliotheken ein ziemlich mächtiges Werkzeug, um Customizing etc. zu ermöglichen.

Die "Parameter" und ggf. auch der Kontext des Kommandos sind ja üblicherweise Member-Variablen der konkreten Kommando-Implementierung. Das wird einmal im Konstruktor übergeben, den Rest weiß das Kommando für sich. Das Aufräumen ist ja mit GC dann egal.

Die Vermeidung von Fragilität für Replays finde ich jetzt gerade beim Command-Pattern eigentlich recht einfach umsetzbar. Man kann die Kommandos ja schon am Klassennamen versionieren. Bei Refactorings muss die Funktionalität dann erhalten bleiben. Das kann man dann recht gut testen.

Ich weiß nicht, ob das hier diskutiert werden darf, aber wie läuft das denn z.B. in Anno - also jetzt abgesehen von Eingabegeräteverarbeitung? Wird da aus allem Events gemacht? Das Event-Pattern erscheint mir persönlich für Spiele noch etwas geeigneter, wenn man Replays und Netzwerkfähigkeit haben will. Aber das kann man ja trotzdem mit Commands vereinen.

Für Eingabegeräteverarbeitung stelle ich mir das Command-Pattern auch nur bedingt nützlich vor. Hotkeys etc. ja, aber bei allem was mehrere Knöpfe oder sogar Mausbewegungen betrifft, fängt es dann an nervig zu werden. Man müsste dann vielleicht so Kommandos bauen, die durch beliebig viele Knöpfe nur einmal ausgelöst werden. Im Kommando gibt es dann verschiedene Slots für diese Knöpfe mit denen man dann, wie von TGGC bei der moveXY-Methode beschrieben, die eigentliche Bewegung durchführt.

Noch ein Nachtrag: Ich habe mal eine Art "zeitsensitiven endlichen Automaten" gebaut, um verschiedene Kommandos auszulösen. Das ging da um ein One-Button-Spiel, wo es so Dinge wie Gedrückthalten, Klick, Doppelklick und Dreifachklick gibt. Man konnte für die Zustandsübergänge Tastenzustände und Mindest-/Maximalwerte für die Zeit im aktuellen Zustand angeben. Das hat gar nicht schlecht funktioniert, war aber irgendwie auch kompliziert zu füttern. Ein komplexes Zusammenspiel mit anderen Knöpfen stelle ich mir aber sehr kompliziert vor.

Dieser Beitrag wurde bereits 10 mal editiert, zuletzt von »Chromanoid« (09.08.2017, 21:10)


TGGC

1x Rätselkönig

Beiträge: 1 799

Beruf: Software Entwickler

  • Private Nachricht senden

23

13.08.2017, 10:15

Die Vermeidung von Fragilität für Replays finde ich jetzt gerade beim Command-Pattern eigentlich recht einfach umsetzbar. Man kann die Kommandos ja schon am Klassennamen versionieren. Bei Refactorings muss die Funktionalität dann erhalten bleiben. Das kann man dann recht gut testen.
Oh wie naiv. Als wenn sich nur was in den Commands ändert und man dann einfach das execute eines alten Commands ausführen müsste...


Ich weiß nicht, ob das hier diskutiert werden darf, aber wie läuft das denn z.B. in Anno - also jetzt abgesehen von Eingabegeräteverarbeitung? Wird da aus allem Events gemacht? Das Event-Pattern erscheint mir persönlich für Spiele noch etwas geeigneter, wenn man Replays und Netzwerkfähigkeit haben will. Aber das kann man ja trotzdem mit Commands vereinen.
Events ist sehr weit gefasst! Man kann jeden Methodenaufruf als "Event" begreifen...

Mein eigentlicher Punkt ist nur, das man nicht jeden Event in eine eigene Klasseninstanz stecken muss, die dann auch noch von einen bestimmten Basisklasse ableiten muss. Falls hier jemand in einem sinnvollen Projekt mal alle seine Tastatureingaben direkt so verpackt hat, würde ich da gerne mal von den Erfahrungen mit dieser Methode hören.

24

13.08.2017, 17:13

Die Vermeidung von Fragilität für Replays finde ich jetzt gerade beim Command-Pattern eigentlich recht einfach umsetzbar. Man kann die Kommandos ja schon am Klassennamen versionieren. Bei Refactorings muss die Funktionalität dann erhalten bleiben. Das kann man dann recht gut testen.
Oh wie naiv. Als wenn sich nur was in den Commands ändert und man dann einfach das execute eines alten Commands ausführen müsste...
Warum gleich so persönlich werden? Ich behaupte gar nicht, dass man nicht immer darauf achten muss, was da so drumherum passiert. Aber wenn man wirklich alles mit Commands abhandelt und den globalen Zustand vorher und nachher kennt, dann kann man das relativ gut abtesten - zur Not auch mit Mutation-Testing, dann kann man die Test Coverage wirklich ziemlich ernst nehmen. Oft hat man doch gar keine andere Wahl, wenn man eine effiziente Undo-Redo-Funktion einbauen will. Ich habe noch nie in einer größeren Gruppe über längere Zeit ein "One-Shot-Projekt", wie das bei Spielen meist der Fall ist, entwickelt. Da stelle ich mir das aufrecht erhalten der Rückwärtskompatibilität allgemein schwierig bzw. einfach vernachlässigbar vor. Bei Anwendungen hat man ja oft einfach keine andere Wahl, weil irgendwelche Geschäftsprozesse "wie immer" weiterlaufen sollen. Webservice-Versionierung ist ja eine ziemlich harte Nuss für viele Unternehmen. Da finde ich das Kapseln von Verhalten in eigene Klassen schon ziemlich sinnvoll.

Ich weiß nicht, ob das hier diskutiert werden darf, aber wie läuft das denn z.B. in Anno - also jetzt abgesehen von Eingabegeräteverarbeitung? Wird da aus allem Events gemacht? Das Event-Pattern erscheint mir persönlich für Spiele noch etwas geeigneter, wenn man Replays und Netzwerkfähigkeit haben will. Aber das kann man ja trotzdem mit Commands vereinen.
Events ist sehr weit gefasst! Man kann jeden Methodenaufruf als "Event" begreifen...
Das ist schon klar. In einigen Programmiersprachen sind Methodenaufrufe konzeptuell ja sogar relativ nah dran. Ich dachte Du würdest verstehen, warum ich das frage. Mich würde das einfach grundsätzlich interessieren, welche Patterns ihr so einsetzt, um zum Einen Verhalten zu kapseln und zum Anderen reproduzierbare Zustandsänderungen z.B. für Multiplayer umzusetzen.

Mein eigentlicher Punkt ist nur, das man nicht jeden Event in eine eigene Klasseninstanz stecken muss, die dann auch noch von einen bestimmten Basisklasse ableiten muss. Falls hier jemand in einem sinnvollen Projekt mal alle seine Tastatureingaben direkt so verpackt hat, würde ich da gerne mal von den Erfahrungen mit dieser Methode hören.
Das sehe ich auch so. Allerdings repräsentieren Klassen auch wunderbar eindeutige Bezeichner, die bei den richtigen Sprachen wie eine Event-ID fungieren können. Viele Events will man ja auch dokumentieren, das kann man dann wunderbar an der individuellen Event-Klasse machen. Was Reflection angeht scheiden sich ja die Geister, aber ich finde instanceof und Co. sehr sinnvolle Sprachmittel. Jedenfalls im Kontext dieser Sprachen kann ich das sehr ausgiebige Erstellen von Klassen für Events und Co. gut verstehen und finde das auch sinnvoll. Wenn man davon ausgeht, dass bei Klassen auf Einzigartigkeit im Softwareprojekt geachtet wird, dann sind Klassen ein wunderbares Mittel, um sehr entkoppelt effiziente Event-Systeme zu entwickeln. In allen anderen Fällen ist man immer darauf angewiesen, irgendwie IDs zentral festzulegen.

Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von »Chromanoid« (13.08.2017, 17:51)


TGGC

1x Rätselkönig

Beiträge: 1 799

Beruf: Software Entwickler

  • Private Nachricht senden

25

19.08.2017, 11:24

Warum gleich so persönlich werden? Ich behaupte gar nicht, dass man nicht immer darauf achten muss, was da so drumherum passiert. Aber wenn man wirklich alles mit Commands abhandelt und den globalen Zustand vorher und nachher kennt, dann kann man das relativ gut abtesten
Ach komm, man muss nicht alles auf sich beziehen. Dir ist sicher bekannt das man auch Algorithmen als naiv bezeichnet und nicht nur Personen.

Und hier ging es nicht nur drum Commands zu kapseln, sondern einfach alles was gedrueckt wird. Klar machts Sinn z.B. sowas wie COpenFileCommand mit den entsprechenden Parametern wie Dateiname etc. zu haben. Aber so wie das hier beschrieben wurde, wuerde dafür erstmal ALt+F, dann Cursor down, Cursor Down, Cursor Down, Enter, D, a, t, e, i, ., t, x, t, Enter als Commands erzeugt. Macht das Sinn? Nein, damit kann man doch nichts anfangen! Und selbst wenn du Versionen in deinen Commands einfuegst, kannst du das nicht einfach wieder so ausfuehren! Beim zweiten mal muss man da villeicht ein "wirklich ueberschreiben?" bestaetigen. Oder man hat ein dynamisches File Menu, wo sich noch eine recent open Liste einhaengt, und ploetzlich sind die Befehle verschoben. Oder ich wechsle die Sprache und man muesste jetzt Alt+D druecken. Undo funktioniert damit genausowenig, wie soll man denn jeden einzelnen Tastendruck rückgängig machen?

Werbeanzeige