标题故意是双曲线的,可能只是我对模式的经验不足,但这是我的理由:
实现实体的“通常”或可以说是直接的方法是通过将它们实现为对象并将其子类化为常规行为。这导致了经典的问题“是或EvilTree
的子类”。如果我们允许多重继承,就会出现钻石问题。我们可以代替拉的组合功能,并进一步向上层次导致神类,或者我们可以故意留出的行为在我们和类(使他们在极端情况下接口),以便可以实现本身-这会导致如果有一个代码复制。Tree
Enemy
Tree
Enemy
Tree
Entity
EvilTree
SomewhatEvilTree
实体组件系统试图通过将解决这一问题Tree
和Enemy
对象到不同的组件-比如Position
,Health
和AI
-和执行系统,例如AISystem
,根据AI决定改变一个Entitiy的位置。到目前为止,EvilTree
还算不错,但是如果可以启动电源并造成损害该怎么办?首先,我们需要一个CollisionSystem
和一个DamageSystem
(我们可能已经有了这些)。CollisionSystem
与进行通信的需求DamageSystem
:每次两件事冲突时,CollisionSystem
都会向发送一条消息,DamageSystem
以便减少健康状况。损坏也会受到通电的影响,因此我们需要将其存储在某个地方。我们是否创建一个PowerupComponent
附加到实体的新商品?但是然后DamageSystem
需要知道一些它宁愿一无所知的东西-毕竟,有些东西会造成无法获得能量的伤害(例如Spike
)。我们是否允许PowerupSystem
修改StatComponent
与该答案类似的用于损伤计算的?但是现在两个系统访问相同的数据。随着我们的游戏变得越来越复杂,它将变成无形的依赖图,其中组件在许多系统之间共享。在这一点上,我们可以使用全局静态变量并摆脱所有样板。
有解决此问题的有效方法吗?我曾经有过一个想法,就是让组件具有某些功能,例如,给StatComponent
attack()
它默认情况下只返回一个整数,但是可以在上电时进行组合:
attack = getAttack compose powerupBy(20) compose powerdownBy(40)
这不能解决attack
必须保存在多个系统访问的组件中的问题,但是如果我有足够的语言来支持它,至少我可以正确键入这些函数:
// In StatComponent
type Strength = PrePowerup | PostPowerup
type Damage = Int
type PrePowerup = Int
type PostPowerup = Int
attack: Strength = getAttack //default value, can be changed by systems
getAttack: PrePowerup
// these functions can be defined in other components or in PowerupSystems
powerupBy: Strength -> PostPowerup
powerdownBy: Strength -> PostPowerup
subtractArmor: Strength -> Damage
// in DamageSystem
dealDamage: Damage -> () = attack compose subtractArmor compose hurtSomeEntity
这样,我至少可以保证系统添加的各种功能的正确排序。无论哪种方式,似乎我都在这里快速地进行函数式反应式编程,因此我要问自己是否从一开始就不应该使用它(我只是研究FRP,所以在这里我可能是错的)。我发现ECS是对复杂类层次结构的改进,但我不认为这是理想的选择。
有没有解决的办法?是否缺少我想更清楚地将ECS分离的功能/模式?FRP是否严格更适合于此问题?这些问题是由于我要编程的内在复杂性引起的吗?即玻璃钢会有类似的问题吗?