ティックベースのシステムを実装する方法について考えてみたいと思います。
プレイヤーまたは非プレイヤーが取得したすべてのアクションには、最初に実行する時間とクールダウン時間があります。クリーチャーのクールダウン時間が経過すると、新しいアクションを選択できるようになります。プレーヤーがアクションを選択する必要がある場合、ゲームは「一時停止」されます。
例:
1: プレーヤーのヘビースイング (実行に 50 ティック、クールダウンに 50 ティック)
2: ゲームは 50 ティック続きます。
3: NPC はアクションを設定できます。
4: プレイヤーがスイングし、50 ティックの間クールダウンします。
5:NPCはアクションを設定できます。
6: プレーヤーのゲームが一時停止しました。
私が現在持っているものは機能しますが、効率的ではありません。各アクションを静的メソッドとして持つクラスがあります。これらのメソッドは、すべてのデータを含む構造体を出力します。これは、個々のクリーチャーのアクション キューに渡されます。
更新ループごとにキューが呼び出され、プレーヤーがアクションを実行した場合は攻撃時間のカウントダウンが開始されます。攻撃が解決されたら、アクション クラスの静的メソッドを再度呼び出します。そして、クールダウン タイマーのカウント ダウンを開始します。
したがって、私が持っているべきものは、おそらくすべてのアクションを保持し、そのリストをソートして不要な時間/ティックをスキップし、次のアクションに直接進むリストです。しかし、移動、攻撃、能力などのさまざまなタイプのアクションがあり、これの適切な実装について頭を悩ませることはできません.
クリーチャーが基本的な攻撃を実行すると、これが呼び出されます (attack はクリーチャー自身のインスタンス化された攻撃構造体です)
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;
}
これは、プレイヤーがアクション キューを取得したときに、各クリーチャーの update メソッドで呼び出されます。プレーヤーにキューされたアクションがない場合は 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;
}
}