我正在制作游戏。游戏由无限平面组成。单位必须位于离散的正方形上,因此可以使用简单的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语言中,我可能会从另一个继承Location
或Unit
,并使特定类型的Unit继承。
另一个考虑因素是这将通过线路发送大量这些对象,因此我需要将它们序列化为JSON以便在客户端上使用,并且不想编写大量的解析样板。
答案 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 上,而不是像通信格式那样的约束。