开发者

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

开发者 https://www.devze.com 2022-12-18 20:24 出处:网络
I am programming a game as an exercise and I\'ve run into a design problem. My role playing game will have the typical classes like Fighter, Wizard, Theif, Cleric. How do I design my classes so that p

I am programming a game as an exercise and I've run into a design problem. My role playing game will have the typical classes like Fighter, Wizard, Theif, Cleric. How do I design my classes so that players can multi-class? For example, one player might start off as a Fighter (and gain the related skills fighters have), then multi-class to a Wizard (at that point they gain wizard spells), and later on multi-class yet again to a rogue (now gaining all abilities rogues have). So this player is now a Fighter-Wizard-Rogue. I don't know to represent this in C#.

At first I tried to use the decorator pattern but I'm unable to multi-class multiple times with this. Any pointers on how to de开发者_开发知识库sign this?

Only thing I can think of is having an IList<CharacterBaseClass> property for each character and adding Fighter, Wizard, Rogue, etc to this as the player multi-classes. So something like this..

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

and each time they mutli-class I add to the 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());    

I'm sure there must be a better way than this?


Just because your characters are wizards and warriors, that doesn't mean you have to create subclasses for them. Instead, ask yourself, "At the code level, what does a character's class do?" Likely, you won't want to have C# subclasses for character classes at all. Instead, figure out what the class actually does, and then determine the right way to model that in code.

For example, if character class restricts the equiment a character can use, then you can define a class for 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; }
}

Don't feel you need to use subclasses to model every "is-a" relationship in your game.

Another option is to use the Type Object pattern to model your character classes. If you do that, it'd be easy to give each character a set of those Type Object instances instead of a single one, giving you, in effect, multiple inheritance.


With the decorator pattern, you could possibly do it.

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

Personally I would probably look at attaching classes to the character instead:

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

Of course, if you need complex interactions between the classes, so that not only does a Fighter/Thief gets bonuses and skills from each, but he gets something more as well, perhaps the only correct route for that might be to create specific multi-classes for all the combinations:

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

This would of course just explode with all the combinations.

What about a pure table-driven effort?

Place all applicable skills, spells, bonuses, effects, etc. in a hunking big table, then define the classes by linking a specific class to the specific items in that table. This way it would be much simpler to create hybrid classes by linking across different base classes.

To use a decorator pattern and still get proper access to everything, each class (in the programming sense of the word) needs to be implemented properly as a decorator class.

For instance:

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


If the classes have some common interface or base class, then multiclass is additional class (MultiClass) which also implements this interface or base class, then delegates to its contained instances.

For example:

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

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

    ...
}

If you want to add more classes, you can add AddClass method to the base Class, which would create MultiClass from single class, or recreate multiclass with one more contained class for MultiClass.


Not everyone's cup of tea, but you could use state pattern.

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


I think your characters should be able to have multiple Facet/Role implementing "Archetypes". Then each one having multiple skills or attributes. Let's say...

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

If you get the Idea, then you could store properties/data and call skills/tasks with some degree of indirection and flexibility. Good luck!


You may want to consider composition.

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


Néstor Sánchez A. provides you with a good solution. Drop your OOP thinking for a while and read this:

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

Not every problem can be solved with plain OOP in an elegant way.

0

精彩评论

暂无评论...
验证码 换一张
取 消