Как мне создать классы в моей ролевой игре, чтобы разрешить мультиклассирование? - PullRequest
2 голосов
/ 31 января 2010

Я программирую игру как упражнение и столкнулся с проблемой дизайна. В моей ролевой игре будут типичные классы, такие как Fighter, Wizard, Theif, Cleric. Как мне спроектировать свои классы, чтобы игроки могли мультиклассировать? Например, один игрок может начать с бойца (и получить соответствующие навыки у бойцов), затем мультиклассировать с волшебником (в этот момент они получают заклинания волшебника), а затем в мультиклассе и снова разбойником ( теперь набираются все способности мошенников). Итак, этот игрок теперь Fighter-Wizard-Rogue. Я не знаю, чтобы представить это в 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 ]

7 голосов
/ 05 февраля 2010

Если ваши персонажи являются волшебниками и воинами, это не значит, что вы должны создавать для них подклассы. Вместо этого спросите себя: «На уровне кода, что делает класс персонажа?» Вероятно, вы вообще не захотите иметь подклассы 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; }
}

Не думайте, что вам нужно использовать подклассы, чтобы смоделировать все отношения "есть" в вашей игре.

Другой вариант - использовать шаблон Type Object для моделирования классов ваших персонажей. Если вы сделаете это, было бы легко дать каждому персонажу набор этих экземпляров Type Object вместо одного, что фактически дает вам множественное наследование.

3 голосов
/ 31 января 2010

С шаблоном декоратора вы могли бы возможно сделать это.

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;
    }
}
1 голос
/ 31 января 2010

Я думаю, что ваши персонажи должны иметь несколько аспектов / ролей, реализующих «архетипы». Затем каждый из них имеет несколько навыков или атрибутов. Допустим ...

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 голос
/ 31 января 2010

Не у каждого чашка чая, но вы можете использовать шаблон состояния.

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 голос
/ 31 января 2010

Если у классов есть некоторый общий интерфейс или базовый класс, то мультикласс - это дополнительный класс (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.

0 голосов
/ 21 апреля 2010

Néstor Sánchez A. предлагает вам хорошее решение. Оставьте на некоторое время ваши мысли о ООП и прочитайте это:

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

Не все проблемы можно решить с помощью простого ООП элегантным способом.

0 голосов
/ 31 января 2010

Возможно, вы захотите рассмотреть композицию.

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