如何在Haskell中建模2D世界

时间:2012-01-18 01:33:49

标签: haskell

我正在制作游戏。游戏由无限平面组成。单位必须位于离散的正方形上,因此可以使用简单的Location { x :: Int, y :: Int }

进行定位

Unit可能有很多种。有些可能是生物,有些只是物体,比如一块石头或木头(想想那里的2d minecraft)。许多人将是空的(只是草或其他)。

你如何在Haskell中建模?我考虑过做下面的事情,但是对象和生物呢?他们可能有不同的领域?在Unit上将它们全部归一化?

data Unit = Unit { x :: Int, y :: Int, type :: String, ... many shared properties... }

我还考虑过拥有位置类型

data Location = Location { x :: Int, y :: Int, unit :: Unit } 
-- or this
data Location = Location { x :: Int, y :: Int }
data Unit = Unit { unitFields... , location :: Location }

你有什么想法吗?在OO语言中,我可能会从另一个继承LocationUnit,并使特定类型的Unit继承。

另一个考虑因素是这将通过线路发送大量这些对象,因此我需要将它们序列化为JSON以便在客户端上使用,并且不想编写大量的解析样板。

1 个答案:

答案 0 :(得分:7)

Location只是一个简单的二维Point类型。

我建议不要将Unit绑在他们所在的位置;只需使用Map Location Unit来处理网格上的位置之间的地图以及那里有什么(如果有的话)。

至于Unit的特定类型,我至少会建议将公共字段分解为数据类型:

data UnitInfo = UnitInfo { ... }

data BlockType = Grass | Wood | ...

data Unit
  = NPC UnitInfo AgentID
  | Player UnitInfo PlayerID
  | Block UnitInfo BlockType

或类似。

一般来说,将常见因素纳入自己的数据类型,并尽可能保持数据简单和“孤立”(即将“此单位位于何处?”之类的内容移动到单独的结构中关联这两个,以便各个数据类型尽可能“永恒”,可重用和抽象。

String的“类型”有一个Unit是Haskell中强大的反模式;它通常表示您正在尝试使用数据类型实现动态类型或OOP结构,这是不合适的。

您的JSON要求使事情变得复杂,但this FAQ entry显示了一个很好的示例,说明如何在Haskell中使用函数和数据,在没有String类型或花哨类型类黑客的情况下以惯用方式实现这种通用性 - 类型作为抽象的主要单位。当然,前者会给你带来麻烦;很难将函数序列化为JSON。但是,您可以从ADT维护一个代表生物或块等“类型”的地图及其实际实施:

-- records containing functions to describe arbitrary behaviour; see FAQ entry
data BlockOps = BlockOps { ... }
data CreatureOps = CreatureOps { ... }
data Block = Block { ... }
data Creature = Creature { ... }
data Unit = BlockUnit Block | CreatureUnit Creature
newtype GameField = GameField (Map Point Unit)

-- these types are sent over the network, and mapped *back* to the "rich" but
-- non-transferable structures describing their behaviour; of course, this means
-- that BlockOps and CreatureOps must contain a BlockType/CreatureType to map
-- them back to this representation
data BlockType = Grass | Wood | ...
data CreatureType = ...
blockTypes :: Map BlockType BlockOps
creatureTypes :: Map CreatureType CreatureOps

这使您可以拥有典型OOP结构的所有可扩展性和不重复性,同时保持功能简单性并允许简单的网络传输游戏状态。

一般来说,你应该避免考虑继承和其他OOP概念;相反,尝试从简单结构的功能和组成方面考虑动态行为。函数是函数式编程中最强大的工具,因此是名称,可以表示任何复杂的行为模式。最好不要让像网络游戏这样的要求影响你的基本设计;就像上面的例子一样,几乎总是可以将这些东西分层在为表现力和简单性而构建的设计的 top 上,而不是像通信格式那样的约束。