Scala游戏编程 - 在OOP和FP之间

时间:2012-01-03 15:26:54

标签: oop scala functional-programming

我正在尝试用Scala编写一个教程游戏。处理,打算尽可能多地使用FP。但是,我得出结论,不可变状态游戏对象在这样的应用程序中是无利可图的。如果一个对象很大,那么在许多这样的对象不断更新的情况下(因此,每个周期都要自己复制),例如使用copy() func,它可能会导致非常密集的内存消耗。坚决这个的默认方法是什么?我想出的唯一一件事就是将对象切成小块 - 对象,这样只有那些需要更新的对象才会被更新,同时让“大”对象保持不变。

3 个答案:

答案 0 :(得分:8)

游戏引擎本质上是一种(离散的)事件模拟。通常,这些是通过可变数据结构实现的,例如事件堆,quad / oct树,用于对象的空间查询和大量哈希表。

对于这些数据结构中的每一个,可变变体更快。此外,不可变数据结构会产生必须收集的垃圾,因此GC上的压力会更高,并且您的应用程序最终会变慢。如果实时是一个问题,GC暂停可能是有害的(例如,它可能会影响游戏中的帧速率),尤其是在处理能力较低的平台上,例如Android。

至于copy()方法 - 它不必复制整个对象来创建更新版本。如果您的对象是以树状方式分层组织的(例如,作为案例类的层次结构),那么更改对象的一个​​属性需要只重写此树中的一个路径 - 您不需要重写所有内容。尽管如此,它还是比拥有可变版本和就地更新更昂贵。

示例:

case class World(map: Array[Item], players: Vector[Player])

case class Player(health: Int, speed: Int, weapon: Weapon, shield: Shield)

case class Weapon(strength: Int, ammo: Int)

要为武器添加更多弹药,您无需复制整个World

def setAmmo(playerNum: Int, newAmmo: Int, world: World): World = {
  val p = players(playerNum)
  world.copy(players = players.updated(playerNum, p.copy(weapon = p.weapon.copy(ammo = newAmmo))))
}

在此示例中,map,其他players和修改后的shield的{​​{1}}在内存中保持不变,并且不会急切复制。

我建议使用可变数据结构来表示状态 - 除非你有一个并发游戏引擎(例如)一个模拟游戏状态的编写器,以及一系列呈现输出,处理声音的读者,网络等等,在这个用例中,不可变数据结构的好处几乎没有。

答案 1 :(得分:7)

首先,不要做过早的优化。你测量过代码了吗?也许有一些具体的瓶颈?

由于大多数对象都是由通过数据结构连接的较小对象组成,我认为你可以通过使用持久数据结构来解决这个问题

  

持久性数据结构是一种始终保留的数据结构   修改后的自身的先前版本;这样的数据   结构实际上是不可变的,因为它们的操作没有   (明显地)就地更新结构,但总是产生一个   新的更新结构

Here is wonderful talk关于其中一些内容的{p> Daniel Spiewak。如果你想要更多,请看看Chris Okasaki的Purely Functional Data Structures

答案 2 :(得分:1)

只是接受这样一个事实:游戏状态本质上是可变的。仅将不可变类用于概念上的值,例如位置,速度等。这可能仍会产生大量垃圾,但启用转义分析运行可能会有所帮助。