实施基于刻度/回合的战斗系统

时间:2012-10-11 10:16:08

标签: c# xna

我想了解一下如何实现基于tick的系统。

玩家或非玩家获得的每个动作都有最初的执行时间和冷却时间。一旦生物的冷却时间过去,它就会选择一个新动作。如果玩家必须选择一个动作,游戏就会“暂停”。

示例:

1:玩家重重摆动(执行50个刻度,冷却50个刻度)

2:游戏持续50个小时。

3:NPC可以设定行动。

4:玩家挥动并冷却50个刻度。

5:NPC可以设定行动。

6:游戏暂停了游戏。

我目前有什么作品,但效率不高。我有一个类,每个动作都是静态方法。这些方法输出包含所有数据的结构。这将被传递给单个生物的动作。

每个更新循环调用提示并开始倒计时,如果玩家已经执行了操作。一旦攻击应该解决,我再次在actions类中调用静态方法。我开始倒数冷却计时器。

所以我应该拥有的可能是一个包含所有操作和排序列表的列表,跳过不必要的时间/滴答并直接进入下一个操作。但是会有不同类型的动作,比如移动,攻击,能力,我无法围绕这个很好的实现。

当一个生物执行基本攻击时,它会被调用(攻击是生物自己的实例化攻击结构)

attack = Actions.BasicAttack(this, player, rand);

这就是Actions类的样子。

public struct Attack
    {
        public int Damage;
        public string Type;
        public int Time;
        public int Cooldown;
        public Creature target;
        public bool solved;
    }




    public static Attack BasicAttack(Creature attacker, Creature defender, Random rand)
    {
        Attack attack = new Attack();

        attack.Damage = rand.Next(attacker.MinBaseDmg, attacker.MaxBaseDmg + 1);
        attack.Type = "Melee";
        attack.Time = 50;
        attack.Cooldown = 30;
        attack.target = defender;
        attack.solved = false;

        return attack;
    }

当玩家有一个动作提示时,这会在每个生物的更新方法中被调用。如果玩家没有动作提示,则Tick = 0,当玩家有提示动作时,tick = 1。

protected void ActionCue(int tick)
    {
        if (attack.target != null)
        {
            if (attack.Time > 1)
            {
                Console.WriteLine(attack.Time);
                attack.Time -= tick;
                this.free = false;
            }
            else if (!attack.solved)
            {
                Actions.SolveAttack(attack.Damage, attack.Type, attack.target);
                attack.solved = true;
            }
            else if (attack.solved && attack.Cooldown > 1)
            {
                //Console.WriteLine(attack.Cooldown);
                attack.Cooldown -= tick;
            }
            else
                free = true;
        }
    }

2 个答案:

答案 0 :(得分:1)

考虑这样的事情(我将使用伪代码 - 它远未被优化等等,但它可能足够快,或者让你在优化你想要做的事情的路上)

class CombatEventList
{
   public static AddEvent(CombatEvent event, int ticksTillHappens)   
}

virtual class CombatEvent
{
    public virtual void CombatAction()
}

class PlayerActionChoice : ComabtEvent
{
   public void CombatAction
   {
       var playerAction = GetUserDecision();//returns i.e CombatEvent PlayerMeeleAttack
       CombatEventList.AddEvent(playerAction, 0);
   }
}

class PlayerMeeleAttack : CombatEvent
{
   int cooldownInTicks = 50;

   public void CombatAction
   {
       MakeAttack()//damages the moster etc - all the stuff the attack is supposed to do
       var nextEvent = new PlayerActionChoice();
       CombatEventList.AddEvent(nextEvent, cooldownInTicks);
   }
}

那么,这是如何运作的?

我们收到了一系列活动。

列表检查现在应该发生的所有事件,并执行他们的CombatAction。

在他们的CombatAction中,事件会向列表中添加新事件。例如,PlayerMeeleAttack事件在适当的冷却后设置PlayerActionChoice事件,以便他可以稍后采取其他操作。

解决了所有当前的CombatEvents并将自己的CombatEvents添加到列表后,列表会检查下一个事件(最低延迟)

列表会睡眠指定的滴答数(下一个事件的延迟)。一旦完成睡眠,它会以适当的数量降低所有事件的冷却时间,并处理所有当前事件(那些刚刚达到0延迟的事件)

这是一个循环

列表以CombatStartEvent开头,即将发生(延迟0)。它在CombatAction方法中设置PlayerActionChoice和MonsterActionChoice事件。

当然,这远不是最佳的,它只是一个草图,或者是一个让你思考的想法。可能有更好的想法,我没有想到这个问题 - 但这显然比你目前的解决方案更有效:)

答案 1 :(得分:0)

好几个小时后,我觉得这个工作正常。以下是需要它的人的代码。我也愿意接受反馈。

这是能力等级,所需的一切都可以在这里添加。对于基于tick的系统,时间变量很重要。

public string AbilityName { get; private set; }
    public int minDamage { get; private set; }
    public int maxDamage { get; private set; }

    public int ActivationTime { get; private set; }
    public int CooldownTime { get; private set; }
    public int Timer;

    public Ability(string AbilityName)
    {
        if (AbilityName == "attack")
        {
            this.AbilityName = AbilityName;
            minDamage = 10;
            maxDamage = 20;
            ActivationTime = 20;
            CooldownTime = 30;
            Timer = ActivationTime;

            iconPath = "ability/icon/attack";
        }
    }

这是任务类,能力,攻击者和目标作为参数传递,能力名称或类型可用于执行不同种类/类型的能力,如移动与攻击。

public Ability ability { get; private set; }
    public bool onCooldown;

    public Creature attacker { get; private set; }
    List<Creature> targets = new List<Creature>();

    /// <summary>
    /// Initiates a attack task
    /// </summary>
    /// <param name="attacker"></param>
    /// <param name="defender"></param>
    public Task(Creature attacker, List<Creature> targets, Ability ability)
    {
        this.ability = ability;
        this.attacker = attacker;
        this.targets = targets;

        onCooldown = false;
    }

    public void Perform()
    {
        //performce abilty
        Console.WriteLine(attacker.Name + " performce ability");
    }

玩家或AI现在可以从他们拥有的能力中创建任务,如下所示:

targets.Add(player); //This is just a basic attack so only one "creature" gets in the list
                task = new Task(this, targets, abilityList[0]); //Task is created
                taskList.Add(task); //Task is added to a list i manage in a main class
                free = false; //creature is put on hold and cant do anything till task is completed

这是大多数魔法发生的地方。在主类中,如果玩家不是“免费”,则每次更新都会调用此方法。在我对下一个任务执行任何操作之前,我会更新所有任务,因为我不想在更新其状态后编辑其统计信息。

private void TaskHandler()
    {
        int ticksToAdvance = 0;
        // get the next task requiring a action
        taskList.Sort((x, y) => x.ability.Timer.CompareTo(y.ability.Timer));

        //get the amount of cooldown left
        ticksToAdvance = taskList[0].ability.Timer;

        //Update all tasks
        foreach (Task t in taskList)
        {
            t.ability.Timer -= ticksToAdvance;
        }

        //check if this task is on cooldown
        if (taskList[0].onCooldown)
        {
            //Reset ability timer, free creature and remove task from the list.
            taskList[0].ability.Timer = taskList[0].ability.ActivationTime;
            taskList[0].attacker.free = true;
            taskList.RemoveAt(0);
        }
        else
        {
            //perform ability
            taskList[0].Perform();
            //set timer to cooldown
            taskList[0].onCooldown = true;
            taskList[0].ability.Timer = taskList[0].ability.CooldownTime;
        }
    }