Functional Reactive F# - Storing States in Games
我是一名学生,目前正在学习使用f的功能反应范式。这对我来说是一个全新的观点。昨天我学习了如何用这种模式创建一个简单的乒乓球游戏。到目前为止,我所理解的观点是:我们认为价值是时间的函数。在它的纯形式上,它是无状态的。但是,我需要记住球的位置(或状态)。所以我总是通过球的当前位置作为全局函数的参数。
如果我们谈论一些稍微复杂一些的游戏,比如太空入侵者,我们有很多状态(外星人的位置、外星人当前的生命值、剩余炸弹的数量等)
有优雅/最好的方法来解决这个问题吗?我们是否总是在顶层存储状态?是否应将所有当前状态作为全局函数的附加输入参数给出?
有人能用F上的简单样本来解释这个吗?谢谢。
有不止一种方法可以做玻璃钢,这是一个活跃的研究领域。什么是最好的,很大程度上取决于事物如何相互作用的细节,未来可能会出现新的更好的技术。
大体上来说,我们的想法是让行为具有时间的功能,而不是普通的价值观(如你所说)。行为可以根据其他行为来定义,并且可以定义为在发生特定事件时在其他行为之间进行交换。
在您的示例中,通常不需要通过参数记住球的位置(但是对于某些类型的FRP,您可能需要记住)。相反,你可以有一种行为:
随着事情变得越来越复杂,您将以越来越复杂的方式定义行为,依赖于其他行为和事件-包括在不同的FRP框架中处理不同的递归依赖关系。在f中,对于递归依赖项,我希望您需要一个
1 2 3 4 5 6 7 8 9 | 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} |
然后可以定义alienpos、alienhp的行为,依赖于播放器,playerpos、playerbombs可以依赖于外星人。
不管怎样,如果你能提供你所使用的玻璃钢的更多细节,就更容易给出更具体的建议。(如果你想要什么样的建议-我个人建议阅读:http://conal.net/papers/push-pull-frp/push-pull-frp.pdf)
我没有在f_下进行反应式编程的经验,但是纯功能系统中的全局状态问题是很常见的,并且有一个非常优雅的解决方案:monads。
虽然monad本身主要用于haskell,但底层概念将其作为计算表达式变成了f。
这个想法是,你不需要改变状态,只需要描述状态的转换,即如何产生新的状态。状态本身可以完全隐藏在程序中。通过使用特殊的单体语法,您几乎可以命令性地编写纯的但有状态的程序。
从这个源代码获取(修改过的)实现,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 |
因此,让我们举个例子:我们想要编写一个函数,在继续操作时可以将值写入日志(只是一个列表)。因此,我们定义
1 2 3 4 5 | 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 } |
在
1 2 3 4 5 6 7 8 9 10 11 | 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: %A State: %A" result finalState |
不过,这里的一切都是纯功能的;)
Tomas在f_中对反应式编程做了一个很好的讨论。许多概念应该适用于您的案例。
榆树是一种现代化的玻璃钢结构。对于在诸如"太空入侵者"之类的游戏中普遍存在的动态集合建模,它包含一个基于箭头化FRP概念的自动化库。你一定要去看看。
也许你想看看fsreactive。