Storing States in Games

I am a student currently learning about Functional Reactive paradigm using F#. It's radically new viewpoint for me. Yesterday I learned about creating a simple ping-pong game using this paradigm. The idea I grasp so far is : we think values as functions of time. On its pure form, it's stateless. However, I need to remember the position of the ball (or state). So I always pass the current position of the ball as the parameter of the global function.

If we talk about slight more complex games, like Space Invaders, we have a lot of states (aliens' position, aliens' current HP, number of remaining bombs, etc)

Is there an elegant/best way to tackle this problem? Do we always store states on the top level? Do all current states should be given as the additional input argument of the global function?

Can anybody explain this using simple sample on F#? Thanks a lot.


There's more than one way to do FRP, and it's an active area of research. What's best can depend a lot on the details of how things interact with each other, and new and better techniques may appear in the future.

Broadly the idea is to have behaviours that are functions of time in place of ordinary values (as you said). Behaviours can be defined in terms of other behaviours, and can be defined to swap between other behaviours when particular events occur.

In your example, you generally wouldn't need to remember the position of the ball via arguments (but for some kinds of FRP you might do). Instead you can just have a behaviour:
ballPos : time -> (float * float)
This might have global scope, or for a larger program it may be better to have a local scope with all uses of it in that scope.

As things get more complicated, you'll have behaviours defined in increasingly complex ways, depend on other behaviours and events - including recursive dependencies which are handled differently in different FRP frameworks. In F# for recursive dependencies I'd expect you'd need a let rec including all involved behaviours. These can still be organised into structures though - at the top-level you might have:

type alienInfo =  { pos : float*float; hp : float }
type playerInfo = { pos : float*float; bombs : int } 
let rec aliens : time -> alienInfo array =             // You might want laziness here.
    let behaviours = [| for n in 1..numAliens -> 
                        (alienPos player n, alienHP player n) |]
    fun t -> [| for (posBeh, hpBeh) in behaviours -> 
                {pos=posBeh t; hp=hpBeh t} |]          // You might want laziness here.
and player : time -> playerInfo  = fun t ->
    { pos=playerPos aliens t; bombs=playerBombs aliens t}

And then the behaviours for alienPos, alienHP can be defined, with dependencies on the player, and playerPos, playerBombs can be defined with dependencies on aliens.

Anyway, if you can give more details of what kind of FRP you're using, it will be easier to give more specific advice. (And if you want advice on what kind - personally I'd recommend reading: http://conal.net/papers/push-pull-frp/push-pull-frp.pdf)


I haven't got any experience with reactive programming under F#, but the problem of global state in purely functional systems is quite common and has a quite elegant solution: Monads .

While monads themselves are primarily used in Haskell, the underlying concept made it into F# as computation expressions.

The idea is that you don't actually change states but just describe the transitions of the state, ie how to produce new states. The state itself can be completely hidden in the programm. By using special monadic syntax, you can write pure but stateful programms almost imperatively.

Taking a (modified) implementation from this source, the State monad could look like this

let (>>=) x f =
   (fun s0 ->
      let a,s = x s0    
      f a s)       
let returnS a = (fun s -> a, s)

type StateBuilder() =
  member m.Delay(f) = f()
  member m.Bind(x, f) = x >>= f
  member m.Return a = returnS a
  member m.ReturnFrom(f) = f

let state = new StateBuilder()     

let getState = (fun s -> s, s)
let setState s = (fun _ -> (),s) 

let runState m s = m s |> fst

So let's have an example: We want to write a function that can write values into a log (just a list) while proceeding. We therefore define

let writeLog x = state {
  let! oldLog = getState // Notice the ! for monadic computations (i.e. where the state is involved)
  do! setState (oldLog @ [x]) // Set new state
  return () // Just return (), we only update the state
}

Within state , we can now use this in an imperative syntax without having to handle the log list manually.

let test = state {
   let k = 42
   do! writeLog k // It's just that - no log list we had to handle explicitly
   let b = 2 * k
   do! writeLog b
   return "Blub"
}

let (result, finalState) = test [] // Run the stateful computation (starting with an empty list)
printfn "Result: %AnState: %A" result finalState

Still, everything is purely functional here ;)


Tomas gave a nice talk about reactive programming in F#. Many concepts should apply in your case.

链接地址: http://www.djcxy.com/p/47716.html

上一篇: 函数式编程如何应用于仿真?

下一篇: 在游戏中存储国家