How do I design classes in my role playing game to allow multi-classing?
我正在编写一个游戏作为练习,我遇到了一个设计问题。我的角色扮演游戏将有典型的类,如战士,巫师,theif,牧师。我如何设计我的课程,让玩家可以多个班级?例如,一个玩家可能从一个战士开始(并获得战士所拥有的相关技能),然后从多个等级变为一个巫师(此时他们获得巫师法术),然后再变为多个等级,再变为一个流氓(现在获得流氓所拥有的所有能力)。所以这个玩家现在是一个战士巫师流氓。我不知道用C来代表这个。
起初我尝试使用decorator模式,但是我不能多次使用这个模式进行多类处理。关于如何设计这个有什么建议吗?
我唯一能想到的是为每个角色都有一个
1 2 3 4 5 | class CharacterBaseClass { public IList<CharacterBaseClass> MultiClasses { get; set; } // constructors, etc } |
每次他们都要上我的课
1 2 3 4 5 6 7 8 |
号
我相信一定有比这更好的方法吗?
仅仅因为你的角色是巫师和战士,这并不意味着你必须为他们创建子类。相反,问问自己,"在代码级别,角色的类做什么?"很可能,您根本不想为字符类提供C子类。相反,找出这个类实际上做了什么,然后确定在代码中建模的正确方法。
例如,如果字符类限制了字符可以使用的设备,则可以为
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 |
号
我个人可能会考虑将类附加到角色:
1 2 3 |
当然,如果你需要在类之间进行复杂的交互,那么不仅一个战士/小偷从每个类中获得奖金和技能,而且他也会得到更多的东西,也许唯一正确的途径可能是为所有的组合创建特定的多类:
。
当然,所有的组合都会爆炸。
纯表驱动的工作怎么样?
将所有适用的技能、咒语、奖励、效果等放在一个高难度的表格中,然后通过将特定的类别链接到表格中的特定项目来定义类别。这样,通过跨不同的基类链接来创建混合类就简单多了。
要使用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(); } } |