■各種アクション系クラスの考え方
先の記事で触れた通り、スプライトのパラメータ変更は即時に表示へ反映されるので、位置やスケール・アルファを時々刻々変化させたり、あるいはテクスチャ切り出し場所やテクスチャそのものを順々に入れ替えたりすれば、スプライトを動かすことができる。
なので、タイマーを設定して一定時間ごとにスプライトの状態を変化させるコードを自前で書けば動きを作ることは可能である。
が、スプライトの数が多くなったり動きが複雑になると管理が複雑になってくるし、何より面倒くさい。
そうした場合には各種アニメーション用のクラスを利用すると、上述のような定期的な状態変更を自動で実行してくれるので、簡単にアニメーションを実現できる。
たとえば「スプライト aSprite の scale を1秒間の間に 1.0 から 2.0 に変化させる」は次のように記述する。
CCSprite* sprite1 = [CCSprite spriteWithFile:@"dog1.png"]; //スプライト作成
CCActionTween* scaleAction1 = [CCActionTween actionWithDuration:1.0 key:@"scale" from:1.0 to:2.0]; // アクションオブジェクト※1
[sprite1 runAction:scaleAction]; // パラメータ操作を受けるスプライトを指定※2
CCActionTweenは、パラメータ変化を定期的に実行するためのクラスで、key: を変更すればスケールや位置・アルファなど任意のパラメータを操作できる。
このようにすると、一定時間ごと(この場合1フレームごと)に scaleAction が sprite1 のパラメータ変更メソッド([sprite1 setScale: ])を呼び出し、結果的に sprite1 の拡大アニメーションが実現する。
他のアニメーション系クラスも同様に一定時間ごとに指定したスプライトのパラメータを変化させる仕組みになっている。
cocos2dの用語では、こうしたアニメーションを総称してアクションと呼び、その中で特にテクスチャを入れ替えるコマアニメーションだけをアニメーションと呼ぶ。
以下もそれにならうことにする。
※1)cocos2d付属のサンプルを見ると、アクション系クラスの宣言は、 id scaleAction1 = … などと id で宣言されていることが多い。そのようにしている理由は調べてないので分からないが、個人的な感想として、アクションを合成したりいろいろやってるとコロコロとクラス名が変わるので id で宣言したくなる気分にはなる。
※2)これも個人的な感想だが、 scaleAction が sprite1 に命令するという構図なのにコード上は「sprite1 に scaleAction を付加する」という形式になっているのがちょっと違和感。
CCAction に startWithTarget:target というメソッドがあるので これを使ってもいいのだが、あくまで cocos2d の作法に則った方がいろいろと都合が良いので頭の中で読み替えるにとどめてます。
■アクションのターゲットについて
CCAction.h を見ると、アクションがどのスプライトを操作するのか=すなわちターゲットの定義は
id target_;
となっている。これは一見するとどんなオブジェクトでもOKな雰囲気ではあるが、実際には CCNodeやそのサブクラスが想定されており、コレクションクラス(NSArray や NSSet)は想定されていない。
ということはつまり、1つのアクションのターゲットは1個に限定されている、ということになる。
したがって、sprite1とsprite2に同じ動きをさせようと思って
[sprite1 runAction:action];
[sprite2 runAction:action];
みたいにやっても、結局
action.target_ = sprite1;
action.target_ = sprite2; //(実際にはできません)
とやっているのと同じで、
1行目でターゲットが sprite1 に、2行目でターゲットが sprite2に変更されるだけ。よって、結果は sprite2 しか動かない。
同じ動きを2個のスプライトにさせたいときは、同じアクションを2個作るしかないようだ。
(ちなみに、
id action = […]
[sprite1 runAction:action];
[sprite2 runAction:[[action copy] autoRelease];
のようにしても良いという情報もあるようだが、自分の環境では動かなかった)
■アクションの合成
アクション系クラスは、パラメータ変化を命令するだけが仕事なので、同時にいくつも runAction すればスプライトのパラメータが同時に何種類も変更され、結果的にアクションの合成ができる。
ちょうど、上述の関係と逆である(「アクション1個 を 複数スプライトにかける」は不可だが、「複数アクション を スプライト1個にかける」は可能)。
あえて明示的に合成したい場合などは、CCSpawnを使うと合成済みのアクションオブジェクトを取得できる。スプライトの runAction には合成済みアクションを指定すればよい。
id doubleAction = [CCSpawn actionOne:action1 two:action2];
[sprite1 runAction:doubleAction];
さらに、合成とは異なるが、「繰り返す」「このアクションの後にこのアクション」とかいったことも指定できる。
id newAction1 = [CCRepeat action:anAction times:times]; // 繰り返し
id newAction2 = [CCSequence actions:action1, action2,nil]; // action1の次にaction2を実行
また、CCCallFunc と CCSequence を併用すると終わった後に Objective-C のメソッドを呼ぶことも可能だ。例えば以下のようにすれば action1 と action2 が実行された後に self の finishMethod が呼ばれる。
id callFuncAction = [CCCallFunc actionWithTarget:self selector:@selector(finishMethod)];
id newAction3 = [CCSequence actions:action1, action2, callFuncAction, nil];