読者です 読者をやめる 読者になる 読者になる

プログラミングの魔物

エラー、バグ、仕様変更と戦うブログ

cocos2dの基礎 ノード、シーン、レイヤ、アクション

cocos2dで作るiPhone&iPadゲームプログラミング3章

cocos2dには以下のシングルトンクラスがよく使われている

CCActionManager* sharedManager = [CCActionManager sharedManager];
CCDirector* sharedDirector = [CCDirector sharedDirector];
CCSpriteFrameCache* sharedCache = [CCSpriteFrameCache sharedSpriteFrameCache];
CCTextureCache* sharedTexCache = [CCTextureCache sharedTextureCache];
CCTouchDispatcher* sharedDispatcher = [CCTouchDispatcher sharedDispatcher];
CDAudioManager* sharedManager = [CDAudioManager sharedManager];
SimpleAudioEngine* sharedEngine = [SimpleAudioEngine sharedEngine];

Director

cocos2dの心臓部。グローバル設定、シーンの管理

  • シーンへのアクセスと変更
  • cocos2dの設定へのアクセス
  • ビュー(OpenGL、UIView、UIWindow)へのアクセス
  • ゲームの一時停止、再開、終了
  • UIKitおよびOpenGL座標の変換

4種類のDirectorから選択可能(細部が異なる)。最もよく使われるのはCCDirectorDisplayLink。iOS3.1以降のバージョンでしか利用できない
CocoaTouchと並行する場合はCCDirectorFastThreaded

シーングラフ

現在アクティブなすべてのcocos2dノードからなる階層
シーンそのものを除くすべてのノードが親ノードを1つだけ持ち、子ノードをいくつでも持つことができる。
配下のノードは移動、回転、拡大縮小の影響を受ける

CCNodeクラス

CCNodeはすべてのノードの親クラス

ノードを操作する

  • CCNode* childNode = [CCNode node]; //新しいノード作成
  • [myNode addChild:childNode z:0 tag:123]; //子を追加。zの小さいものから順に描画。zが同じ場合は追加順
  • CCNode* retrievedNode = [myNode getChildByTag:123]; //子を取得
  • [myNode removeChildByTag:123 cleanup:YES]; //タグで子を削除。cleanupは実行中のアクションも全て停止
  • [myNode removeChild:retrievedNode]; //ノードポインタで削除
  • [myNode removeAllChildrenWithCleanup:YES]; //すべての子ノード削除
  • [myNode removeFromParentAndCleanup:YES]; //親ノード(myNode)を削除

同じタグをつけるとその中から最初のノードしか取り出せない
ノードとアクションのタグは競合しない

アクションを操作する

ノードではアクションも実行できる。移動、回転、拡大縮小など

  • CCAction* action = [CCBlink actionWithDuration:10 blinks:20]; //アクションを宣言する
  • action.tag = 234;
  • [myNOde runAction:action]; //ノードを点滅させるアクションを実行
  • CCAction* retrievedAction = [myNode getActionByTag:234]; //タグを使ってアクションを取得
  • [myNode stopActionByTag:234]; //タグを使って停止
  • [myNode stopAction:action]; //ポインタで停止
  • [myNode stopAllActions]; //ノードのすべてのアクションを停止

スケジュールされたメッセージ

[self scheduleUpdate]でupdateを設定すると

-(void) update:(ccTime)delta

が各フレームで呼び出される。
別のメソッド、あるいは別の間隔で呼び出したい場合は

[self schedule:@selector(updateTenTimesPerSecond:) interval:0.1f];とすると
-(void) updateTenTimesPerSecond:(ccTime)delta

が0.1秒おきに呼び出される
ノードのすべてのセレクタを停止させるには

[self unscheduleAllSelectors];

特定のメッセージを停止するには

[self unschedule:@selector(updateTenTimesPerSecond:)];//セレクタを指定した場合
[self unscheduleUpdate];//scheduleUpdateで呼び出した場合

セレクタ内で停止させるには

[self unschedule:_cmd];//_cmdは現在のメソッドを表す省略表記

ノードごとにupdateに優先順位を付けられる

[self scheduleUpdateWithPriority:-1];//デフォルトは0なので-1が先に呼び出される

シーンとレイヤ

CCSceneとCCLayerはCCNodeと同様ビジュアル表現を持たない抽象概念。シーングラフの出発点
シーングラフの出発点はCCScene
CCLayerはノードのグループ化、入力の処理など

CCScene

[[CCDirector sharedDirector] runWithScene:[HelloWorld scene]]; //最初のシーン登録
[[CCDirector sharedDirector] replaceScene:[HelloWorld scene]]; //置き換え

新しいシーンを読み込むときは古いシーンが破棄される前なので一時的にメモリが増大する
シーンの作成と破棄でログを吐くといい。切替時にdeallocのログメッセージが送信されていなかったらシーン全体がメモリリークしていることになる。
ノードのメモリ管理はcocos2dに任せないと危険。

シーンのプッシュとポップ

pushSceneとpopScene。古いシーンを破棄せずに新しいシーンを実行
複数の場所で使われる共通のシーン、ボリュームなどの設定画面の呼び出しなどに使う。
素早く変更できる反面メモリを食う。
スタックに積むので気をつけないと積み過ぎになったり取り過ぎになったりしてしまう。

[[CCDirector sharedDirector] pushScene:[Stettings scene]]; //プッシュ
[[CCDirector sharedDirector] popScene]; //ポップ

CCTransitionScene

見た目は良いが切り替えに時間が掛かる。1秒以内か使わないのが妥当。

CCTransitionFade *tran = [CCTransitionFate transitionWithDuration:3 scene:[HelloWorld scene] withColor:ccRED]; //3秒で赤にフェード
[[CCDirector sharedDirector] replaceScene:tran];

replaceSceneやpushSceneで使えるがpopSceneでは別のテクニックが必要。あるいは最新バージョンではすでに出来るかも
P58にTransitionの種類が載ってる

CCLayer

シーンによっては複数のCCLayerが必要になることがある。その場合はシーンの作成時にレイヤを追加する必要がある。
たとえば静的なフレームの下にスクロールする背景がある場合など。背景レイヤを動かすとそのレイヤにある要素は追従するので簡単に移動できる。またレイヤのZオーダーに応じて同じレイヤのオブジェクトが前面、背面に移動可能
レイヤはグループ化の概念。まとめて移動、回転、拡縮。
いくつ作ってもいいが、タッチ入力や加速度センサーを受け取るレイヤを作り過ぎないようにする。必要に応じて転送するなど

タッチイベントの受け取り

self.isTouchEnabled = YES;

タッチの開始、移動、終了、中止が報告されるが、中止はほとんど無いので無視するか終了に転送する。
タッチイベントはCocoaTouchAPIによって受け取られるため、OpenGL座標に変換する必要がある

-(CGPoint) locationFromTouch:(UITouch *t)touch{
  CGPoint touchLocation = [touch locationInView: [touch view]];
  return [[CCDirector sharedDirector] convertToGL:touchLocation];
}

マルチタッチに対応するには[glView setMultipleTouchEnabled:YES]する必要がある
・ターゲットタッチハンドラは一連のタッチを別々のイベントに分割する。
特定のタッチをイベントキューから削除することで他のレイヤに転送しないようにできる。
ターゲットハンドラを有効にするには

[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];

タッチ入力メソッドはtouchesではなくtouch

加速度センサー

self.isAccelerometerEnabled = YES;
CCSprite
CCSprite *sprite = [CCSprite spriteWithFile:@"Default.png"];//ファイルからCCSprite作成
[self addChild:sprite];

※iOSデバイス上ではファイル名の大文字と小文字が区別されるので注意

アンカーポイント

テクスチャのオフセット。デフォルトは0.5

テクスチャのサイズ

4,8,16,32,64,128,256,512,1024ピクセルのいずれか
第三世代では2048が加わる。テクスチャは正方形でなくてもいい。

CCLabelTTF

テキスト表示するための単純な選択肢

メニュー

CCMenuクラス。CCMenuItemノードを追加してメニューを設定する。
詳細はP66

アクション

どのノードでも利用可能。

CCMoveTo* move = [CCMoveTo actionWithDuration:3 postion:CGPointMake(100,200)];//(100,200)へ3秒以内に移動
[myNode runAction:move];

アクションにはインスタンスアクションとインターバルアクションの2種類がある。インスタンスアクションは、基本的にはvisibleやflipXのようなノードプロパティを設定するのと同じ。インターバルアクションは上の移動のように一定時間にわたって実行される。
アクションは終わると自動的にノードから削除され、メモリから解放される。

アクションを繰り返す

CCRotateBy* rotateBy = [CCRotateBy actionWithDuration:2 angle:360];
CCRepeateForever* repeat = [CCRepeatForever actionWithAction:rotateBy];
[myNode runAction:repeat];//永遠に回転

イーズアクション

一定の動きではなく徐々に変化させたりするアクション。

CCMoveTo* move = [CCMoveTo actionWithDuration:3 position:CGPointMake(100,200)];
CCEaseInOut* ease = [CCEaseInOut actionWithAction:move rate:4];//ゆっくりと加速したあと減速する
[myNode runAction:ease];

アクションシーケンス

通常、複数のアクションを登録するとすべてのアクションがそれぞれのタスクを同時に実行する。アクションを順番に実行するにはシーケンスにする。

インスタントアクション

プロパティでも変更可能な効果だがシーケンスに組み込める。
CCCallFuncアクション・・・シーケンス終了などのタイミングで通知メッセージを送信できる。

参考:

cocos2dで作る iPhone&iPadゲームプログラミング

cocos2dで作る iPhone&iPadゲームプログラミング