关于c#:如何在角色扮演游戏中设计类以允许多类化?

How do I design classes in my role playing game to allow multi-classing?

我正在编写一个游戏作为练习,我遇到了一个设计问题。我的角色扮演游戏将有典型的类,如战士,巫师,theif,牧师。我如何设计我的课程,让玩家可以多个班级?例如,一个玩家可能从一个战士开始(并获得战士所拥有的相关技能),然后从多个等级变为一个巫师(此时他们获得巫师法术),然后再变为多个等级,再变为一个流氓(现在获得流氓所拥有的所有能力)。所以这个玩家现在是一个战士巫师流氓。我不知道用C来代表这个。

起初我尝试使用decorator模式,但是我不能多次使用这个模式进行多类处理。关于如何设计这个有什么建议吗?

我唯一能想到的是为每个角色都有一个IList属性,并将战士、巫师、流氓等添加到这个角色中,作为多类玩家。就像这样……

1
2
3
4
5
class CharacterBaseClass
{
   public IList<CharacterBaseClass> MultiClasses { get; set; }
   // constructors, etc
}

每次他们都要上我的课

1
2
3
4
5
6
7
8
// player starts off as Fighter
Warrior player1 = new Warrior();

// now multi-class to Wizard
player1.MultiClasses.Add(new Wizard());

// now multi-class to Theif
player1.MultiClasses.Add(new Theif());

我相信一定有比这更好的方法吗?


仅仅因为你的角色是巫师和战士,这并不意味着你必须为他们创建子类。相反,问问自己,"在代码级别,角色的类做什么?"很可能,您根本不想为字符类提供C子类。相反,找出这个类实际上做了什么,然后确定在代码中建模的正确方法。

例如,如果字符类限制了字符可以使用的设备,则可以为AllowedEquipment定义一个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class AllowedEquipment
{
    public static AllowedEquiment Warrior()
    {
        return new AllowedEquipment() {
            Daggers = true;
            Swords = true;
            Shields = true;
            Armor = true
        };
    }

    public static AllowedEquiment Wizard()
    {
        return new AllowedEquipment() {
            Daggers = true;
            Swords = false;
            Shields = false;
            Armor = true
        };
    }

    public bool Daggers { get; set; }
    public bool Swords { get; set; }
    public bool Shields { get; set; }
    public bool Armor { get; set; }
}

不要觉得你需要使用子类来为游戏中的每一个"IS-A"关系建模。

另一个选项是使用类型对象模式来建模角色类。如果这样做,就很容易给每个字符一组这些类型的对象实例,而不是一个单一的实例,实际上,给您多个继承。


有了装饰图案,你就可以做到。

1
2
3
Character person = new Character("Rambo");
person = new Fighter(person); // decorate him with Fighter skills
person = new Thief(person);   // also decorate him with Thief skills

我个人可能会考虑将类附加到角色:

1
2
3
Character person = new Character("Rambo");
person.AttachClass(new Fighter());
person.AttachClass(new Thief());

当然,如果你需要在类之间进行复杂的交互,那么不仅一个战士/小偷从每个类中获得奖金和技能,而且他也会得到更多的东西,也许唯一正确的途径可能是为所有的组合创建特定的多类:

1
2
Character person = new Character("Rambo");
person.AttachClass(new FighterThief());

当然,所有的组合都会爆炸。

纯表驱动的工作怎么样?

将所有适用的技能、咒语、奖励、效果等放在一个高难度的表格中,然后通过将特定的类别链接到表格中的特定项目来定义类别。这样,通过跨不同的基类链接来创建混合类就简单多了。

要使用decorator模式并仍能正确访问所有内容,每个类(从编程的意义上说)都需要作为decorator类正确地实现。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class BaseClass
{
    protected BaseClass(BaseClass underlyingCharacterClass);
    public abstract bool CanCastSpells();
    public abstract List<Spell> GetAvailableSpells();
    protected BaseClass UnderlyingCharacterClass;
}

public class Wizard : BaseClass
{
    public override bool CanCastSpells() { return true; }
    public override List<Spell> GetAvailableSpells()
    {
        List<Spell> result = new List<Spell>();
        if (UnderlyingCharacterClass != null)
            result.AddRange(UnderlyingCharacterClass.GetAvailableSpells());
        result.Add(new WizardSpell1());
        ...
        return result;
    }
}

public class Thief : BaseClass
{
    public override bool CanCastSpells()
    {
        if (UnderlyingCharacterClass != null)
            return UnderlyingCharacterClass.CanCastSpells();
        return false;
    }
    public override List<Spell> GetAvailableSpells()
    {
        List<Spell> result = new List<Spell>();
        if (UnderlyingCharacterClass != null)
            result.AddRange(UnderlyingCharacterClass.GetAvailableSpells());
        return result;
    }
}


我认为你的角色应该能够有多个方面/角色实现"原型"。然后每个人都有多种技能或属性。假设…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Archetype
{
    string Name;
    Dictionary<string,Type> Properties;
    Dictionary<string,Action> Skills;
}

class Character
{
    string Name;
    string Alias;

    Dictionary<Archetype,Dictionary<string,object>> FacetData;
}

class TheGame
{
    public static void Main()
    {
        var Pilot = new Archetype();
        Pilot.Name ="Combat-Pilot";
        Pilot.Properties.Add("FlightHours", typeof(int));
        Pilot.Properties.Add("AmbientTypes", typeof(List<string>));

        var Jedi = new Archetype();
        Jedi.Name ="Jedi";
        Jedi.Properties.Add("ForceLevel", typeof(int));
        Jedi.Properties.Add("Title", typeof(string));
        Jedi.Properties.Add("IsCombatVeteran", typeof(bool));
        Jedi.Skills.Add("LightSaberFight", FightWithLightSaber());

        var Anakin = new Character();
        Anakin.Id = 100;
        Anakin.Name ="Anakin Skywalker";
        Anakin.Alias ="Darth Vader";

        Anakin.FacetData.Add(Pilot, new Dictionary<string, object>()
            { {"FlightHours", 2500 },
              {"AmbientTypes", new List<string>() {"Atmospheric","Space","Hyper-Space"} } };

        Anakin.FacetData.Add(Jedi, new Dictionary<string, object>()
            { {"ForceLevel", 7 },
              {"Title","Padawan" },
              {"IsCombatVeteran", true } };

        Anakin.ApplySkill(Jedi,"LightSaberFight", Target);
    }

    public static void FightWithLightSaber(Character Target)
    {
        ShowBrightLightSaberPointingTo(Target);
        EmitCoolSound();
    }
}

如果你有了这个想法,那么你就可以存储属性/数据,并以某种程度的间接性和灵活性调用技能/任务。祝你好运!


不是每个人都喝的茶,但是你可以使用状态模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public interface Player
{
   void Fight();
   void CastSpell();
   void DoRoguishThings();
}

public class PlayerImpl : Player
{
   Player fighter;
   Player wizard;
   Player rogue;

   Player current;

   public void Fight(){ current.Fight(); }
   public void CastSpell(){ current.CastSpell(); }
   public void DoRoguishThings(){ current.DoRoguishThings; }

   public void MakeWizard(){ current = wizard; }
   public void GoRogue(){ current = rogue; }
}
public class Fighter : Player
{
   public void Fight(){ // do fighting }

   public void CastSpell()
   {
      Console.WriteLine("You can't cast a spell, you are but a mere pugilist.");
   }

   ...
}

public class Wizard : Player
{
   public void Fight(){ // do wizardly fighting }
   public void CastSpell() { // do spell-casting }
   public void DoRoguishThings() { // whatever }
}


如果类有一些公共接口或基类,那么多类是附加类(多类),它也实现了这个接口或基类,然后委托给它包含的实例。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
public class MultiClass : Class {
    ...
    public MultiClass(params Class[] classes) {
        this.classes = classes;
    }

    public IEnumerable<Ability> GetAbilities() {
        return this.classes.SelectMany(с => c.GetAbilities());
    }

    ...
}

如果要添加更多的类,可以将AddClass方法添加到基类中,这将从单个类创建多个类,或者使用多个包含类为多个类重新创建多个类。


N_stor S_nchez A.为您提供了一个很好的解决方案。暂时放下你的OOP思考,阅读以下内容:

http://www.devmaster.net/articles/oo-game-design/

并不是所有的问题都可以优雅地用普通的OOP来解决。


你可能想考虑构图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
interface IWarrior
{
    void Slash();    
}

interface IMage
{
    void Cast();
}

class Warrior : IWarrior
{
    public void Slash() { }
}

class Mage : IMage
{
    public void Cast() { }
}

class WarriorMage : IWarrior, IMage
{
    private readonly Warrior _Warrior;
    private readonly Mage _Mage;

    public void Slash()
    {
        _Warrior.Slash();
    }

    public void Cast()
    {
        _Mage.Cast();
    }
}