我想了解一下如何实现基于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;
}
}
答案 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;
}
}