これまでCCMoveTo
や、CCRotateTo
など個々のアクションの使い方をご説明しましたが、今回は、実際にこれらのアクションがどのような内部動作をしているのかを深く解析したいと思います。
例えば、CCMoveTo
をはじめとする全てのアクションを利用する場合には以下のようにCCNode
のRunAction
メソッドを使用します。
var moveToAction = new CCMoveTo(3f, new CCPoint(50f, 100f)); node.RunAction(moveToAction);
まずは、RunAction
したアクションがどのように管理されているかを確認してみましょう。
CCNode
のRunAction
メソッドのソースコードを見てみると以下のようになっています。
public CCActionState RunAction(CCAction action) { return ActionManager != null ? ActionManager.AddAction(action, this, !IsRunning) : AddLazyAction(action, this, !IsRunning); }
RunAction
したアクションをActionManager
に登録しています。AddLazyAction
の方も追っていくと、最終的にはActionManager
に登録するようになっています。
よって、RunAction
したアクションは全てActionManager
で一括管理されるようになっています。
また、ActionManager
のAddAction
を確認すると、追加されたアクションのStartAction
をコールし、CCActionState
を返すようになっています。
public CCActionState AddAction(CCAction action, CCNode target, bool paused = false) { // 省略 var state = action.StartAction(target); // 省略 return state; }
次に、実際のアクションの動きがどのように動作しているのかを確認します。
基本的な動作原理として、1フレームごとに、ActionManager
のUpdate
メソッドがコールされます。
引数のdt
は deltaTime つまり、前のフレームから現在のフレームまでの経過時間です。
public void Update(float dt) { // 省略 for (int i = 0; i < count; i++) { // 省略 currentTarget.CurrentActionState.Step(dt); // 省略 } }
この中で、登録されているCCActionState
のStep
メソッドをコールしています。
Step
メソッドは、CCMoveTo
や、CCRotateTo
をStartAction
したときに生成されるCCMoveToState
および、CCRotateToState
の継承元であるCCFiniteTimeActionState
に実装されています。
Step
メソッドでは、当該のアクションのすでに完了した部分の時間を分子に、アクション全体の所要時間を分母にし、アクションが何%進んだかを示す値を引数にしUpdate
メソッドをコールしています。
protected internal override void Step(float dt) { if (firstTick) { firstTick = false; Elapsed = 0f; } else { Elapsed += dt; } Update (Math.Max (0f, Math.Min (1, Elapsed / Math.Max (Duration, float.Epsilon) ) ) ); }
これを、例としてCCMoveToState
で確認すると、以下のようになっています。
残りの未完了のアクションであるPositionDelta
のうち、今回のフレームで消化すべき量を求めてその分だけアクションするようになっています。
public class CCMoveToState : CCMoveByState { public CCMoveToState (CCMoveTo action, CCNode target) : base (action, target) { StartPosition = target.Position; PositionDelta = action.PositionEnd - target.Position; } public override void Update (float time) { if (Target != null) { CCPoint currentPos = Target.Position; CCPoint newPos = StartPosition + PositionDelta * time; Target.Position = newPos; PreviousPosition = newPos; } } }
つまりアクションは一見するとスケジューラのようなフレームで管理された動作とは違うように見えますが、詳しく見てみると、スケジューラと同じように1フレームごとの動作に細切れにされた上で実行されているのが分かります。
というわけで、今回はここまでです。