Gibt es eine übliche Technik, um Zustände (im Allgemeinen) in einer funktionalen Programmiersprache zu behandeln? Es gibt in jeder (funktionalen) Programmiersprache Lösungen für den globalen Zustand, aber ich möchte dies so weit wie möglich vermeiden.
Alle rein funktionalen Zustände sind Funktionsparameter. Ich muss also den gesamten Spielstatus (eine gigantische Hashmap mit der Welt, Spielern, Positionen, Punkten, Assets, Feinden, ...) als Parameter für alle Funktionen angeben, die die Welt mit einem bestimmten Eingang oder Trigger manipulieren möchten . Die Funktion selbst wählt die relevanten Informationen aus dem Gamestate-Blob aus, tut etwas damit, manipuliert den Gamestate und gibt den Gamestate zurück. Aber das scheint eine schlechte Lösung für das Problem zu sein. Wenn ich den gesamten Gamestate in alle Funktionen einbaue, habe ich im Gegensatz zu globalen Variablen oder dem imperativen Ansatz keinen Nutzen für mich.
Ich könnte nur die relevanten Informationen in die Funktionen einfügen und die Aktionen zurückgeben, die für die gegebene Eingabe ausgeführt werden. Und eine einzige Funktion wendet alle Aktionen auf den Gamestate an. Die meisten Funktionen benötigen jedoch viele "relevante" Informationen. move()
brauche die Objektposition, die Geschwindigkeit, die Karte für die Kollision, die Position aller Feinde, die aktuelle Gesundheit, ... Also scheint dieser Ansatz auch nicht zu funktionieren.
Meine Frage ist also, wie ich mit der enormen Menge an Status in einer funktionalen Programmiersprache umgehen kann - insbesondere für die Spieleentwicklung?
BEARBEITEN: Es gibt einige Spiel-Frameworks zum Erstellen von Spielen in Clojure. Ein Ansatz, um dieses Problem teilweise zu lösen, besteht darin, alle Objekte im Spiel als "Entitäten" zu fädeln und in einen riesigen Beutel zu packen. A gigant Hauptfunktion hält den Bildschirm und die Entitäten und Griff Ereignisse ( :on-key-down
, :on-init
, ...) für diese Einheiten und die Hauptanzeigeschleife laufen. Aber das ist nicht die saubere Lösung, nach der ich suche.
quelle
move()
, sollten Sie wahrscheinlich an der ‚aktuellen‘ Objekt werden vorbei (oder eine Kennung für sie), plus die Welt es bewegt sich durch, und nur herleiten aktuelle Position und Geschwindigkeit ... ausgegeben die gesamte Physik Welt dann, oder zumindest eine Liste der geänderten Objekte.Antworten:
Nebenwirkungen und Zustand in funktionalen Programmiersprachen sind ein größeres Problem in der Informatik. Falls Sie ihnen noch nicht begegnet sind, schauen Sie sich vielleicht Monaden an . Seien Sie jedoch gewarnt: Es handelt sich um ein ziemlich fortschrittliches Konzept, und die meisten Menschen, die ich kenne (ich eingeschlossen) haben Mühe, sie zu erfassen. Es gibt viele, viele Online-Tutorials mit unterschiedlichen Ansätzen und Wissensanforderungen. Persönlich gefiel mir Eric Lipperts am besten.
Eric Lippert über Monaden
Einige Dinge zu beachten:
Einige abschließende Gedanken:
Alles in allem denke ich, auch wenn es wissenschaftlich interessant sein könnte, bezweifle ich, dass dieser Ansatz praktisch ist und die Mühe wert ist.
quelle
Ich habe einige Spiele mit F # (Multi-Paradigma, unreine, funktionale Erstsprache) mit Ansätzen von OOP bis FRP geschrieben . Dies ist eine weit gefasste Frage, aber ich werde mein Bestes geben.
Mein bevorzugter Weg ist es, einen unveränderlichen Typ zu haben, der das gesamte Spiel repräsentiert
State
. Ich habe dann einen veränderlichen Verweis auf den StromState
, der bei jedem Tick aktualisiert wird. Dies ist nicht unbedingt rein, aber es hält Veränderlichkeit an einen Ort beschränkt.Nicht wahr. Da der
State
Typ unveränderlich ist, können Sie keine alte Komponente haben, die die Welt auf schlecht definierte Weise mutiert. Dies behebt das größte Problem mit demGameObject
von Unity verbreiteten Ansatz: Es ist schwierig, die Reihenfolge derUpdate
Anrufe zu steuern .Und im Gegensatz zur Verwendung von Globals ist es einfach in der Einheit zu testen und zu parallelisieren.
Sie sollten auch Hilfsfunktionen schreiben, die Sub-Eigenschaften des Status erhalten, um das Problem zu lösen.
Beispielsweise:
Hier
update
wirkt auf den ganzen Staat, aberupdateSpaceShip
nur auf einen EinzelnenSpaceShip
in Isolation.Mein Vorschlag wäre, einen
Input
Typ zu erstellen , der die Zustände Tastatur, Maus, Gamepad usw. enthält. Sie können dann eine Funktion schreiben, die ein brauchtState
undInput
die nächste zurückgibtState
:Um Ihnen eine Vorstellung davon zu geben, wie dies zusammenpasst, könnte das gesamte Spiel so aussehen:
Für einfache Spiele können Sie den obigen Ansatz verwenden (Hilfsfunktionen). Für etwas komplizierteres möchten Sie vielleicht " Linsen " ausprobieren .
Im Gegensatz zu einigen Kommentaren hier würde ich dringend empfehlen, Spiele in einer (unreinen) funktionalen Programmiersprache zu schreiben oder zumindest auszuprobieren! Ich habe festgestellt, dass dies die Iteration beschleunigt, die Codebasis verkleinert und Fehler seltener macht.
Ich glaube auch nicht, dass Sie Monaden lernen müssen, um Spiele in einer (unreinen) FP-Sprache zu schreiben. Dies liegt daran, dass Ihr Code höchstwahrscheinlich blockiert und Single-Threaded ist.
Dies gilt insbesondere, wenn Sie ein Multiplayer-Spiel schreiben. Dann wird es mit diesem Ansatz viel einfacher, da Sie den Spielstatus einfach serialisieren und über das Netzwerk senden können.
Warum mehr Spiele nicht so geschrieben sind, kann ich nicht sagen. Dies gilt jedoch für alle Programmierbereiche (außer vielleicht für Finanzen). Daher würde ich dies nicht als Argument dafür verwenden, dass funktionale Sprachen für die Programmierung von Spielen ungeeignet sind.
Lesenswert ist auch Purely Functional Retrogames .
quelle
Was Sie suchen, ist die Entwicklung von FRP-Spielen.
Einige Videoeinführungen:
"Kontrolle von Zeit und Raum: Verständnis der vielen Formulierungen von FRP" von Evan Czaplicki
Bodil Stokke: Reaktive Spieleentwicklung für den anspruchsvollen Hipster [JSConf2014]
Lieben Sie dieses Gespräch mit Carmack, beschreibt seine Erfahrungen mit rein funktionaler Programmierung in der Spieleentwicklung John Carmacks Keynote auf der Quakecon 2013, Teil 4
Es ist zu 100% möglich und vorzuziehen, die Kernlogik des Spiels rein funktional zu gestalten. Die Branche als Ganzes ist einfach zurückgeblieben und steckt in einem Denkmuster.
Dies ist auch in Unity möglich.
Um die Frage zu beantworten, wird jedes Mal, wenn sich etwas bewegt, ein neuer Spielstatus aktualisiert / erstellt, wie Carmack in seinem Vortrag sagt, es ist kein Problem. Die drastische Reduzierung des kognitiven Overheads, der sich aus einer rein funktionalen, hochgradig wartbaren und flexiblen Architektur ergibt, steht in keinem Verhältnis zu den Auswirkungen auf die Leistung, wenn überhaupt.
quelle