我正在参加我的compsci学士学位的游戏开发课程以及我们制作XNA游戏的项目。我们正在制作一个jrpg风格的游戏,并为战斗提出了一个很酷的想法:每个回合你排队3技能,根据技能和选择的顺序可以应用各种奖励效果。我们制作了a basic one with 2 skills: attack and fire。
你从第一个技能开始,然后箭头显示所选择的第二和第三技能。第二级和第三级的框显示了此时添加的效果。
基础知识: 攻击+攻击=第二次攻击的爆击几率更高。 火+攻击=对攻击的原始伤害造成一些火焰伤害。 Fire + Fire =应用刻录状态
更难的部分是当你连续击中3次时,因为它们会造成一种特殊的攻击。攻击造成1/4的暴击几率,火焰造成双重伤害火力攻击(命名为火球)。
在if语句中实现这一点可能会让更多技能变得痛苦。 if语句的数量等于n ^ n从1到n的总和,所以如果我们需要6个技能,我们需要编写6 + 36 + 216 = 258 if语句!其中许多也是多余的!这很容易出错,因为我们必须精心制作每个if语句,以便在编写流程图时它们处于正确的位置。
这就是为什么我们认为我们应该有一些具有静态效果的广义组合,如果它可以累积则可以增加一个计数器,然后当我们连续3次调用具有该技能的特殊攻击的函数时。 / p>
首先想到的是有限状态机。除了我认为的特价外,它能够处理所有情况。也许是一个下推式自动机?我遇到的主要问题是我不知道如何在代码中实现它们。我在课堂上学到的是理论课。
还有其他更高效或更容易编写/编码的方法吗?
答案 0 :(得分:1)
我会想出某种递归模型:
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(new Fire(new Melee()));
或者假设用户选择每个分支,您只需要保留最后一个分支,并将其作为孩子继续添加到他们选择的下一个攻击中。
Attack ChosenAttack = new Melee();
// Some events occur...
ChosenAttack = new Fire(ChosenAttack);
// Some more...
ChosenAttack = new Melee(ChosenAttack);
如果我不太了解这个问题而道歉,我的建议将不起作用。
答案 1 :(得分:1)
听起来你真的想要仍然能够自定义不同选择对玩家的影响,同时避免编写所有代码。
执行此操作的一种方法是创建一个数据结构,该数据结构将处理任何组合的结果,并使创建新组合的任务更容易处理可能更容易的大量可能性改变比你必须重新编译的代码,例如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>
你明白了。在处理您的用户选择时(我只是在这里猜测),您可以存储他们在Enum中所做的事情。因此,要使用XML,您可能会按照
的方式执行某些操作public enum ActionType
{
Attack,
Fire,
None
}
allActions必须是为了让这个例子与我认为的游戏理念一致。
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;
}
这个选项让它的缺点如XmlSerializer相当慢,但这意味着你可以创建一个简单的工具来帮助生成所有选项所需的xml,并简单地调整值以平衡游戏。
桌子上的一个选项。希望有所帮助。