将玩家暂时转变为动物的角色扮演游戏中的设计考虑

7
我正在制作一个角色扮演游戏,以便练习设计模式。我希望玩家能够将自己变成不同的动物。例如,德鲁伊可能能够变成一只猎豹。目前我打算使用装饰者模式来实现这一点,但我的问题是:如何使得当德鲁伊处于猎豹形态时,他们只能访问猎豹的技能?换句话说,他们不应该能够访问普通德鲁伊的技能。

使用装饰者模式,即使在猎豹形态下,我的德鲁伊仍然可以访问他们的普通德鲁伊技能。
class Druid : Character
{
   // many cool druid skills and spells
   void LightHeal(Character target) { }
}

abstract class CharacterDecorator : Character
{
    Character DecoratedCharacter;
}

class CheetahForm : CharacterDecorator
{
    Character DecoratedCharacter;
    public CheetahForm(Character decoratedCharacter)
    {
       DecoratedCharacter= decoratedCharacter;
    }

    // many cool cheetah related skills
    void CheetahRun()
    {
       // let player move very fast
    }
}

现在使用这些类

Druid myDruid = new Druid();
myDruid.LightHeal(myDruid); // casting light heal here is fine
myDruid = new CheetahForm(myDruid);
myDruid.LightHeal(myDruid); // casting here should not be allowed

嗯……现在我想起来了,如果不将该类向下转换,我的德鲁伊是否将无法使用Druid类的法术/技能?但即使是这种情况,是否有更好的方法确保此时的myDruid被锁定在所有Druid相关的法术/技能之外,直到它被重新转换为Druid(因为目前它处于CheetahForm状态)?

7个回答

10
您不应该将法术和技能作为类的方法。它们应该是 Spell 和 Skill 对象的集合,可以通过 Character(或可能是 Creature)接口访问。然后,您的 CheetaForm 可以重写 getSkills 和 getSpells 方法,以返回适当技能的新修改后的集合。

我非常喜欢这个想法。我在大学时也为一个游戏做过类似的东西,效果很好。每当技能组发生变化时,用户界面就会动态地更新。 - Codesleuth
1
你不应该。 - Mark Withers

1

不要将Druid作为Character的派生类,考虑使用类型对象模式。首先,将角色类特定技能拆分为它们自己的对象:

abstract class Skill
{
    void Perform();
}

class LightHeal : Skill
{
    public void Perform()
    {
        // stuff...
    }
}

class CheetahRun : Skill
{
    public void Perform()
    {
        // stuff...
    }
}

// other skill classes...

现在,通过以下方式来摆脱派生字符类:

class CharacterType
{
    public readonly List<Skill> Skills = new List<Skill>();
}

class Character
{
    public IEnumerable<Skill> Skills { get { return mType.Skills; } }

    private CharacterType mType;
}

现在您可以将您的角色“类”创建为对象

CharacterType druid = new CharacterType();
druid.Skills.Add(new LightHeal());

CharacterType cheetah = new CharacterType();
cheetah.Skills.Add(new CheetahRun());

现在,通过将Character中的mTypedruid更改为cheetah,实际上可以更改其类,并交换可用的技能。

我真的开始喜欢这种方法!我可以做的另一件事是把 CharacterType 设为 List<mType>。然后,我甚至可以允许角色成为子类,以便他们可以保留所有的技能/法术。我将制作原型并观察其效果。 - mikedev
是的,完全正确。一旦您使用类型对象,就可以使用它们实现单个甚至多重继承。天空是极限。 - munificent
2
请尽快完成你的游戏编程设计模式书籍,这样我就可以购买一本了! :) - mikedev
好的,我对此采取了稍微不同的方法。我没有使用CharacterType,而是使用List<Character>来代替mType。然后,我将所有技能/法术放在装饰器中(就像我的CheetahForm装饰器一样)。现在,我可以装饰我的角色,赋予它们不同的技能/法术,而不仅仅是设置它们的mType。但我不确定是否喜欢这种方式...虽然这种方式对我来说很有意义,但感觉好像把它变得比必要的更复杂了。 - mikedev
通过类型对象,你可以通过简单的 if (mType != cheetah) 来实现相同的功能。 - munificent

1
也许你可以将德鲁伊的技能集放入修饰器中,而不是将其放入德鲁伊类中。
Druid myDruid = new Druid();
myDruid = new StandardDruid(myDruid);

忽略那个名字可怕的“StandardDruid”类,我希望你能看出我的意图 :)

这种安排有其优点,即所有基于技能的代码都将一致地包含在装饰器中,而不是一半在原始德鲁伊类中,另一半在装饰器中。如果你有另一个可以变形成德鲁伊的类,你可以轻松地做到这一点,而不需要任何代码重复。


我几乎以为这个会起作用!但对于我的情况来说,这是行不通的。例如,Druid 派生自 Character,所以在使用 CheetahForm 进行装饰之前,我可以将 Druid 向下转换为 Character。但如果我这样做,我将失去一些关于 Druid 的信息,例如角色当前记忆了哪些法术。 - mikedev

1

很抱歉告诉大家,但你们都错了,试图用锤子敲打不是钉子的东西 :-P

要解决你的问题mikedev,你需要超越面向对象编程(OOP),因为OOP无法正确解决这个问题。 看看实体系统/组件系统。

首先,你应该阅读的是http://www.devmaster.net/articles/oo-game-design/

然后,你的游戏设计之旅将开始。


0
我会通过将LightHeal方法更改为虚拟方法,然后在CheetahForm类中覆盖它,以抛出异常或向用户显示他们现在无法这样做的消息来解决这个问题:
class Druid : Character
{
   // many cool druid skills and spells
   virtual void LightHeal(Character target) { }
}

abstract class CharacterDecorator : Character
{
    Character DecoratedCharacter;
}

class CheetahForm : CharacterDecorator
{
    Character DecoratedCharacter;
    public CheetahDecorator(Character decoratedCharacter)
    {
       DecoratedCharacter= decoratedCharacter;
    }

    // many cool cheetah related skills
    void CheetahRun()
    {
       // let player move very fast
    }

    override void LightHeal(Character target)
    {
        ShowPlayerMessage("You cannot do that while shape shifted.");
    }
}

无论何时我玩涉及到这种情况的游戏(例如《魔兽世界》),用户界面都会更改以防止施放这些法术,但是用户仍然可以通过宏尝试施放这些法术,因此以这种方式处理有助于阻止这种情况发生。
我认为没有一种方法可以在不使用接口来描述CheetahForm的类的情况下“隐藏”该方法,因为将其引用到此处只会显示从接口中可用的方法。

0

这个怎么样?

interface ISkillState { ... }

interface ISkill { ... }

interface IForm
{
    Collection<ISkill> skills { get; set; }
    Dictionary<ISkill,ISkillState> skillStates { get; set; }
}

class Character
{
    private IForm actualForm;
    private Collection<IForm> currentForms;

    ...
}

例如:

class Spell : ISkill {}

class SpellState : ISkillState {}

0
我会在德鲁伊类中创建一个“形态”属性,并在施法时进行if检查,以确定德鲁伊是否变形。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接