如何在角色扮演游戏中设计类以允许多类化?

时间:2010-01-31 08:50:47

标签: c# design-patterns decorator

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

起初我尝试使用装饰器模式但是我不能多次多次使用它。关于如何设计这个的任何指针?

我唯一能想到的就是为每个角色拥有IList<CharacterBaseClass>属性,并将Fighter,Wizard,Rogue等添加到玩家多类中。这样的事情......

class CharacterBaseClass
{
   public IList<CharacterBaseClass> MultiClasses { get; set; }
   // constructors, etc
}

每次他们多次加入IList

// 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());    

我确定必须有比这更好的方法吗?

7 个答案:

答案 0 :(得分:7)

仅仅因为你的角色巫师和战士,这并不意味着你必须为他们创建子类。相反,问问自己,“在代码级别,角色的课程做什么?”可能,你根本不想要为字符类设置C#子类。相反,弄清楚类实际做了什么,然后确定在代码中对其进行建模的正确方法。

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

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”关系。

另一个选择是使用Type Object模式为角色类建模。如果你这样做,那么很容易为每个角色提供一组Type Object实例而不是单个实例,这实际上为你提供了多重继承。

答案 1 :(得分: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

就个人而言,我可能会考虑将字符附加到字符中:

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

当然,如果你需要在课程之间进行复杂的互动,那么战斗机/小偷不仅可以从每个人那里获得奖金和技能,而且还可以获得更多的东西,也许唯一正确的途径就是创造所有组合的特定多类:

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

这当然会爆炸所有组合。

纯表驱动的努力怎么样?

将所有适用的技能,法术,奖励,效果等放在一张大表中,然后通过将特定类链接到该表中的特定项来定义类。这样,通过链接不同的基类来创建混合类会更加简单。

要使用装饰器模式并仍然可以正确访问所有内容,每个类(在编程意义上的单词)需要作为装饰器类正确实现。

例如:

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;
    }
}

答案 2 :(得分:1)

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

例如:

public class MultiClass : Class {
    ...
    public MultiClass(params Class[] classes) {
        this.classes = classes;
    }

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

    ...
}

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:1)

我认为你的角色应该能够有多个Facet / Role实现“Archetypes”。 然后每个人都有多种技能或属性。让我们说......

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();
    }
}

如果您获得了Idea,那么您可以存储属性/数据并通过某种程度的间接性和灵活性调用技能/任务。 祝你好运!

答案 5 :(得分:0)

您可能需要考虑构图。

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();
    }
}

答案 6 :(得分:0)

NéstorSánchezA。为您提供了一个很好的解决方案。放弃你的OOP思考一段时间并阅读:

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

并非所有问题都可以通过简单的OOP以优雅的方式解决。