今回は、Cocos Sharp の もう一つの重要な機能であるスケジューラがどのような内部動作をしているのかを深く解析したいと思います。
スケジューラにメソッドを登録する場合には、以下のようにCCNodeのScheduleメソッドを使用します。
Schedule(t => this.DetectCollisions(), 0.1f);
これは、0.1秒ごとにDetectCollisionsメソッドを実行しなさいという意味です。
まずは、Scheduleメソッドの内部動作を確認してみましょう。
CCNodeのScheduleメソッドのソースコードを見てみると以下のようになっています。
public void Schedule (Action<float> selector, float interval) { Schedule (selector, interval, CCSchedulePriority.RepeatForever, 0.0f); }
Scheduleメソッドのオーバーロードをコールしていますので、コール先のソースコードを見てみると以下のようになっています。
public void Schedule (Action<float> selector, float interval, uint repeat, float delay) { if (Scheduler != null) Scheduler.Schedule (selector, this, interval, repeat, delay, !IsRunning); else AddLazySchedule (selector, this, interval, repeat, delay, !IsRunning); }
SchedulerプロパティのScheduleメソッドをコールしています。AddLazyScheduleの方も追っていくと、最終的にはScheduleメソッドをコールするようになっています。
Schedulerプロパティのを確認すると、その実体はCCScheduler.SharedSchedulerのようです。
CCScheduler Scheduler
{
get { return CCScheduler.SharedScheduler; }
}
SharedSchedulerというプロパティ名からもわかるとおり、CCSchedulerを確認すると Singleton になっています。
さらに、Scheduleメソッドを確認すると以下のように、デリゲートと付帯情報をCCTimerのリストとして登録しています。
public void Schedule (Action<float> selector, ICCUpdatable target, float interval, uint repeat, float delay, bool paused) { // 省略 element.Timers.Add(new CCTimer(this, target, selector, interval, repeat, delay)); // 省略 }
よって、CCNodeのScheduleメソッドに登録したデリゲートは全てCCSchedulerで一括管理されるようになっていることがわかります。
では次に一括管理されたデリゲートがどのように実行されていくか見てみましょう。
デリゲートと付帯情報はCCTimerのリストとして管理されており、CCSchedulerのUpdateメソッド内で、各CCTimerのUpdateメソッドがコールされています。
public class CCScheduler { internal void Update (float dt) { if (TimeScale != 1.0f) { dt *= TimeScale; } // 省略 for (elt.TimerIndex = 0; elt.TimerIndex < elt.Timers.Count; ++elt.TimerIndex) { elt.CurrentTimer = elt.Timers[elt.TimerIndex]; if(elt.CurrentTimer != null) { elt.CurrentTimerSalvaged = false; elt.CurrentTimer.Update(dt); elt.CurrentTimer = null; } } // 省略 } }
CCTimerのUpdateメソッドを確認してみると以下のようになっています。
internal class CCTimer : ICCUpdatable { public void Update(float dt) { if (elapsed == -1) { elapsed = 0; timesExecuted = 0; } else { if (runForever && !useDelay) { //standard timer usage elapsed += dt; if (elapsed >= Interval) { if (Selector != null) { Selector(elapsed); } elapsed = 0; } } else { // 省略 } } } }
今回のフレームの経過時間であるdtから、前回デリゲートを実行してからのトータル経過時間elapsedを計算して、与えられたIntervalを経過していたら、Selectorデリゲートを実行しているのが分かります。
これで、スケジューラがどのような内部動作をしているのかがわかりました。
というわけで、今回はここまでです。