Pura programmazione funzionale e stato del gioco


12

Esiste una tecnica comune per gestire lo stato (in generale) in un linguaggio di programmazione funzionale? Esistono soluzioni in ogni linguaggio di programmazione (funzionale) per gestire lo stato globale, ma voglio evitarlo il più possibile.

Tutti gli stati in un puro modo funzionale sono parametri di funzione. Quindi ho bisogno di mettere l'intero stato del gioco (una gigantesca hashmap con il mondo, i giocatori, le posizioni, il punteggio, le risorse, i nemici, ...)) come parametro per tutte le funzioni che vogliono manipolare il mondo su un dato input o trigger . La stessa funzione raccoglie le informazioni rilevanti dal BLOB del gamestato, fa qualcosa con esso, manipola il gamestato e restituisce il gamestato. Ma questa sembra una soluzione povera per il problema. Se metto l'intero gamestate in tutte le funzioni, non c'è alcun vantaggio per me in contrasto con le variabili globali o l'approccio imperativo.

Potrei inserire solo le informazioni rilevanti nelle funzioni e restituire le azioni che verranno intraprese per l'input dato. E una singola funzione applica tutte le azioni al gamestate. Ma la maggior parte delle funzioni necessita di molte informazioni "rilevanti". move()serve la posizione dell'oggetto, la velocità, la mappa per la collisione, la posizione di tutti i clisteri, la salute attuale, ... Quindi neanche questo approccio sembra funzionare.

Quindi la mia domanda è: come posso gestire l'enorme quantità di stato in un linguaggio di programmazione funzionale, specialmente per lo sviluppo di giochi?

EDIT: ci sono alcuni framework di gioco per la costruzione di giochi in Clojure. L'approccio per risolvere parzialmente questo problema è quello di infilare tutti gli oggetti nel gioco come "entità" e metterlo in una borsa enorme. Una funzione principale gigant sta tenendo lo schermo e le entità e gli eventi maniglia ( :on-key-down, :on-init, ...) per questo gli enti ed eseguire il ciclo display principale. Ma questa non è la soluzione pulita che sto cercando.


Ho pensato a questo genere di cose per un po '; per me, non è l' input che è il problema unico, poiché devi ancora alimentare (approssimativamente) gli stessi elementi alle funzioni nella programmazione non funzionale. No, il problema è l' output (e i successivi aggiornamenti correlati). Alcuni dei parametri di input devono essere combinati; perché move(), probabilmente dovresti passare nell'oggetto "corrente" (o un identificatore per esso), oltre al mondo attraverso il quale si sta muovendo, e derivare semplicemente la posizione e la velocità attuali ... l'output è allora l'intero mondo della fisica, o almeno un elenco di oggetti modificati.
Clockwork-Muse

il vantaggio del puro funzionamento è che i prototipi di funzioni mostrano tutte le dipendenze del programma.
tp1

3
IMO, i linguaggi funzionali sono scarsamente adatti per scrivere giochi. Questo è uno dei tanti problemi che dovrai risolvere. I giochi richiedono un controllo molto preciso delle prestazioni e raramente hanno una buona concorrenza, a causa del modo imprevedibile in cui si verificano gli eventi. I linguaggi funzionali (puri) sono noti per essere banalmente parallelizzabili e difficili da ottimizzare. Scrivere un gioco è DURO e consiglio di farlo in un linguaggio tipico, prima di affrontare qualcosa di altrettanto complesso (programmazione funzionale).
Casey Kuball,

Risposte:


7

Gli effetti collaterali e lo stato nei linguaggi di programmazione funzionale sono un problema più ampio nell'informatica. Nel caso non li avessi mai incontrati prima, forse dai un'occhiata alle monadi . Attenzione però: sono un concetto abbastanza avanzato e la maggior parte delle persone che conosco (me compreso) fanno fatica a coglierle. Ci sono molti, molti tutorial online, con approcci e requisiti di conoscenza diversi. Personalmente, mi è piaciuto il migliore di Eric Lippert.

Sono un programmatore C # senza alcun background di "programmazione funzionale". Cos'è questa cosa "monade" di cui continuo a sentir parlare, e a che mi serve?

Eric Lippert su Monads

Alcune cose da considerare, però:

  • Insisti nell'usare un linguaggio puramente funzionale? Se sei abile sia nella programmazione funzionale che nello sviluppo del gioco, forse potresti farlo. (Anche se mi piacerebbe sapere se i benefici valgono lo sforzo.)
  • Non sarebbe meglio usare l'approccio funzionale solo dove necessario? Se stai usando un linguaggio orientato agli oggetti (o, più probabilmente, un multi-paradigma), nulla ti impedisce di usare lo stile funzionale per implementare sezioni che ne traggono profitto. (Un po 'come MapReduce, forse?)

Alcuni pensieri finali:

  • Parallelismo: anche se i giochi lo usano pesantemente, AFAIK la maggior parte di essi accade già nella GPU comunque.
  • Statelessness: le mutazioni di stato sono parte integrante dei giochi. Cercare di sbarazzarsene potrebbe complicare inutilmente le cose.
  • Magari guarda come gioca il linguaggio funzionale F # con l'ecosistema orientato agli oggetti di .NET, se sei interessato.

Tutto sommato, penso che anche se potrebbe essere interessante a livello accademico, dubito che questo approccio sia pratico e che valga la pena.


Perché pubblicare un commento su un argomento in cui non hai esperienza? Un'opinione proveniente da persone intrappolate in un paradigma di pensiero.
Anthony Raimondo,

4

Ho scritto alcuni giochi usando F # (multi-paradigma, impuro, primo linguaggio funzionale), con approcci che vanno da OOP a FRP . Questa è una domanda generica, ma farò del mio meglio.

Esiste una tecnica comune per gestire lo stato (in generale) in un linguaggio di programmazione funzionale?

Il mio modo preferito è avere un tipo immutabile che rappresenti l'intero gioco State. Ho quindi un riferimento mutevole alla corrente Stateche viene aggiornata ad ogni tick. Questo non è strettamente puro, ma mantiene la mutabilità limitata a un posto.

Se metto l'intero gamestate in tutte le funzioni, non ci sono vantaggi per me in contrasto con le variabili globali o l'approccio imperativo.

Non vero. Poiché il Statetipo è immutabile, non è possibile avere componenti vecchi che mutano il mondo in modi mal definiti. Questo risolve il problema più grande con l' GameObjectapproccio (reso popolare da Unity): è difficile controllare l'ordine delle Updatechiamate.

E a differenza dell'uso dei globuli, è facilmente testabile in unità e parallelizzabile,

Dovresti anche scrivere funzioni di supporto che ricevano sotto-proprietà dello stato per risolvere il problema.

Per esempio:

let updateSpaceShip ship = 
  {
    ship with 
      Position = ship.Position + ship.Velocity
  }

let update state = 
  { 
    state with 
      SpaceShips = state.SpaceShips |> map updateSpaceShip 
  }

Qui updateagisce su tutto lo stato, ma updateSpaceShipagisce solo su un individuo SpaceShipin isolamento.

Potrei inserire solo le informazioni rilevanti nelle funzioni e restituire le azioni che verranno intraprese per l'input dato.

Il mio suggerimento sarebbe quello di creare un Inputtipo che contenga gli stati tastiera, mouse, game pad, ecc. È quindi possibile scrivere una funzione che accetta a Statee Inputrestituisce il successivo State:

let applyInput input state = 
  // Something

Per darti un'idea di come si adatta, il gioco in generale potrebbe assomigliare a questo:

let mutable state = initialState ()

// Game loop
while true do
  // Apply user input to the world
  let input = collectInput ()

  state <- applyInput input state

  // Game logic
  let dt = computeDeltaTime ()

  state <- update dt state

  // Draw
  render state

Quindi la mia domanda è: come posso gestire l'enorme quantità di stato in un linguaggio di programmazione funzionale, specialmente per lo sviluppo di giochi?

Per i giochi semplici è possibile utilizzare l'approccio sopra (funzioni di aiuto). Per qualcosa di più complicato, potresti provare " obiettivi ".

Contrariamente ad alcuni dei commenti qui, suggerirei vivamente di scrivere giochi in un linguaggio di programmazione (impuro) funzionale, o almeno di provarlo! Ho scoperto che rende l'iterazione più veloce, le basi di codice più piccole e i bug meno comuni.

Inoltre , non penso che tu debba imparare le monadi per scrivere giochi in un linguaggio (impuro) FP. Questo perché molto probabilmente il tuo codice sarà bloccato e con thread singolo.

Ciò è particolarmente vero se stai scrivendo una partita multiplayer. Quindi le cose diventeranno molto più facili con questo approccio perché ti consente di serializzare banalmente lo stato del gioco e inviarlo attraverso la rete.

Per quanto riguarda il motivo per cui più giochi non sono scritti in questo modo ... Non posso dire. Tuttavia, questo è vero in tutti i domini di programmazione (tranne forse la finanza), quindi non lo userei come argomento secondo cui i linguaggi funzionali non sono adatti alla programmazione di gioco.

Merita una lettura anche il Retrogames puramente funzionale .


1

Quello che stai cercando è lo sviluppo di giochi FRP.

Alcune presentazioni video:

È possibile e preferibile al 100% rendere la logica di gioco principale in un modo puramente funzionale, l'industria nel suo insieme è semplicemente indietro, bloccata in un paradigma di pensiero.

È possibile farlo anche in Unity.

Per rispondere alla domanda un nuovo stato del gioco verrà aggiornato / creato ogni volta che qualcosa si muove, come dice Carmack nei suoi discorsi, non è un problema. La drastica riduzione del sovraccarico cognitivo che deriva da un'architettura puramente funzionale, altamente mantenibile e flessibile supera di gran lunga le prestazioni, se esiste.

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.