如何设计可以组合的多种不同攻击类型?


17

我正在制作一个自上而下的2D游戏,并且我想拥有很多不同的攻击类型。我想使攻击变得非常灵活和可组合,就像以撒的绑定的工作方式一样。这是游戏中所有收藏品的清单。为了找到一个好的例子,让我们看一下Spoon Bender项目。

汤匙弯机使艾萨克能够射出归巢的眼泪。

如果您查看“协同作用”部分,就会发现它可以与其他收藏品结合使用,从而产生有趣而直观的效果。例如,如果将其与“内眼 ” 结合使用,则“将使Isaac能够一次发射多幅归巢镜头”。这是有道理的,因为内眼

使艾萨克三杆

设计这样的东西的好架构是什么?这是一个蛮力解决方案:

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 ...

但这很快就会失去控制。设计这样的系统的更好方法是什么?


就我个人而言,我只是将所有配备的物品保留在一个列表中,并让它们实现某种通用接口,该接口将您所变异的对象从一个对象移到另一个对象。“针对每一项修改计划的攻击”,因此一项可以复制弹丸的数量,一项可以增加其色调并更改伤害(因此,如果一项使红色螺栓变成黄色,而另一项变为黄色,则在修改攻击后都会有橙色攻击) 。您也可能只有一个通用项目类,该类具有参数来决定其如何修改计划的攻击。
本杰明·丹格·约翰逊

2
我想指出的是,柯比64采用强力方法,并对所有可能的能力组合采用了硬编程的不同效果,因此这是可行的。
凯文(Kevin)

Answers:


16

您完全不需要手动编码组合。相反,您可以专注于每个项目为您提供的属性。例如,项目A设置Projectile=Fireball,Targetting=Homing项目BFireMode=ArcShot,Count=3。该ArcShot逻辑负责在弧中发出Count多个Projectile项目。

可以将这两项与可以自由修改这些(或其他)属性的任何其他项组合。如果您添加了新型的弹丸,它将自动与配合使用ArcShot;如果您添加了新的发射模式,它将自动与Fireball弹丸一起使用。同样,Targetting是在FireMode创建弹丸时设置弹丸的控制器的属性,因此可以根据需要轻松,轻松地将它们任意组合。

您可能还会设置属性依赖项等。例如,ArcShot要求您具有的提供程序Projectile(可能只是默认设置)。您可以设置优先级,以便如果您有两个同时提供Projectile代码的活动项目,则知道要使用哪个项目。或者,您可以提供UI,让用户选择要使用的弹丸类型,或者只是要求玩家取消装备他不希望使用的高优先级物品,或者使用最新的物品,等等。您还可以允许使用不兼容的系统,例如Projectile不能同时装备两个刚修改过的物品。

通常,在涉及游戏中的对象等时,尽可能使用任何一种数据驱动的方法(或声明方法),而不是过程方法(大的if-else混乱)。通过简单数据可配置的高级通用逻辑比特殊情况规则的硬编码列表更可取。


小巧的尼特,但您似乎没有所使用示例的正确属性。“ Spoon Bender”增加了归巢,“ Inner Eye”增加了三倍。两者都没有增加弧度,两者都是眼泪。如果您在示例中使用任意属性来抽象设计,那么如果没有以误导的方式命名它们,则将更易于阅读。我更喜欢“ Item A”和“ Item B”。
Daniel Kaplan 2013年

1
@tieTYT:我对名称进行了概括,并扩展了示例,使其除了射弹类型和射击模式外还包括目标模式。从来没有玩BoI超过几分钟,所以我没有像其他人那样内化。:)
肖恩·米德迪奇

在我看来,棘手的部分是确定属性的类别。例如:定位是一个,FireMode是另一个,Count可能是第三个...可能不是。即使这是一种武器,也许3枚弹丸可能是火球,而5枚可能是手榴弹。
丹尼尔·卡普兰

@tieTYT:绝对。当然,您可以进一步扩展系统以允许特殊组合或逻辑,但这应该是例外,而不是规则。针对常见情况(而非极端情况)进行优化。
肖恩·米德迪奇

这是一段很棒的视频,介绍了您为什么在过程编程方面的错误 youtu.be/QM1iUe6IofM上,而且您描述的数据驱动方式将任何if / else块映射到单个数据集,本质上无济于事,减少了您必须要处理的特殊情况程序,因为您必须对它们全部进行编程...
RenaissanceProgrammer

8

如果您使用的是OOP语言,这听起来像是使用OOP语言的好地方 Decorator Pattern。当您想要修改攻击的发生方式时,只需使用适当的扩充来装饰它即可。

原始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);
}

如果您有大量的攻击,并且您需要使它们全部以或多或少相同的方式运行,则此方法将是最佳方法。如果您想通过修改器(例如,带有修改器的新动画)来实质性地改变攻击发生的方式,那么该方法不适合您。


如果要更改动画,为什么不适合我?另外,如果我使用的是功能更强的语言怎么办?您知道适合他们的设计模式吗?
Daniel Kaplan 2013年

它更严格,因为修饰符将总是最终调用Attack它聚合的对象的方法。该TripleAttack班级不应该知道该TearAttack班级。如果这是真的,那将导致和else-if障碍一样多的头痛。这意味着任何撕裂动画都必须驻留在TearAttackBehaviour对象内部。该对象不(也不应该)知道它已被TripleAttack对象修饰。结果是这3个眼泪动画独立进行的,因此独立进行的。
NauticalMile 2013年

我很难用言语解释这一点,如果有人想刺中它,请成为我的客人。
NauticalMile 2013年

至于用一种功能更强的语言来实现它,我会考虑一会儿,并在准备好时修改答案。
NauticalMile

1

作为以撒绑定的粉丝,我也想知道如何去做这样的事情。游戏中的系统足够强大,可以从多种效果的组合中产生新的行为(我想到的是获得镜子,勺子弯曲器,并且一些范围增强器导致围绕Isaac的漩涡状导引式隔断墙,Magneto风格)。它们的绝对数量将使“ if”块不切实际。

我的结论是,以撒和他的眼泪是一个庞大的组件-实体框架中心的两个实体。实体具有一些基本的统计信息(移动速度,寿命,范围,损坏,精灵等),每个组成部分都会带来一个统计修饰符和动词。

在代码中,Isaac和他的眼泪每个都有一个包含接口内容的列表。Isaac将会拥有一列订阅IsaacMutator接口的事物的清单,以及他的眼泪tearMutator。IsaacMutator将具有修改Isaacs的运行状况,速度,范围,外观和某些特殊动词的功能。TearMutator将类似。每个游戏循环一次,Isaac就会遍历它拥有的所有IsaacMutators,并且所有活着的眼泪也会循环一次。以您的英语示例为例,它看起来像:

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

等等。因为类型是可加性的,所以您可以堆叠并添加和删除内容。


-5

我认为您的方法效果最好。这些项目各自提供一个条件,如果一起使用它们会产生不同的条件,那么您实际上需要定义所有3种可能的条件。

当两个项目都存在时,您也可以通过创建一种新的定义来解决这个问题,但这实际上增加了卷积:

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

if spoon bender inner eye then ...
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.