Quali sono le sfide e i vantaggi della scrittura di giochi con un linguaggio funzionale?


81

Mentre so che i linguaggi funzionali non sono i più comunemente usati per la scrittura di giochi, ci sono molti vantaggi associati a loro che sembrano interessanti in qualsiasi contesto di programmazione. Soprattutto la facilità di parallelizzazione penso che potrebbe essere molto utile poiché l'attenzione si sta spostando verso un numero sempre maggiore di processori.

Inoltre, con F # come nuovo membro della famiglia .NET, può essere utilizzato direttamente con XNA, ad esempio, il che abbassa un po 'la soglia, al contrario di LISP, Haskell, Erlang, ecc.

Se qualcuno ha esperienza nella scrittura di giochi con codice funzionale, quali sono risultati positivi e negativi? A cosa era adatto, cosa no?

Modifica: trovo difficile decidere che esiste un'unica buona risposta per questo, quindi è probabilmente più adatto come post wiki della comunità.


3
con uno dei miei giochi preferiti di sempre (Rogue) scritto in LISP, questa domanda (e il suo potenziale di risposta) è molto interessante per me.
Justin L.,

Non sapevo che Rogue fosse scritto in LISP, ma lo trovo interessante. Gioco classico. :)
McMuttons il

2
Non sono sicuro di cosa stai parlando. Rogue è stato scritto in C. roguebasin.roguelikedevelopment.org/index.php?title=Rogue
Mason Wheeler il

2
Sono sicuro che hai ragione sul fatto che Rogue originale fosse in C, dato che è stato scritto su Unix, ma ci sono sicuramente diversi cloni Rogue in Lisp: cl-user.net/asp/root-dir (non il link di gioco diretto , aveva così tanti personaggi speciali che sembra avere un collegamento interrotto).
Ciclope,

Risposte:


57

Attualmente sto lavorando a un gioco in Haskell. Non posso parlare per la programmazione funzionale in generale, ma per Haskell in particolare:

Il bene

Queste sono le cose fantastiche che fanno davvero risaltare Haskell.

  • Purezza significa che il ragionamento sul tuo codice è molto più semplice. Non devi preoccuparti del valore "corrente" di una variabile o se l'utilizzo di una determinata funzione entrerà in conflitto con il tuo stato globale in qualche modo. Significa anche che il parallelismo e la concorrenza sono molto più facili da lavorare (e in molti casi banali). Queste cose possono essere una manna per gli sviluppatori di giochi.
  • Haskell rende molto facile scrivere a un livello molto alto usando le astrazioni della tua stessa creazione. Spesso è più facile scrivere un codice molto generico rispetto al codice specializzato in Haskell, e i vantaggi di questo si manifestano in meno tempo di sviluppo e una comprensione del codice più semplice. È più vero in Haskell che in qualsiasi altra lingua che conosco che usare un'interfaccia è come usare un linguaggio completamente nuovo (pur mantenendo i vantaggi del resto dell'ecosistema Haskell). Quello che la maggior parte delle lingue chiama funzionalità linguistiche, Haskell chiama una libreria e non si sente forzato. Se mai ti ritrovi a ragionare sul tuo gioco in psuedocode, probabilmente potresti semplicemente scrivere una semplice interfaccia e renderlo vero codice, e di solito vale la pena farlo.
  • Ciò potrebbe essere in conflitto con altre risposte, ma Haskell è in grado di gestire il codice imperativo meglio di qualsiasi altra lingua che conosco. I vincoli di purezza significano che Haskell ha una separazione tra i concetti di "valutazione" (riduzione delle espressioni) ed "esecuzione" (esecuzione di effetti collaterali). Puoi controllare quando e come gli effetti collaterali vengono eseguiti con facilità senza pari dai cosiddetti linguaggi "imperativi". Nonostante ciò che ho detto in precedenza sulla purezza, alcuni vincoli C sono ancora molto imperativi (OpenGL, ti sto guardando), quindi questo controllo a grana fine sul codice imperativo è il benvenuto. Anche i programmatori C più esperti apprezzeranno probabilmente una volta che si abitueranno.
  • GHC genera binari piuttosto veloci. Non sono veloci come i binari generati dai comuni compilatori C, ma sono in un ordine di grandezza e sono molto più veloci di quello che otterresti con un linguaggio interpretato. Anche se non puoi scrivere il tuo gioco in altre lingue di alto livello come Python perché sarebbe troppo lento, ci sono buone probabilità che tu possa farlo in Haskell.
  • Nonostante non ci siano molte librerie relative al gioco in Haskell, come indicato sotto "The Bad" di seguito, l'interfaccia di funzioni straniere di Haskell è la più semplice che io abbia mai usato. Puoi legare a C scrivendo quasi esclusivamente in Haskell.

Il cattivo

Queste sono cose che non sono buone ma che possono essere risolte senza troppi sforzi.

  • Il tempo e lo sforzo necessari per imparare l'Haskell possono essere piuttosto elevati se sei abituato a lavorare con linguaggi non funzionali. La lingua in sé non è molto difficile, ma quando si tiene conto delle estensioni linguistiche comuni e dell'enorme quantità di librerie (ricordate, molte cose che potreste considerare le funzionalità linguistiche in altre lingue sono librerie in Haskell), sembra molto più inquietante. Secondo me, ne vale la pena, ma altri potrebbero non essere d'accordo.
  • Alcune persone si lamentano molto della pigrizia. Se sai cosa stai facendo, non causerai perdite di spazio che sono difficili da rintracciare (non ho creato una perdita di spazio in un paio d'anni ormai), ma i principianti hanno molti più problemi con questo. Sarebbe sciocco da parte mia abbattere queste persone e affermare che questo non è un problema, ma affermerò che è un problema che può essere facilmente risolto con l'esperienza.
  • Non ci sono molte grandi librerie per creare giochi in Haskell, e non molti Haskeller ci scrivono giochi, quindi è difficile trovare risorse e aiuto su questo argomento.

Il brutto

Queste sono cose che richiederebbero notevoli sforzi per superare.

  • Se stai scrivendo un gioco ad alta intensità di risorse, il garbage collector di GHC potrebbe morderti abbastanza forte. È un GC generazionale e all'avanguardia, quindi ogni tanto potresti perdere qualche fotogramma. Per alcuni giochi questo va bene, ma per altri è un grosso problema. Io e alcuni altri sviluppatori di giochi che usano Haskell vorremmo vedere un GC soft-real-time implementato per GHC, ma per quanto ne so, nessuno sforzo è stato speso per questo. Attualmente, non mi preoccupo di aggirare questo problema (e non ho ancora visto alcun frame rilasciato da esso), ma almeno un altro team ha fatto ricorso a mettere il loro ciclo di rendering principale in C.

2
Cosa ne pensi di FRP?
Wei Hu,

4
@Wei Hu: Sono un grande fan dell'idea di FRP e l'ho studiato abbastanza ampiamente da me stesso, inclusa la creazione di alcune semantiche diverse e la scrittura di alcune implementazioni. Le migliori implementazioni presentano ancora gravi errori semantici e / o di implementazione. Direi che sono praticabili per lo sviluppo di giochi, ma non con molti vantaggi (ancora) rispetto agli approcci tradizionali.
Jake McArthur,

1
Un utente anonimo ha provato a modificare questa risposta cancellando la parte "brutta". "Il garbage collector di Haskell è ora concorrente, parallelo e generazionale dal 2011 ( ghc.haskell.org/trac/ghc/blog/new-gc-preview )"
Jari Komppa

1
Sfortunatamente, questo non è in realtà vero. Il concomitante GC è stato trovato troppo complicato per giustificare il piccolo miglioramento complessivo, e non è stato unito.
Jake McArthur

1
@ Hi-Angel Esiste un vecchio backend GHC che ha generato C, ma non è diverso dal generatore di codice nativo o dal back-end LLVM. Richiede ancora il runtime GHC. Inoltre, non c'è nulla di speciale in un back-end C che potrebbe rendere più semplice evitare un garbage collector. Se fosse così semplice ed economico eliminare il GC, probabilmente lo farebbero anche gli altri backend.
Jake McArthur,

19

Ho trascorso l'ultimo anno a sviluppare un motore di gioco commerciale a Haskell e per noi l'esperienza è stata straordinariamente positiva. Il nostro mondo di gioco è complesso e Haskell ha reso semplice la modellazione del processo di conversione da un formato editor a un formato motore di gioco. Odierei pensare come sarebbe quel codice in un linguaggio imperativo.

Le perdite di spazio sono emerse in occasioni, e mentre hanno causato un po 'di problemi, nello schema generale è stata una piccola quantità (ad esempio rispetto alla ricerca di deadlock in progetti Java di dimensioni simili), e una volta risolti , rimasero fissi.

Stiamo usando FRP simile a Yampa, e c'è sicuramente una curva di apprendimento ad esso associata, ma una volta che è finita, l'esperienza è molto positiva. Le biblioteche non sono state un problema per noi: tutto ciò di cui avevamo bisogno era disponibile. I ritardi nella raccolta dei rifiuti sono stati un problema particolare poiché si tratta di una piattaforma integrata. Abbiamo usato un po 'di C ++ per gestire l'animazione. Le prestazioni sono state anche un problema con questa essendo una piattaforma integrata (= processore lento). Abbiamo fatto un po 'di C e guardiamo anche alle tecnologie emergenti di Haskell come accelerare. L'animatore C ++ è stato una decisione di progettazione all'inizio e i luoghi in cui il codice è troppo lento sono solo aree molto piccole. A lungo termine, vogliamo tradurre tutte le nostre C in Haskell.

Haskell ha reso facile un lavoro difficile e tutte le difficoltà che ho appena menzionato sono state minime rispetto alla grande quantità di codice complesso che abbiamo prodotto che è pulito, gestibile e praticamente indistruttibile. Il parallelismo sarà presto un problema nello sviluppo del gioco e siamo estremamente ben posizionati per affrontarlo. Parte di ciò che ho detto potrebbe non applicarsi ai piccoli progetti, perché ci stiamo occupando a lungo termine, quindi i costi di avvio come le curve di apprendimento, il supporto delle biblioteche, ecc. Sono molto meno problematici.


7
Si stanno avvicinando 5 anni da quando hai pubblicato questa risposta; saresti disposto ad aggiornarlo? Come ha funzionato il motore di gioco commerciale? Quali pezzi si sono rivelati preziosi? Sei stato in grado di sfruttare il parallelismo come speravi?
Levi Morrison,

17

Dave menziona alcuni punti eccellenti, anche se devo sottolineare che Haskell ha risolto entrambi i suoi problemi. L'apolidia può essere aggirata usando la monade di stato (EDIT: non proprio - vedi sotto per i dettagli) , e il sequenziamento può essere gestito usando la monade IO (EDIT: o qualsiasi altra monade, per quella materia ...) .

Le sfide che dovrai affrontare (e che ho provato a imparare sia alla programmazione di giochi sia a Haskell), vanno più in questo senso. (Questi sono tutti specifici di Haskell, dal momento che non ho ancora approfondito con altre lingue FP.)

  • Curva di apprendimento FP: FP richiede un completo cambiamento di mentalità dalla programmazione iterativa. Imparare a pensare in termini di mappe e pieghe piuttosto che in loop richiede un allenamento mentale se non ci si è abituati.
  • Curva di apprendimento della Mathy: Haskell è sia benedetto che maledetto dalla sofisticazione matematica dei suoi progettisti, perché dopo aver appreso le basi di FP, sei bloccato con monadi, morfismi, frecce e tutta una serie di altre considerazioni che, sebbene non difficili in sé e per sé, sono molto astratti.
  • Monadi: questi cuccioli non sono particolarmente difficili da distrarre, soprattutto se accetti l'affermazione di Eric Raymond secondo cui sono "un trucco per trasformare la composizione di funzioni in un modo di forzare il sequenziamento delle chiamate di funzioni". Sfortunatamente (sebbene questo stia migliorando man mano che Haskell guadagna popolarità), molte spiegazioni delle monadi mirano molto al di sopra della testa della maggior parte dei programmatori ("sono monoidi nella categoria degli endofunctor!"), O in un'analogia del tutto inutile (ad es. , L'esempio dolorosamente accurato di Brent Yorgey, "le monadi sono come i burritos !" Pensi che stia scherzando? Che nessuno potesse mai inventare un'analogia così sciocca in un tutorial? Ripensaci .)
  • Ecosistema: se sei riuscito a superare tutti gli altri dossi della strada, ti guiderai contro la realtà pratica quotidiana di lavorare con un linguaggio ancora ampiamente sperimentale. Dio ti aiuti quando arrivi qui. Qui è dove ho iniziato a fare seriamente jonesing per Scala, o F #, o qualche altra lingua in cui esiste almeno una garanzia di interoperabilità con altre librerie. Una volta, ho ottenuto Haskell per collegare e caricare le librerie OpenGL in Windows. Mi ha fatto sentire abbastanza bene. Quindi i collegamenti OpenGL sono stati nuovamente rilasciati e hanno rotto tutto ciò che avevo fatto. Questa è la vita nella terra di Haskell. Onestamente, può essere un dolore. Vuoi un wrapper per il motore Bullet? E 'in Hackage - come abandonwareda novembre dello scorso anno. Ti piace Ogre3d? L'hackage ha collegamenti solo a un sottoinsieme della libreria . La linea di fondo è che se ti attieni a una lingua fuori dai sentieri battuti per lo sviluppo del gioco, passerai molto più tempo a lavorare sull'impianto idraulico di base di quanto faresti se seguissi la mandria e rimanessi bloccato con C ++.

Il rovescio della medaglia è che le cose stanno rapidamente migliorando. E tutto dipende da cosa vuoi dall'esperienza. Vuoi costruire un gioco e metterlo sul tuo sito web per cercare fama e fortuna? Stick con C ++ o Python. Ma se vuoi imparare qualcosa di nuovo che ti richiederà di innovare i tuoi processi, prova un linguaggio funzionale. Basta avere molta pazienza con te stesso mentre stai imparando.

Antti Salonen ha un blog interessante che illustra in dettaglio la sua relazione di nuovo con la programmazione dei giochi di Haskell. Vale la pena leggere.

Modifica (un anno dopo): ora che ho studiato di più la monade di stato, mi rendo conto che non è una soluzione particolarmente buona per lo stato che è destinato a persistere al di fuori di una particolare funzione. Le vere soluzioni per l'apolidia si trovano in Haskell in IOVar, ST, MVar (per la sicurezza dei thread) o attraverso qualcosa come Yampa, che utilizza Arrows e FRP per gestire lo stato interno che è comunque nascosto allo sviluppatore. (Questo elenco è in ordine di difficoltà, anche se i primi tre non sono particolarmente difficili una volta comprese le monadi.)


1
Alcuni buoni pensieri lì, anche se abbastanza specifici per Haskell, penso che siano pensieri che si applicano alla maggior parte delle lingue marginali. Come hai detto brevemente, penso che sia qui che linguaggi come F # hanno il potenziale per brillare. Gestire state / UI con C #, ad esempio, e quindi avere moduli logici in F # per algoritmi come ricerca di percorsi potenzialmente, AI, ecc.
McMuttons

5

Obiettivo Caml!

L'uso di linguaggi funzionali può essere un enorme vantaggio nella maggior parte dei tipi di sviluppo software, soprattutto perché riducono notevolmente i tempi di sviluppo. Riesco a vedere un grande potenziale per la scrittura di un server back-end in un gioco, o i livelli di intelligenza artificiale e logica sul client, in un linguaggio funzionale. E come tutti sanno, LISP è stato utilizzato per gli script NPC.

Se dovessi provare a scrivere il frontend di un gioco in un linguaggio funzionale, sceglierei sicuramente Objective Caml , che è un linguaggio ibrido. È un discendente ML e consente di mescolare stili iterativi e funzionali, ha un sistema oggettivo con oggetti di stato. (Caml è la lingua su cui è modellato F #.)

I collegamenti OpenGL sembrano funzionare perfettamente e le persone hanno creato giochi in OCaml da molto tempo. Il linguaggio può essere compilato ed è potenzialmente molto veloce (notoriamente ha vinto su C in alcuni benchmark molto tempo fa, non come stanno le cose adesso).

Ci sono anche tonnellate di biblioteche. Tutto, dalle cose di informatica alle associazioni a tutti i tipi di biblioteche.



3

Per quanto riguarda i vantaggi, controlla Clean Game Library (http://cleangl.sourceforge.net/) e controlla il gioco platform. Una volta che hai la testa intorno alla sintassi (ti incoraggio a provare) vedrai l'eleganza pura dei costrutti e quanto la fonte si avvicina ai concetti che stanno esprimendo. Come bonus aggiuntivo, l'astrazione dello stato e la necessità di passare esplicitamente lo stato in giro, rende il tuo codice molto più multicore amichevole.

D'altra parte, richiede un grande cambiamento di paradigma. Molto di ciò che sei abituato a fare senza pensarci all'improvviso non esiste più e ti sconcerterai su come risolverlo, semplicemente perché stai pensando in schemi sbagliati.


2

Dal mio punto di vista le maggiori sfide saranno:

  • Statelessness dei linguaggi funzionali: l'idea nei giochi è che ogni entità ha uno stato e questo stato viene modificato da frame a frame. Esistono dei meccanismi per imitare quel comportamento in alcune lingue (ad esempio funziona con uno schema "!" In plt) ma sembra in qualche modo innaturale.
  • Ordine di esecuzione : nei giochi alcune parti di codice dipendono da altre parti di codice da completare prima dell'esecuzione. I linguaggi funzionali generalmente non forniscono alcuna garanzia sull'ordine di esecuzione degli argomenti di una funzione. Esistono modi per imitare questo comportamento (ad esempio " (begin..." nello schema plt).

Sentiti libero di estendere questo elenco (e forse di aggiungere alcuni punti positivi ^^).


1
L'ordine di esecuzione può variare in un linguaggio non rigoroso come Haskell ma non causa problemi per le parti di codice che dipendono da altre parti di codice. Puoi pensarlo come una valutazione su richiesta. Un calcolo non viene eseguito fino a quando non è necessario il risultato. Penso che l'unico modo che potrebbe causare dolore sia in termini di prestazioni. Ad esempio, in alcuni casi potrebbe essere difficile ottenere una buona memorizzazione nella cache perché non si avrà il controllo sull'ordinamento della valutazione oltre a specificare le dipendenze tra i calcoli.
Jason Dagit il

1

Puoi scrivere il tuo codice in C / C ++ e usare un linguaggio incorporato come Embedded Common Lisp per i tuoi script. Sebbene riuscire a compilarli su una piattaforma diversa potrebbe essere difficile. Potresti guardare Lisp In Small Pieces per imparare a scrivere il tuo linguaggio di scripting. Non è poi così difficile, se hai tempo.


0

Ho scritto semplici giochi in LISP ed è stato divertente, ma non qualcosa che consiglierei. La programmazione funzionale riguarda il risultato finale. Quindi è molto utile per elaborare i dati e prendere decisioni, ma ho trovato difficile rendere semplice il codice pulito come un ciclo di controllo di base.

Non ho imparato F # anche se quindi potrebbe essere molto più facile lavorare con.


Il mio pensiero iniziale potrebbe essere quello di scrivere l'impalcatura e la gestione dello stato con un linguaggio imperativo, e quindi fare la logica di gioco e simili con bit funzionali. Con .NET e C # / F # questo sarebbe molto semplice, ad esempio, dato che usano le stesse librerie e possono parlare tra loro senza alcuna penalità reale di cui io sia a conoscenza.
McMuttons,

-1

il positivo sarebbe la prestazione e la portabilità e il negativo sarebbe la gestione della complessità , il gioco oggi è seriamente complesso e necessita di strumenti o linguaggio di programmazione in grado di gestire meglio la complessità, come la metodologia orientata agli oggetti o la programmazione generica che è difficile da implementare nella programmazione funzionale linguaggio.


11
Ma stai scherzando? FP è una programmazione generica con steroidi. Essere in grado di generalizzare sia i tipi di dati che le funzioni dà a FP un serio potere di gestire la complessità.
rtperson

Sono d'accordo, uno dei maggiori punti di forza di FP è la sua capacità di fare codice generalizzato.
McMuttons,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.