Was sind die Herausforderungen und Vorteile des Schreibens von Spielen mit einer funktionalen Sprache?


81

Obwohl ich weiß, dass funktionale Sprachen nicht die am häufigsten zum Schreiben von Spielen verwendeten sind, gibt es eine Reihe von Vorteilen, die in jedem Programmierkontext interessant erscheinen. Besonders die Leichtigkeit der Parallelisierung könnte meiner Meinung nach sehr nützlich sein, da der Fokus auf immer mehr Prozessoren gerichtet ist.

Mit F # als neuem Mitglied der .NET-Familie kann es auch direkt mit XNA verwendet werden, wodurch der Schwellenwert erheblich gesenkt wird, im Gegensatz zu LISP, Haskell, Erlang usw.

Wenn jemand Erfahrung mit dem Schreiben von Spielen mit funktionalem Code hat, was hat sich als positiv und negativ herausgestellt? Wofür war es geeignet, wofür nicht?

Bearbeiten: Es fällt schwer zu entscheiden, ob es eine gute Antwort dafür gibt. Daher ist es wahrscheinlich besser als Community-Wiki-Beitrag geeignet.


3
Da eines meiner Lieblingsspiele (Rogue) in LISP geschrieben ist, ist diese Frage (und ihr Antwortpotential) für mich sehr interessant.
Justin L.

Ich wusste nicht, dass Rogue in LISP geschrieben wurde, aber ich finde das interessant. Klassisches Spiel. :)
McMuttons

2
Ich bin nicht sicher, wovon du redest. Rogue wurde in C. roguebasin.roguelikedevelopment.org/index.php?title=Rogue
Mason Wheeler

2
Ich bin sicher, Sie haben Recht damit, dass der ursprüngliche Rogue in C ist, da er unter Unix geschrieben wurde, aber in Lisp gibt es sicherlich mehrere Rogue- Klone : cl-user.net/asp/root-dir (nicht der direkte Game-Link) , es hatte so viele Sonderzeichen, dass die Verknüpfung offenbar unterbrochen ist).
Cyclops

Antworten:


57

Ich arbeite gerade an einem Spiel in Haskell. Ich kann nicht allgemein für funktionale Programmierung sprechen, sondern speziell für Haskell:

Der gute

Dies sind die großartigen Dinge, die Haskell wirklich auszeichnen.

  • Reinheit bedeutet, dass es viel einfacher ist, über Ihren Code nachzudenken. Sie müssen sich keine Gedanken über den "aktuellen" Wert einer Variablen machen oder darüber, ob die Verwendung einer bestimmten Funktion in irgendeiner Weise mit Ihrem globalen Status in Konflikt steht. Dies bedeutet auch, dass Parallelität und Nebenläufigkeit viel einfacher zu handhaben sind (und in vielen Fällen trivial sind). Diese Dinge können für Spieleentwickler ein Glücksfall sein.
  • Mit Haskell ist es sehr einfach, mit Abstraktionen Ihrer eigenen Kreation auf sehr hohem Niveau zu schreiben. In Haskell ist es oft einfacher, sehr generischen Code zu schreiben als spezialisierten Code. Die Vorteile zeigen sich in einer kürzeren Entwicklungszeit und einem einfacheren Codeverständnis. In Haskell ist es wahrer als in jeder anderen Sprache, die ich kenne, dass die Verwendung einer Benutzeroberfläche der Verwendung einer völlig neuen Sprache gleicht (wobei die Vorteile des restlichen Haskell-Ökosystems erhalten bleiben). Was die meisten Sprachen als Sprachfunktionen bezeichnen, nennt Haskell eine Bibliothek, und sie fühlt sich nicht gezwungen. Wenn Sie jemals über Ihr Spiel im Pseudocode nachdenken, können Sie wahrscheinlich einfach eine einfache Oberfläche schreiben und daraus echten Code machen. In der Regel lohnt es sich, dies zu tun.
  • Dies steht möglicherweise im Widerspruch zu anderen Antworten, aber Haskell beherrscht den Umgang mit imperativem Code besser als jede andere Sprache, die ich kenne. Die Einschränkungen der Reinheit bedeuten, dass Haskell eine Trennung zwischen den Konzepten "Bewertung" (Reduzieren von Ausdrücken) und "Ausführung" (Durchführen von Nebenwirkungen) hat. Sie können mit Leichtigkeit steuern, wann und wie Nebenwirkungen auftreten, die von den sogenannten "Imperativ" -Sprachen nicht erreicht werden. Ungeachtet dessen, was ich zuvor über Reinheit gesagt habe, sind einige C-Bindungen immer noch sehr wichtig (OpenGL, ich sehe Sie an), daher ist diese feinkörnige Kontrolle über imperativen Code sehr willkommen. Selbst die erfahrensten C-Programmierer werden es wahrscheinlich zu schätzen wissen, wenn sie sich erst einmal daran gewöhnt haben.
  • GHC generiert ziemlich schnelle Binärdateien. Sie sind nicht so schnell wie Binärdateien, die von gängigen C-Compilern generiert wurden, liegen jedoch in einer Größenordnung und sind weitaus schneller als das, was Sie mit einer interpretierten Sprache erreichen würden. Auch wenn Sie Ihr Spiel nicht in anderen Hochsprachen wie Python schreiben können, weil es zu langsam wäre, gibt es eine gute Chance, dass Sie es in Haskell tun können.
  • Obwohl es in Haskell nicht viele spielbezogene Bibliotheken gibt, ist die Haskell-Oberfläche für fremde Funktionen die einfachste, die ich jemals benutzt habe. Sie können an C binden, indem Sie fast ausschließlich in Haskell schreiben.

Das Schlechte

Das sind Dinge , die nicht gut sind , aber um ohne gearbeitet werden zu viel Aufwand.

  • Der Zeit- und Arbeitsaufwand für das Erlernen von Haskell kann recht hoch sein, wenn Sie es gewohnt sind, mit nicht funktionalen Sprachen zu arbeiten. Die Sprache selbst ist nicht sehr schwierig, aber wenn Sie die üblichen Spracherweiterungen und die große Anzahl an Bibliotheken berücksichtigen (denken Sie daran, dass viele Dinge, die Sie für Sprachfunktionen in anderen Sprachen halten, Bibliotheken in Haskell sind), sieht es viel bedrohlicher aus. Meiner Meinung nach ist es das wert, aber andere mögen anderer Meinung sein.
  • Manche Leute beklagen sich viel über Faulheit. Wenn Sie wissen, was Sie tun, verursachen Sie keine schwer auffindbaren Lecks (ich habe seit ein paar Jahren keine Lecks mehr), aber Anfänger haben viel mehr Probleme damit. Es wäre dumm von mir, diese Leute niederzuschießen und zu behaupten, dass dies kein Problem ist, aber ich werde behaupten, dass es ein Problem ist, das mit Erfahrung leicht gelöst werden kann.
  • Es gibt nicht viele großartige Bibliotheken, um Spiele in Haskell zu erstellen, und nicht viele Haskeller schreiben Spiele darin. Daher ist es schwierig, Ressourcen und Hilfe in dieser Angelegenheit zu finden.

Das hässliche

Dies sind Dinge, deren Überwindung erhebliche Anstrengungen erfordern würde.

  • Wenn Sie ein ressourcenintensives Spiel schreiben, kann der GHC-Müllsammler Sie ziemlich hart beißen. Es ist ein generationsübergreifender Stop-the-World-GC, so dass Sie von Zeit zu Zeit einige Frames ablegen können. Für einige Spiele ist das in Ordnung, für andere ist es ein großes Problem. Ich und einige andere Spieleentwickler, die Haskell verwenden, möchten einen für GHC implementierten Soft-Real-Time-GC, aber meines Wissens wird noch kein Aufwand dafür aufgewendet. Momentan mache ich mir keine Gedanken darüber (und habe noch keine abgelegten Frames gesehen), aber mindestens ein anderes Team hat seine Hauptrenderschleife auf C gesetzt.

2
Was halten Sie von FRP?
Wei Hu

4
@Wei Hu: Ich bin ein großer Fan der FRP-Idee und habe sie selbst ziemlich ausführlich recherchiert, einschließlich der Erstellung einiger verschiedener Semantiken und der Erstellung einiger Implementierungen. Die besten Implementierungen weisen immer noch schwerwiegende semantische und / oder Implementierungsfehler auf. Ich würde sagen, dass sie für die Spieleentwicklung geeignet sind, aber (noch) nicht viele Vorteile gegenüber herkömmlichen Ansätzen bieten.
Jake McArthur

1
Ein anonymer Benutzer hat versucht, diese Antwort zu bearbeiten und dabei den "hässlichen" Teil gelöscht. "Der Garbage Collector in Haskell ist seit 2011 gleichzeitig, parallel und generationsübergreifend ( ghc.haskell.org/trac/ghc/blog/new-gc-preview )"
Jari Komppa,

1
Leider stimmt das nicht wirklich. Die gleichzeitige GC erwies sich als zu kompliziert, um die kleine allgemeine Verbesserung zu rechtfertigen, und sie wurde nicht übernommen.
Jake McArthur,

1
@ Hi-Angel Es gibt ein altes GHC-Backend, das C generiert hat, aber es unterscheidet sich nicht vom nativen Codegenerator oder dem LLVM-Backend. Es benötigt weiterhin die GHC-Laufzeit. Außerdem hat ein C-Backend nichts Besonderes, das es einfacher machen würde, einen Garbage Collector zu umgehen. Wenn es so einfach und billig wäre, den GC zu eliminieren, würden es wahrscheinlich auch die anderen Backends tun.
Jake McArthur

19

Ich habe das letzte Jahr damit verbracht, eine kommerzielle Spiel-Engine in Haskell zu entwickeln, und für uns waren die Erfahrungen überwiegend positiv. Unsere Spielewelt ist komplex und Haskell hat es einfach gemacht, den Konvertierungsprozess von einem Editor-Format in ein Game-Engine-Format zu modellieren. Ich würde es hassen zu überlegen, wie dieser Code in einer imperativen Sprache aussehen würde.

Gelegentlich sind Speicherplatzlecks aufgetreten, und obwohl sie einige Probleme verursacht haben, waren sie im allgemeinen Schema nur geringfügig (z. B. im Vergleich zum Auffinden von Deadlocks in Java-Projekten ähnlicher Größe) und wurden behoben blieben sie fest.

Wir verwenden FRP ähnlich wie Yampa, und es ist sicherlich eine Lernkurve damit verbunden, aber wenn das vorbei ist, ist die Erfahrung sehr positiv. Bibliotheken waren für uns kein Problem - alles, was wir brauchten, war verfügbar. Verzögerungen bei der Speicherbereinigung waren ein besonderes Problem, da es sich um eine eingebettete Plattform handelt. Wir haben C ++ zum Verwalten der Animation verwendet. Leistung war auch ein Problem, da dies eine eingebettete Plattform (= langsamer Prozessor) ist. Wir haben einige C gemacht und wir suchen auch nach neuen Haskell-Technologien wie Accelerate. Der C ++ - Animator war schon früh eine Designentscheidung und die Stellen, an denen der Code zu langsam ist, sind nur sehr kleine Bereiche. Langfristig wollen wir unser gesamtes C in Haskell übersetzen.

Haskell hat eine schwierige Aufgabe leicht gemacht, und all die Schwierigkeiten, die ich gerade erwähnt habe, waren im Vergleich zu der großen Menge an komplexem Code, der sauber, wartbar und praktisch unzerbrechlich ist, winzig. Parallelität wird in Kürze ein Thema in der Spieleentwicklung sein, und wir sind sehr gut in der Lage, damit umzugehen. Einige meiner Aussagen treffen möglicherweise nicht auf kleine Projekte zu, da wir uns auf lange Sicht in diesem Bereich befinden. Daher sind Startkosten wie Lernkurven, Bibliotheksunterstützung usw. weitaus weniger ein Problem.


7
Es ist fast 5 Jahre her, seit Sie diese Antwort gepostet haben. Wären Sie bereit, es zu aktualisieren? Wie hat die kommerzielle Game-Engine funktioniert? Welche Stücke haben sich als wertvoll erwiesen? Konnten Sie die Parallelität so nutzen, wie Sie es sich erhofft hatten?
Levi Morrison

17

Dave erwähnt einige hervorragende Punkte, obwohl ich darauf hinweisen muss, dass Haskell beide Probleme gelöst hat. Die Zustandslosigkeit kann mit der State-Monade umgangen werden (EDIT: nicht wirklich - siehe unten für Details) , und die Sequenzierung kann mit der IO-Monade (EDIT: oder jeder anderen Monade für diese Angelegenheit ...) durchgeführt werden .

Die Herausforderungen, die Sie haben (und bei denen ich versucht habe, sowohl Spieleprogrammierung als auch Haskell zu erlernen), liegen eher in dieser Richtung. (Diese sind alle Haskell-spezifisch, da ich noch keine anderen FP-Sprachen beherrsche.)

  • FP Lernkurve: FP erfordert eine völlige Umorientierung von der iterativen Programmierung. Wenn Sie nicht daran gewöhnt sind, müssen Sie lernen, in Karten und Falten anstatt in Schleifen zu denken.
  • Mathy-Lernkurve: Haskell ist sowohl gesegnet als auch verflucht von der mathematischen Raffinesse seiner Designer, denn nachdem Sie die Grundlagen von FP erlernt haben, sind Sie mit Monaden, Morphismen, Pfeilen und einer ganzen Reihe anderer Überlegungen konfrontiert, die zwar nicht schwierig sind an und für sich sind sehr abstrakt.
  • Monaden: Diese Welpen sind nicht besonders schwer in den Bann zu ziehen, besonders wenn Sie Eric Raymonds Aussage akzeptieren, dass sie ein "Hack sind, um die Funktionskomposition in eine Art und Weise zu verwandeln, die die Sequenzierung von Funktionsaufrufen erzwingt". Leider (obwohl dies mit zunehmender Popularität von Haskell besser wird) zielen viele Erklärungen von Monaden entweder weit über den Kopf der meisten Programmierer ("sie sind Monoide in der Kategorie der Endofunktoren!") Oder auf eine völlig nicht hilfreiche Analogie (z. B. , distressingly genaue Beispiel Brent Yorgey „ Monaden sind wie Burritos !“ Sie denken , er ist ein Scherz? Dass niemand jemals mit einer solchen doof Analogie in einem Tutorial einfiel? denken Sie noch einmal .)
  • Ökosystem: Wenn Sie es geschafft haben, über alle anderen Hindernisse hinwegzukommen, werden Sie sich mit der alltäglichen Praxis der Arbeit mit einer noch weitgehend experimentellen Sprache messen müssen. Gott helfe dir, wenn du hier bist. Hier habe ich angefangen, ernsthaft für Scala, F # oder eine andere Sprache zu jonen, in der es zumindest eine gewisse Garantie für die Interoperabilität mit anderen Bibliotheken gibt. Es war einmal, als ich Haskell dazu brachte, die OpenGL-Bibliotheken in Windows zu verlinken und zu laden. Das hat mir ziemlich gut getan. Dann wurden die OpenGL-Bindungen wieder freigegeben und brachen alles, was ich getan hatte. So ist das Leben in Haskell-Land. Ehrlich gesagt kann es ein Schmerz sein. Sie möchten einen Wrapper für die Bullet-Engine? Es ist in Hackage - als Abandonwarevom November des letzten Jahres. Dir gefällt Ogre3d? Hackage ist nur an eine Teilmenge der Bibliothek gebunden . Fazit: Wenn Sie sich für die Spieleentwicklung an eine andere Sprache halten, werden Sie viel mehr Zeit für die Grundinstallation aufwenden, als wenn Sie nur der Herde gefolgt wären und bei C ++ geblieben wären.

Die Kehrseite davon ist, dass sich die Dinge schnell verbessern. Und es hängt wirklich alles davon ab, was Sie aus der Erfahrung heraus wollen. Sie möchten ein Spiel erstellen und es auf Ihre Website stellen, um nach Ruhm und Reichtum zu suchen? Bleib bei C ++ oder Python. Wenn Sie jedoch etwas Neues lernen möchten, bei dem Sie Ihre Prozesse innovieren müssen, probieren Sie eine funktionale Sprache aus. Hab einfach viel Geduld mit dir selbst, während du lernst.

Antti Salonen hat ein interessantes Blog, in dem er ausführlich über seine Affäre mit der Programmierung von Haskell-Spielen berichtet. Es lohnt sich zu lesen.

Bearbeiten (ein Jahr später): Nachdem ich die staatliche Monade genauer studiert habe, ist mir klar, dass dies keine besonders gute Lösung für einen Staat ist, der außerhalb einer bestimmten Funktion bestehen bleiben soll. Die wirklichen Lösungen für Staatenlosigkeit finden sich in Haskell in IOVar, ST, MVar (für Threadsicherheit) oder über Yampa, das mithilfe von Arrows und FRP den internen Status verwaltet, der dem Entwickler jedoch verborgen bleibt. (Diese Liste ist nach Schwierigkeitsgrad geordnet, obwohl die ersten drei nicht besonders schwierig sind, wenn Sie die Monaden verstehen.)


1
Einige gute Gedanken dort, auch wenn sie ziemlich Haskell-spezifisch sind, denke ich, das sind Gedanken, die für die meisten Randsprachen gelten. Wie Sie kurz erwähnt haben, denke ich, dass hier Sprachen wie F # das Potenzial haben zu glänzen. Behandeln von Status / Benutzeroberfläche mit C #, und haben dann
Logikmodule

5

Ziel Caml!

Die Verwendung funktionaler Sprachen kann bei den meisten Arten der Softwareentwicklung von großem Vorteil sein, vor allem, weil sie die Entwicklungszeit erheblich verkürzen. Ich sehe ein großes Potenzial für das Schreiben eines Server-Backends für ein Spiel oder der KI- und Logik-Ebenen auf dem Client in einer funktionalen Sprache. Und wie jeder weiß, wurde LISP für NPC-Skripte verwendet.

Wenn ich versuchen würde, das Frontend eines Spiels in einer funktionalen Sprache zu schreiben, würde ich mich definitiv für Objective Caml entscheiden , eine hybride Sprache. Es ist ein ML-Nachkomme und ermöglicht es, iterative und funktionale Stile zu mischen. Es hat ein objektives System mit statusbehafteten Objekten. (Caml ist die Sprache, der F # nachempfunden ist.)

OpenGL-Bindungen scheinen einwandfrei zu funktionieren, und die Leute haben lange Zeit Spiele in OCaml gemacht. Die Sprache kann kompiliert werden und ist möglicherweise sehr schnell (sie hat C in einigen Benchmarks vor langer Zeit berühmt gemacht, nicht wie es jetzt aussieht).

Es gibt auch Tonnen von Bibliotheken. Alles von Informatik bis hin zu Bindungen für alle Arten von Bibliotheken.


4

Keine wirkliche Antwort auf irgendetwas, aber ich bin der Meinung, dass eine Diskussion über Spielprogrammierung in funktionalen Sprachen unvollständig ist, ohne dass Functional Reactive Programming (FRP) - http://www.haskell.org/frp/ - und seine häufigste Implementierung erwähnt werden ( Ich denke?), YAMPA - http://www.haskell.org/yampa/ .


3

Informationen zu den Vorteilen finden Sie in der Clean Game Library (http://cleangl.sourceforge.net/) und im Platformer-Spiel. Sobald Sie sich mit der Syntax vertraut gemacht haben (ich empfehle Ihnen, es zu versuchen), werden Sie sehen, wie elegant die Konstrukte sind und wie genau die Quellen den Konzepten entsprechen, die sie ausdrücken. Als zusätzlichen Vorteil macht die Abstraktion des Zustands und die Notwendigkeit, den Zustand explizit weiterzugeben, Ihren Code viel mehrkerniger.

Andererseits erfordert es einen großen Paradigmenwechsel. Vieles, was Sie gewohnt sind, ohne darüber nachzudenken, existiert plötzlich nicht mehr und Sie werden sich wundern, wie Sie es lösen können, einfach weil Sie in den falschen Mustern denken.


2

Aus meiner Sicht werden die größten Herausforderungen sein:

  • Zustandslosigkeit funktionaler Sprachen: Die Idee in Spielen ist, dass jede Entität einen Status hat und dieser Status von Frame zu Frame geändert wird. Es gibt Mechanismen, um dieses Verhalten in einigen Sprachen nachzuahmen (zum Beispiel Funktionen mit einem "!" Im PLT-Schema), aber es fühlt sich irgendwie unnatürlich an.
  • Ausführungsreihenfolge : In Spielen hängen einige Codeteile von anderen Codeteilen ab, die vor der Ausführung beendet werden müssen. Funktionssprachen geben in der Regel keine Gewähr für die Reihenfolge der Ausführung der Argumente einer Funktion. Es gibt Möglichkeiten, dieses Verhalten nachzuahmen (z. B. " (begin..." im PLT-Schema).

Fühlen Sie sich frei, diese Liste zu erweitern (und vielleicht einige positive Punkte hinzuzufügen ^^).


1
Die Ausführungsreihenfolge kann in einer nicht strengen Sprache wie Haskell variieren, verursacht jedoch keine Probleme für Codeteile, die von anderen Codeteilen abhängen. Sie können es sich als Auswertung auf Anfrage vorstellen. Eine Berechnung wird erst durchgeführt, wenn das Ergebnis benötigt wird. Ich denke, der einzige Weg, wie es Ihnen Leid bereiten kann, ist in Bezug auf die Leistung. Beispielsweise kann es in einigen Fällen schwierig sein, eine gute Zwischenspeicherung zu erzielen, da Sie die Reihenfolge der Auswertung nur durch Angabe der Abhängigkeiten zwischen den Berechnungen steuern können.
Jason Dagit

1

Sie können Ihren Code in C / C ++ schreiben und eine eingebettete Sprache wie Embedded Common Lisp für Ihre Skripterstellung verwenden. Obwohl es schwierig sein kann, diese auf einer anderen Plattform zu kompilieren. Sie können sich Lisp In Small Pieces ansehen, um zu lernen, wie Sie Ihre eigene Skriptsprache schreiben. Es ist wirklich nicht so schwer, wenn Sie die Zeit haben.


0

Ich habe einfache Spiele in LISP geschrieben und es hat Spaß gemacht, aber nichts, was ich empfehlen würde. Bei der funktionalen Programmierung dreht sich alles um das Endergebnis. Es ist also sehr praktisch, um Daten zu verarbeiten und Entscheidungen zu treffen, aber ich fand es schwierig, einfachen, sauberen Code wie einen einfachen Regelkreis zu erstellen.

Ich habe F # nicht gelernt, daher ist es möglicherweise viel einfacher, damit zu arbeiten.


Mein erster Gedanke könnte sein, das Gerüst und die Zustandsverwaltung mit einer imperativen Sprache zu schreiben und dann Spiellogik und dergleichen mit funktionalen Bits zu machen. Mit .NET und C # / F # wäre dies zum Beispiel sehr einfach, da sie dieselben Bibliotheken verwenden und ohne wirkliche Nachteile, die mir bekannt sind, miteinander kommunizieren können.
McMuttons

-1

Das Positive wäre Leistung und Portabilität, und das Negative wäre das Verwalten von Komplexität . Spiele sind heutzutage sehr komplex und benötigen Tools oder Programmiersprachen, die die Komplexität besser verwalten können, wie objektorientierte Methoden oder generische Programme, die in der funktionalen Programmierung schwer zu implementieren sind Sprache.


11
Willst du mich veräppeln? FP ist eine generische Programmierung für Steroide. Die Fähigkeit, sowohl Datentypen als auch Funktionen zu verallgemeinern, gibt FP die Möglichkeit , die Komplexität zu verwalten.
RTPerson

Ich stimme zu, eine der größten Stärken von FP ist die Fähigkeit, verallgemeinerten Code zu schreiben.
McMuttons
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.