C# 我有一个游戏的攻击链系统,但是它很快变得非常复杂

C# 我有一个游戏的攻击链系统,但是它很快变得非常复杂,c#,algorithm,if-statement,xna,C#,Algorithm,If Statement,Xna,我正在为我的compsci学士和我们制作XNA游戏的项目it学习游戏开发课程。我们正在制作一个jrpg风格的游戏,并为战斗想出了一个很酷的主意:每回合你排队3个技能,根据技能和选择的顺序,可以应用各种奖励效果。我们成功了 从第一项技能开始,箭头显示选择的第二项和第三项技能。第二层和第三层上的框显示在该点添加的效果 基本要素: 攻击+攻击=第二次攻击的暴击几率更高。 火+攻击=在攻击的原始伤害上增加一些火伤害。 火灾+火灾=应用燃烧状态 更难的是当你连续命中3时,因为他们会进行某种特殊的攻击。攻击

我正在为我的compsci学士和我们制作XNA游戏的项目it学习游戏开发课程。我们正在制作一个jrpg风格的游戏,并为战斗想出了一个很酷的主意:每回合你排队3个技能,根据技能和选择的顺序,可以应用各种奖励效果。我们成功了

从第一项技能开始,箭头显示选择的第二项和第三项技能。第二层和第三层上的框显示在该点添加的效果

基本要素: 攻击+攻击=第二次攻击的暴击几率更高。 火+攻击=在攻击的原始伤害上增加一些火伤害。 火灾+火灾=应用燃烧状态

更难的是当你连续命中3时,因为他们会进行某种特殊的攻击。攻击有1/4几率爆击,火焰造成双倍伤害火焰攻击(命名为火球)

在if语句中实现这一点可能需要更多的技巧。if语句的数量等于n^n从1到n的总和,因此如果我们想要6种技能,我们需要编写6+36+216=258个if语句!其中许多也将是多余的!这很容易出错,因为我们必须精心设计每个if语句,以便在编写流程图时它们处于正确的位置

这就是为什么我们认为应该有一些具有静态效果的广义组合,如果可以累积,可能会增加一个计数器,然后当我们连续有3个时,调用具有该技能特殊攻击的函数

首先想到的是有限状态机。它可以处理除特价品以外的所有案件。也许是下推自动机?我的主要问题是,我不知道如何在代码中实现它们。我在课堂上学到的是理论


还有其他更有效或更容易编写/编码的方法吗?

我想找出某种递归模型:

enum Outcomes { Crit, DoubleCrit, FireDMG, Burn, NoEffect }

abstract class Attack 
{ 
    public Attack() { Child = null; }

    List<Outcomes> GetOutcomes(); 
    protected virtual Attack Child; 
}
class Melee : Attack 
{ 
    public Melee() : base() { }
    public Melee(Attack child) : base() { Child = child; }

    List<Outcomes> GetOutcomes()
    {
        List<Outcomes> ret = new List<Outcomes>();
        if(Child != null) ret.Add(Child.GetOutcomes());

        if(ret.Contains(Outcomes.Crit))
            ret.Add(Outcomes.DoubleCrit);
        else
            ret.Add(Outcomes.Crit);

        return ret;
    }
}
class Fire : Attack 
{ 
    public Fire() : base() { }
    public Fire(Attack child) : base() { Child = child; }

    List<Outcomes> GetOutcomes()
    {
        List<Outcomes> ret = new List<Outcomes>();
        if(Child != null) ret.Add(Child.GetOutcomes());

        List<Outcomes> PossibleOutcomes = new List<Outcomes>();        

        PossibleOutcomes.Add(Outcomes.FireDMG);
        PossibleOutcomes.Add(Outcomes.Burn);

        if(ret.Contains(Outcomes.Burn)) PossibleOutcomes.Add(Outcomes.Fireball)
        if(ret.Contains(Outcomes.FireDMG)) PossibleOutcomes.Add(Outcomes.NoEffect);

        // Use some randomization method to select an item from PossibleOutcomes
        int rnd = 2; // Totally random number.
        ret.Add(PossibleOutcomes[rnd]);

        return ret;
    }
}
或者假设用户选择了每个分支,您只需要保留最后一个分支,并将其作为子级持续添加到他们选择的下一个攻击中

Attack ChosenAttack = new Melee();

// Some events occur...

ChosenAttack = new Fire(ChosenAttack);

// Some more...

ChosenAttack = new Melee(ChosenAttack);

如果我不太理解这个问题,并且我的建议不起作用,我向您道歉。

听起来您真的希望仍然能够定制不同选择对玩家的影响,同时避免编写所有代码

实现这一点的一种方法是创建一个数据结构,该结构将处理任何组合的结果,并使创建新组合的任务更容易处理潜在的大量可能性,这些可能性比必须重新编译的代码(如XML)和基于用户查询结果更容易更改选择

您可能有一个类似以下的类来存储结果

public class AttackResult
{
    public int ChanceToCrit { get;set; }
    public int ChanceToBurn { get;set; }
    public int FireDmg { get;set; }
}
至于XML,您可能会有如下内容:

<Options>
    <Attack>
        <AttackResult>
            <ChanceToCrit>5</ChanceToCrit>
        </AttackResult>
        <Attack>
            <AttackResult>
                <ChanceTCrit>10</ChanceToCrit>
            </AttackResult>
        </Attack>
    </Attack>
    <Fire>
    </Fire>
</Options>
我认为,为了让这个例子与你的游戏理念相配合,所有的动作都是必须的

public ActionResult ProcessActions(List<ActionType> allActions)
{
    string xpathquery = "Options";

    foreach(var playerAction in allActions)
    {
        if(playerAction == ActionType.None)
           break;
        xpathquery += "/" + playerAction.ToString();
    }

    //Query xmldocument of all options
    var result = Singleton.ActionsDoc.SelectSingleNode(xpathquery);
    string attackRes = result.InnerXml;
    XmlSerializer serializer = new XmlSerializer(typeof(ActionResult))
    //Might need to prepend something to attackRes before deserializing, none of this is tested
    byte[] allBytes = Encoding.UTF8.GetBytes(attackRes);
    MemoryStream ms = new MemoryStream(allBytes);

    ActionResult result = (ActionResult)serializer.Deserialize(ms);

    return result;
}
公共操作结果处理操作(列出所有操作)
{
字符串xpathquery=“Options”;
foreach(所有动作中的var playerAction)
{
如果(playerAction==ActionType.None)
打破
xpathquery+=“/”+playerAction.ToString();
}
//查询所有选项的XML文档
var result=Singleton.ActionsDoc.SelectSingleNode(xpathquery);
字符串attackRes=result.InnerXml;
XmlSerializer serializer=新的XmlSerializer(typeof(ActionResult))
//在反序列化之前,可能需要预先准备一些要攻击的东西,这些都没有经过测试
byte[]allBytes=Encoding.UTF8.GetBytes(attackRes);
MemoryStream ms=新的MemoryStream(所有字节);
ActionResult=(ActionResult)序列化程序。反序列化(ms);
返回结果;
}
这个选项有它的缺点,比如XmlSerializer非常慢,但这意味着您可以创建一个简单的工具来帮助生成所有选项所需的xml,并简单地调整值以平衡游戏


摆在桌面上的一个选项。希望这能有所帮助。

除非你有更好的车型,否则你必须坚持FSM。正如你已经说过的,它可能会变得相当大,但是看起来这些技能只是修改了一些修改器,然后修改了下一个技能,所以你的模型可能类似于
{norm bonus,fire bonus};{n,f}+fire=>{0,f*1.1}+{burn:f};{n,f}+normal=>{n+0.01,f*0.5}+{atk:n*f}
(阅读:如果使用火焰,增加火焰修正值10%并进行一些燃烧。如果使用普通攻击,增加暴击几率1%,减少火焰效果50%,并造成相当于暴击几率乘以火焰效果的伤害)
public enum ActionType
{
    Attack,
    Fire,
    None
}
public ActionResult ProcessActions(List<ActionType> allActions)
{
    string xpathquery = "Options";

    foreach(var playerAction in allActions)
    {
        if(playerAction == ActionType.None)
           break;
        xpathquery += "/" + playerAction.ToString();
    }

    //Query xmldocument of all options
    var result = Singleton.ActionsDoc.SelectSingleNode(xpathquery);
    string attackRes = result.InnerXml;
    XmlSerializer serializer = new XmlSerializer(typeof(ActionResult))
    //Might need to prepend something to attackRes before deserializing, none of this is tested
    byte[] allBytes = Encoding.UTF8.GetBytes(attackRes);
    MemoryStream ms = new MemoryStream(allBytes);

    ActionResult result = (ActionResult)serializer.Deserialize(ms);

    return result;
}