如何在Haskell中建模2D世界

Sea*_*ess 5 haskell

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

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

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

data Unit = Unit { x :: Int, y :: Int, type :: String, ... many shared properties... }
Run Code Online (Sandbox Code Playgroud)

我也考虑过拥有一个位置类型

data Location = Location { x :: Int, y :: Int, unit :: Unit } 
-- or this
data Location = Location { x :: Int, y :: Int }
data Unit = Unit { unitFields... , location :: Location }
Run Code Online (Sandbox Code Playgroud)

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

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

ehi*_*ird 7

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

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

至于具体的Unitgo 类型,我至少会建议将公共字段分解为数据类型:

data UnitInfo = UnitInfo { ... }

data BlockType = Grass | Wood | ...

data Unit
  = NPC UnitInfo AgentID
  | Player UnitInfo PlayerID
  | Block UnitInfo BlockType
Run Code Online (Sandbox Code Playgroud)

或类似的.

通常,将常见因素分解为他们自己的数据类型,并尽可能保持数据简单和"孤立"(即将"此单位位于什么位置?"之类的东西转换为将两者关联起来的单独结构,以便个别数据 -类型是"永恒的",可重用和抽象尽可能的).

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

您的JSON要求使事情变得复杂,但是这个FAQ条目显示了一个很好的例子,说明如何在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
Run Code Online (Sandbox Code Playgroud)

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

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