我如何devise大量不同的可以组合的攻击types?

我正在做一个自上而下的2D游戏,我想要有很多不同的攻击types。 我想让这些攻击非常灵活,并且可以结合Isaac的绑定工作方式。 这是游戏中所有collections品的列表 。 为了find一个很好的例子,让我们看看勺子弯管机项目。

勺子本德给艾萨克能够射击归巢的眼泪。

如果你看看“协同效应”部分,你会发现它可以和其他collections品结合起来,形成有趣而直观的效果。 例如,如果它与“内心之眼 ”相结合,它将“使艾萨克能够一次发射多个回归镜头”。 这是有道理的,因为内心的眼睛

给艾萨克三枪

devise这样的东西的好架构是什么? 这是一个暴力解决scheme:

if not spoon bender and not the inner eye then ... if spoon bender and not the inner eye then ... if not spoon bender and the inner eye then ... if spoon bender and the inner eye then ... 

但是这将会非常快速地失控。 什么是更好的方式来devise这样的系统?

你完全不需要手动编码组合。 你可以把重点放在每个项目给你的属性上。 例如, 项目A设置了“ Projectile=Fireball,Targetting=Homing项目B设置FireMode=ArcShot,Count=3ArcShot逻辑负责发出一个圆弧中的Projectile物料Count

这两个项目可以与任何其他自由修改这些(或其他)属性的项目相结合。 如果你添加了一种新型的弹丸,它会自动和ArcShot工作,如果你添加一个新的射击模式,它将自动使用Fireball射弹。 同样的, Targetting是一个属性,在FireMode创建投射物的同时,为投射物设置控制器,所以它们可以很容易地以任意组合的方式组合在一起。

你也可能设置属性依赖等。 例如, ArcShot要求你有一个Projectile的提供者(这可能只是默认的)。 你可以设置优先级,如果你有两个活动项目都提供了Projectile代码知道哪一个使用。 或者,您可以提供用户界面,让用户select使用哪种types的弹丸,或者只需要玩家取消他不想要的高优先级项目,或者使用最近的项目等。您可以进一步允许一个不兼容的系统例如,两个只修改Projectile物品都不能同时装备。

一般来说,如果可能的话,当涉及游戏中的对象等时,优先采用任何types的数据驱动方法(或声明式 )而不是程序性方法(大的if-else错误)。 可以通过简单数据configuration的更高级的通用逻辑比硬编码的特殊规则列表更可取。

如果你使用的是OOP语言,这听起来像是一个使用Decorator模式的好地方。 当你想修改攻击发生的方式时,只需使用适当的扩充来修饰它。

粗c ++示例:

 class AttackBehaviour { /* other code */ virtual void Attack(double angle); }; class TearAttack: public AttackBehaviour { /* other code */ void Attack(double angle); }; class TripleAttack: public AttackBehaviour { /* other code */ AttackBehaviour* baseAttackBehaviour; void Attack(double angle); }; void TripleAttack::Attack(angle) { baseAttackBehaviour->Attack(angle-30); baseAttackBehaviour->Attack(angle); baseAttackBehaviour->Attack(angle+30); } 

如果你有非常多的攻击,这个方法是最好的,你需要让它们的行为或多或less地相同。 如果你想大幅改变攻击发生的方式(例如带修饰符的新animation),那么这种方法不适合你。

作为艾萨克“绑定”的粉丝,我也想知道如何去做这样的事情。 游戏中的系统足够强大,这些突发行为是由效果组合而成的(我想到的是镜子,勺子弯曲器和一些范围助推器,导致围绕艾萨克的旋转,归位撕裂墙,磁电风格)。 它们的绝对数量会使“如果”块不切实际。

我的结论是,艾萨克和他的眼泪是一个庞大的组件entity framework中心的两个实体。 这些实体有一些基本的属性(移动速度,生命,距离,伤害,精灵等等),每个组件都带有一个属性修饰符和一个动词。

在代码中,Isaac和他的眼泪都会有一个包含界面事物的列表。 艾萨克将有一个订阅IsaacMutator接口的东西,他的眼泪tearMutator。 IsaacMutator将具有修改Isaac健康,速度,范围,外观和一些特殊动词的function。 TearMutator会相似。 每场比赛循环一次,艾萨克会遍历所有的艾萨克管理者,而且所有的活着的眼泪也会。 按照你的英文例子,它会看起来像:

 Isaac has IsaacMutators: --spoonbender which gives no stat change and: Tears are made homing --MeatEater which give +1 health, +1 damage and: nothing --MagicMirror which gives no stat change and: Tears are made reflecting Tears have tearMutators: --(depends on MeatEater) +1 damage and: nothing --(depends on MagicMirror) no stat change and: +1 vector towards isaac --(depnds on spoonbender) no stat change and: +1 vector towards enemytype 

等等。 因为这些types是叠加的,所以可以堆叠并添加到你的心中去。

我认为你的方式效果最好。 这些项目每个给出一个条件,如果一起使用它们会产生不同的条件,那么你将有效地需要所有3种可能的条件定义。

你也可以通过创建一个新types的定义,当两个项目都存在,但这实际上增加了卷积:

 if spoon bender and the inner eye then new spoon bender inner eye if spoon bender inner eye then ...