问题 为角色扮演游戏中的“角色”设计一个干净/灵活的方式来施放不同的法术
我正在创建一个角色扮演游戏,以获得乐趣和学习体验。我正处于我的角色(巫师)施放法术的地步。我正在使用战略模式来设置他们在施放法术之前施放的法术。我采用这种方法的原因是因为我希望以后能够添加不同的法术类型,而不必使用字符/向导类。
我的问题 - 这是一个糟糕的设计吗?这有更好/更清洁/更容易的方法吗?
我试图远离那个试图让一切都融入设计模式的“那个人”。但在这种情况下,我觉得这是一个不错的选择。
这是我的代码到目前为止使用2个法术的样子
public class Wizard : Creature
{
public List<Spell> Spells { get; set; }
public void Cast(Spell spell, Creature targetCreature)
{
spell.Cast(this, targetCreature);
}
}
public abstract class Spell
{
public string Name { get; set; }
public int ManaCost { get; set; }
public Spell(string name, int manaCost)
{
Name = name;
ManaCost = manaCost;
}
public void Cast(Creature caster, Creature targetCreature)
{
caster.SubtractMana(ManaCost);
ApplySpell(caster, targetCreature);
}
public abstract void ApplySpell(Creature caster, Creature targetCreature);
}
// increases the target's armor by 4
public class MageArmor : Spell
{
public MageArmor() : base("Mage Armor", 4);
public override void ApplySpell(caster, targetCreature)
{
targetCreature.AddAC(4);
}
}
// target takes 7 damage
public class FireBall : Spell
{
public FireBall() : base("Fire Ball", 5);
public override void ApplySpell(caster, targetCreature)
{
targetCreature.SubtractHealth(7);
}
}
现在施放一个咒语,我们做这样的事情:
Wizard wizard = new Wizard();
wizard.Cast(new Spell.MageArmor(), wizard); // i am buffing myself
更新:更新的代码与以下答案中的一些建议
5565
2018-02-04 07:15
起源
答案:
按照Willcodejavaforfood的说法,你可以设计一个 SpellEffect
描述你的咒语可能具有的单一效果的类。您可以创建一个“词汇表”来用来描述:
法术属性:
- 名称
- 法力消耗
- 目标限制 整个 法术(玩家,全天候,怪物......)
- 法术总持续时间(法术效果持续时间最高)(10秒,5个刻度,......)
- 施法时间
- 法术范围(5米,65个单位......)
- 失败率(5%,90%)
- 在此法术可以再次施放之前等待的时间(重建时间)
- 在任何法术可以再次施放之前等待的时间(恢复时间)
- 等等...
SpellEffect的属性:
- 效果类型(防守,进攻,buff,debuff ......)
- 目标 影响 (自我,党,目标,目标周围区域,线到目标,......)
- 属性或属性作用于(马力,法力,最大马力,力量,攻击速度......)
- 效果改变了多少(+ 10,-500,5%,...)
- 效果持续多长时间(10秒,5个滴答,......)
- 等等
我想你的词汇(上面括号中的单词)将在一组枚举中定义。也许建议创建一个类层次结构来表示SpellEffect类型,而不是使用该特定属性的枚举,因为那里 威力 是一个不需要所有这些属性的SpellEffect类型,或者对于我没有想到的每个基本SpellEffect类型,都有某种自定义逻辑。但这也可能使事情复杂化太多。 KISS原则=)。
无论如何,关键是你要将关于法术效果的特定信息提取到一个单独的数据结构中。这样做的好处就是你可以创造1 Spell
class并使其保持一个SpellEffects列表,以便在激活时应用。然后该法术可以一次执行多种功能(伤害敌人和治疗玩家,又名生命攻击)。您为每个法术创建一个新的法术实例。当然,在某些时候你 将 必须要 创建 法术。您可以轻松地将拼写编辑器实用程序放在一起以使其更容易。
此外,您定义的每个SpellEffect都可以 非常 使用System.Xml.Serialization的XmlSerializer类可以轻松地从XML写入和加载。在SpellEffect这样的简单数据类上使用它是一件轻而易举的事。您甚至可以将最终的拼写列表序列化为xml。例如:
<?xml header-blah-blah?>
<Spells>
<Spell Name="Light Healing" Restriction="Player" Cost="100" Duration="0s"
CastTime="2s" Range="0" FailRate="5%" Recast="10s" Recovery="5s">
<SpellEffect Type="Heal" Target="Self" Stat="Hp" Degree="500" Duration="0s"/>
</Spell>
<Spell Name="Steal Haste" Restriction="NPC" Cost="500" Duration="120s"
CastTime="10s" Range="100" FailRate="10%" Recast="15s" Recovery="8s">
<SpellEffect Type="Buff" Target="Self" Stat="AttackSpeed" Degree="20%" Duration="120s"/>
<SpellEffect Type="Debuff" Target="Target" Stat="AttackSpeed" Degree="-20%" Duration="60s"/>
</Spell>
...
</Spells>
您还可以选择将数据放在数据库中而不是xml中。 Sqlite会小巧,快速,简单且免费。您还可以使用LINQ从xml或sqlite查询拼写数据。
当然,你也可以为你的怪物做类似的事情 - 至少对于他们的数据而言。我不确定逻辑部分。
如果你使用这种系统,你可以获得额外的好处,即能够将你的生物/法术系统用于其他游戏。如果你“硬编码”你的法术,你就不能这样做。它还可以让你改变法术(类平衡,错误,等等) 无 必须重建和重新分发您的游戏可执行文件。只是一个简单的xml文件。
天啊!我现在对你的项目感到非常兴奋,以及我所描述的内容是如何实现的。如果您需要任何帮助,请告诉我!
6
2018-02-04 11:18
特别清楚为什么你希望它是一个两阶段的过程,除非它将在UI中公开(即如果用户将设置“加载的咒语”并且可以在以后改变他们的想法)。
另外,如果你 是 将拥有一个属性而不仅仅是wizard.Cast(新的 Spell.MageArmor(), wizard)
有一个SetSpell方法有点奇怪 - 为什么不只是做 LoadedSpell
财产公众?
最后,法术实际上有任何可变状态吗?你能拥有一组固定的实例(flyweight / enum模式)吗?我不是在考虑这里的内存使用(这是flyweight模式的正常原因),而只是它的概念性。感觉就像你想要的东西真的就像一个Java枚举 - 一组具有自定义行为的值。在C#中更难做到这一点,因为没有直接的语言支持,但它仍然是可能的。
法术中的实际模式(有施法者和目标)似乎是合理的,但是如果你想要能够拥有区域效果法术(使用目标位置而不是特定生物)或诅咒法术,你会发现它变得不灵活/祝福物品等。您可能还需要传递游戏世界的其他状态 - 例如如果你有一个咒语来创建小兵。
3
2018-02-04 07:21
我可能不会在这里为每个法术使用子类。我会尝试使用XML或JSON将其放在磁盘上并动态创建它们。
- 想澄清(希望) -
这种方法需要尽可能提前计划。您必须将属性定义为:
- 名称
- 描述
- 持续时间
- 目标(自我,区域,其他)
- 类型(奖金,伤害,诅咒)
- 效果(例如:1d6冰霜伤害,+ 2护甲等级,-5伤害抗性)
在通用法术类中包含所有这些行为应该使它非常灵活,更直接地进行测试。
2
2018-02-04 08:09
用命令模式封装“法术”是很自然的(这基本上就是你所做的)。但是你遇到两个问题: -
1)你必须重新编译才能添加更多法术
你可以列举所有可能的
行动有可能是一个咒语
拿,然后定义一些法术
外部格式(XML,Database)
加载到您的应用程序中
启动。西方角色扮演游戏往往被编码为
这 - “咒语”包括“申请”
法术效果#1234,参数1000“,
“播放动画#2345”等
您可以将您的游戏状态暴露给脚本
语言和脚本你的法术(你也可以
将此与第一个想法相结合,以便在大多数情况下
你的脚本法术只是在代码中调用预定义的效果。 Planeswalkers的决斗
(X-Box 360上的M:TG游戏)写得很广泛
这种方法
或者你可以忍受它(我...)
2)当你的法术目标不是生物时会发生什么?
我通常做类似下面的事情(不仅仅是在游戏中,我一直使用这种模式来表示mutli-agent-systems中的行为): -
public interface IEffect<TContext>
{
public void Apply(TContext context);
}
public class SingleTargetContext
{
public Creature Target { get; set; }
}
public class AoEContext
{
public Point Target { get; set; }
}
// etc.
这种模式的优势在于它可以非常灵活地执行那些你经常期望法术能够完成的更多固定模型无法做到的“奇怪”事情。你可以把它们连在一起做。你可以有一个效果,它可以为你的目标添加一个TriggeredEffect - 这对于做一些像Thorns Aura这样的事情很有帮助。您可以使用IReversibleEffect(使用额外的Unapply方法)来表示增益效果。
关于Planeswalkers决斗的那篇文章虽然非常出色。太好了,我会连接两次!
2
2018-02-04 11:47
出于某种原因,“咒语”对我来说更像是一种命令模式。但我从来没有设计过这样的游戏......
1
2018-02-04 07:20
我看到这个模式的最大问题是所有法术必须记住减去他们的法术力费用。怎么样:
public abstract class Spell
{
public string Name { get; set; }
public int ManaCost { get; set; }
public Spell(string name, int manaCost)
{
Name = name;
ManaCost = manaCost;
}
public void Cast(Creature caster, Creature targetCreature)
{
caster.SubtractMana(ManaCost); //might throw NotEnoughManaException?
ApplySpell(caster, targetCreature);
}
protected abstract void ApplySpell(Creature caster, Creature targetCreature);
}
另外,应该向导扩展PlayerCharacter,这将扩展Creature?
1
2018-02-04 08:57
我觉得你的设计很好看。由于每个Spell类基本上都是一个函数的包装器(这更适合Command模式,而不是策略),你可以完全摆脱法术类,只需使用一些反射函数来找到拼写方法并添加一些他们的元数据。喜欢:
public delegate void Spell(Creature caster, Creature targetCreature);
public static class Spells
{
[Spell("Mage Armor", 4)]
public static void MageArmor(Creature caster, Creature targetCreature)
{
targetCreature.AddAC(4);
}
[Spell("Fire Ball", 5)]
public static void FireBall(Creature caster, Creature targetCreature)
{
targetCreature.SubtractHealth(7);
}
}
1
2018-02-05 20:26
首先:对于一切事物总是有更好/更清洁/更容易的方法。
但是在我看来,你已经对你的挑战做了很好的抽象,这可以成为进一步改进的坚实基础。
0
2018-02-04 07:21
我可能会遗漏一些东西,但三重奏WizardSpells,LoadedSpell,SetSpell似乎可以澄清一下。具体来说,到目前为止,我没有看到您的代码中使用的列表。我可能会使用LearnNewSpell(Spell newSpell)将向导可用的法术添加到列表中,并检查LoadSpell是否使用该列表中的一个咒语。
此外,如果您要使用多种类型的脚轮,您可能会考虑在某些时候在法术上添加一些关于施法者类型的额外信息。
0
2018-02-04 07:58
你的单元测试是什么样的?
设计是否使您可以轻松编写所需的测试?
0
2018-02-04 08:44