Pura programación funcional y estado del juego


12

¿Existe una técnica común para manejar el estado (en general) en un lenguaje de programación funcional? Hay soluciones en cada lenguaje de programación (funcional) para manejar el estado global, pero quiero evitar esto en la medida de lo posible.

Todos los estados de manera puramente funcional son parámetros de función. Así que necesito poner todo el estado del juego (un gigantesco hashmap con el mundo, jugadores, posiciones, puntaje, activos, enemigos, ...) como un parámetro para todas las funciones que desean manipular el mundo en una entrada o disparador dado . La función en sí recoge la información relevante del blob del estado del juego, hace algo con él, manipula el estado del juego y devuelve el estado del juego. Pero esto parece una solución pobre para el problema. Si pongo todo el estado del juego en todas las funciones, no hay ningún beneficio para mí en contraste con las variables globales o el enfoque imperativo.

Podría poner solo la información relevante en las funciones y devolver las acciones que se tomarán para la entrada dada. Y una sola función aplica todas las acciones al estado del juego. Pero la mayoría de las funciones necesitan mucha información "relevante". move()necesita la posición del objeto, la velocidad, el mapa de colisión, la posición de todos los enemigos, la salud actual, ... Así que este enfoque tampoco parece funcionar.

Entonces, mi pregunta es ¿cómo manejo la gran cantidad de estado en un lenguaje de programación funcional, especialmente para el desarrollo de juegos?

EDITAR: Hay algunos marcos de juego para construir juegos en Clojure. El enfoque para resolver este problema parcialmente es enhebrar todos los objetos en el juego como "entidades" y ponerlo en una bolsa enorme. Una función principal Gigant es la celebración de la pantalla y las entidades y eventos mango ( :on-key-down, :on-init, ...) para esta entidades y ejecutar el bucle principal de la pantalla. Pero esta no es la solución limpia que estoy buscando.


He estado pensando en este tipo de cosas por un tiempo; para mí, no es la entrada ese es el problema único, ya que todavía tiene que alimentar (aproximadamente) los mismos elementos para las funciones en la programación no funcional. No, es la salida (y las actualizaciones relacionadas posteriores) ese es el problema. Algunos de sus parámetros de entrada deben combinarse; para move(), probablemente deberías estar pasando el objeto 'actual' (o un identificador para él), más el mundo por el que se está moviendo, y simplemente derivar la posición y velocidad actuales ... la salida es entonces todo el mundo de la física, o al menos Una lista de objetos modificados.
Clockwork-Muse

La ventaja de funcional puro es que los prototipos de funciones muestran todas las dependencias que tiene su programa.
tp1

3
En mi opinión, los lenguajes funcionales no son adecuados para escribir juegos. Este es uno de los muchos problemas que deberá resolver. Los juegos requieren un control muy preciso del rendimiento, y rara vez tienen buena concurrencia, debido a la forma impredecible de los eventos que ocurren naturalmente. Los lenguajes funcionales (puros) destacan por ser trivialmente paralelizables y difíciles de optimizar. Es difícil escribir un juego, y recomiendo hacerlo en un lenguaje típico, antes de asumir algo igual de complejo (programación funcional).
Casey Kuball

Respuestas:


7

Los efectos secundarios y el estado en los lenguajes de programación funcional son un problema más amplio en informática. En caso de que no los haya encontrado antes, tal vez eche un vistazo a las mónadas . Sin embargo, tenga cuidado: son un concepto bastante avanzado y la mayoría de las personas que conozco (incluido yo) luchan por comprenderlos. Hay muchos, muchos tutoriales en línea, con diferentes enfoques y requisitos de conocimiento. Personalmente, me gustó lo mejor de Eric Lippert.

Soy un programador de C # sin antecedentes de "programación funcional". ¿Qué es esta cosa de la "mónada" de la que sigo oyendo y para qué me sirve?

Eric Lippert sobre mónadas

Algunas cosas a considerar, sin embargo:

  • ¿Insiste en usar un lenguaje puramente funcional? Si eres experto tanto en programación funcional como en desarrollo de juegos, tal vez puedas llevarlo a cabo. (Aunque me gustaría saber si los beneficios valen la pena).
  • ¿No sería mejor utilizar un enfoque funcional solo cuando sea necesario? Si está utilizando un lenguaje orientado a objetos (o, lo más probable, un multi-paradigma), nada le impedirá usar un estilo funcional para implementar secciones que se beneficien de él. (¿Un poco como MapReduce, tal vez?)

Algunas reflexiones finales:

  • Paralelismo: si bien los juegos lo usan mucho, AFAIK la mayor parte ya ocurre en la GPU de todos modos.
  • Apatridia: las mutaciones de estado son una parte integral de los juegos. Intentar deshacerse de ellos podría complicar las cosas innecesariamente.
  • Tal vez mire cómo el lenguaje funcional F # juega con el ecosistema orientado a objetos de .NET, si está interesado.

En general, creo que incluso si pudiera ser interesante académicamente, dudo que este enfoque sea práctico y valga la pena.


¿Por qué publicar un comentario sobre un tema en el que no tienes experiencia? Una opinión proveniente de personas atrapadas en un paradigma de pensamiento.
Anthony Raimondo

4

He escrito algunos juegos usando F # (multi-paradigma, impuro, lenguaje funcional primero), con enfoques que van desde OOP hasta FRP . Esta es una pregunta amplia, pero haré lo mejor que pueda.

¿Existe una técnica común para manejar el estado (en general) en un lenguaje de programación funcional?

Mi forma preferida es tener un tipo inmutable que represente todo el juego State. Luego tengo una referencia mutable a la corriente Stateque se actualiza cada tic. Esto no es estrictamente puro, pero mantiene la mutabilidad limitada a un solo lugar.

Si pongo todo el estado del juego en todas las funciones, no hay ningún beneficio para mí en contraste con las variables globales o el enfoque imperativo.

No es verdad. Debido a que el Statetipo es inmutable, no puede tener ningún componente antiguo que mute el mundo de manera mal definida. Esto soluciona el mayor problema con el GameObjectenfoque (popularizado por Unity): es difícil controlar el orden de las Updatellamadas.

Y a diferencia del uso de globals, es fácilmente comprobable por unidades y paralelo,

También debe escribir funciones auxiliares que reciban subpropiedades del estado para desglosar el problema.

Por ejemplo:

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

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

Aquí updateactúa en todo el estado, pero updateSpaceShipsolo actúa en un individuo SpaceShipaislado.

Podría poner solo la información relevante en las funciones y devolver las acciones que se tomarán para la entrada dada.

Mi sugerencia sería crear un Inputtipo que contenga los estados de teclado, mouse, gamepad, etc. A continuación, puede escribir una función que tome un Statey Inputdevuelva el siguiente State:

let applyInput input state = 
  // Something

Para darle una idea de cómo encaja esto, el juego en general podría verse así:

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

Entonces, mi pregunta es ¿cómo manejo la gran cantidad de estado en un lenguaje de programación funcional, especialmente para el desarrollo de juegos?

Para juegos simples, puede usar el enfoque anterior (funciones auxiliares). Para algo más complicado, es posible que desee probar " lentes ".

Al contrario de algunos de los comentarios aquí, sugeriría encarecidamente escribir juegos en un lenguaje de programación funcional (impuro), ¡o al menos intentarlo! He descubierto que hace que la iteración sea más rápida, que las bases de código sean más pequeñas y que los errores sean menos comunes.

Yo también no creo que hay que aprender a mónadas juegos de escritura en un (impuro) Idioma FP. Esto se debe a que su código probablemente estará bloqueando y con un solo subproceso.

Esto es particularmente cierto si estás escribiendo un juego multijugador. Entonces las cosas se volverán mucho más fáciles con este enfoque porque te permite serializar trivialmente el estado del juego y enviarlo a través de la red.

En cuanto a por qué no se escriben más juegos de esta manera ... No puedo decir. Sin embargo, esto es cierto en todos los dominios de programación (excepto quizás en finanzas), por lo que no usaría esto como argumento de que los lenguajes funcionales no son adecuados para la programación de juegos.

También vale la pena leer es Retrogames puramente funcionales .


1

Lo que estás buscando es el desarrollo de juegos FRP.

Algunas introducciones de video:

Es 100% posible y preferible hacer que la lógica central del juego sea puramente funcional, la industria en su conjunto simplemente está detrás, atrapada en un paradigma de pensamiento.

También es posible hacerlo en Unity.

Para responder a la pregunta, se actualizará / creará un nuevo estado del juego cada vez que algo se mueva, como dice Carmack en su charla, no es un problema. La reducción drástica en la sobrecarga cognitiva que proviene de una arquitectura puramente funcional, altamente mantenible y flexible, supera en gran medida el rendimiento, si es que existe.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.