このチュートリアルは、全2回でお届けしている”Cocos2Dでモグラたたきゲームの作成チュートリアル”の2回目です。このシリーズは、このサイトの他のcocos2Dチュートリアルから幾つかのコンセプトをまとめて一つのアプリを作り上げ、更に新しいコンセプトも加えてお送りします。
前回シリーズ:パート1では、モグラが穴から飛び出して引っ込む、ゲームの基本を作成しました。加えてイメージをそれぞれの違ったデバイス(iPhone、 iPad、そしてRetinaディスプレイ )をそれぞれ効率的に対応させる為の座標変換等に関しても勉強しましたね。
このチュートリアルでは、モグラに可愛い笑顔と叩かれた時の顔をアニメーションで表現しましょう。加えて、モグラ叩きに成功したらポイントを獲得しましょう。当然、サウンドエフェクトも。
もしまだ持っていないなら、前回のチュートリアルで作成したプロジェクトをこちらからダウンロードしてしてください。
アニメーションの定義:実用性
ゲームをもっと楽しくする為に、モグラに2種類のアニメーションを追加します。1つは穴から飛び出して来た時にニヤリとします。2つ目は叩かれた時の顔ですね。
しかし始める前に、アニメーションの定義の実用性を見てみましょう。
アニメーションを作成する手順の一つはスプライトフレームのリストを作成することであることをCocos2dアニメーションチュートリアルから思い出してください。アニメーションで、それぞれ異なる画像に対しては、このような配列にそのスプライトのスプライトフレームを追加する必要があります:
[animFrames addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"myImage.png"]]; |
モグラが笑うアニメーションのイメージは次の順番で実行されます:mole_laugh3.pngをmole_laugh2.png mole_laugh2.png、mole_laugh3.png、mole_laugh1.png。
ですから、以下の様に何行にもわたって設定する事も可能です:
[animFrames addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"mole_laugh1.png"]]; [animFrames addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"mole_laugh2.png"]]; [animFrames addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"mole_laugh3.png"]]; [animFrames addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"mole_laugh2.png"]]; // And so on... |
でもこの方法ではコードが少し汚くなってしまいます。もう少しコードを整理するには、アニーメーションのイメージの定義をコードで行う代わりに、プロパティリストを使いましょう。
プロパティリスト
まだプロパティリストを使った事が無いと仰る方の為に少し解説すると、プロパティリストとはXcodeで作成出来る特別なファイルで、ArrayやDictionaryやStringやNumberなどを格納する事が出来、作成も読み込みもとても簡単なファイルです。
どのくらい簡単かXcodeで試してみましょう。Resources,を右クリックして“AddNew File…”を選択、そして“Mac OS XResourceProperty List”を選択して“Next”をクリック。ファイル名を “laughAnim.plist”として “Finish”をクリック。そうすると以下の様にlaughAnim.plistが表示されるはずです:
全てのプロパティリストはRoot要素を持っていて、これは大抵はArrayかDictionaryです。このプロパティリストは、笑うアニメーションを作成する為のイメージ名をArrayに格納します。Root要素の2番目のコラムをクリックして”Type”のDictionaryをArrayに変更。
次は右端にある3本線のボタンをクリックして、新しいエントリーをArrayに追加します。デフォルトでは”Type”のエントリーは“String”になっています。最初のエントリー“Value”を“mole_laugh1.png”に変更して下さい。
+ボタンをクリックして新しい行を追加します。以下の様に、以上の法方を繰り返して行をアニメーションのフレーム数追加して設定して下さい:
次は上記の方法をリピートしてモグラが叩かれた時のアニメーションフレームを作成しましょう。プロパティリスト名は“hitAnim.plist”として下さい。
それでは、これらのアニメーションを読み込む為のコードを追加しましょう。“HelloWorldScene.h”を開いて、それぞれのアニメーション用のメンバ変数を以下の様に追加しましょう:
// Inside @interface HelloWorld CCAnimation *laughAnim; CCAnimation *hitAnim; |
これらは、それぞれのCCAnimationを参照する為で、後に何度でも再利用する事が出来ます。
次はプロパティリストに定義したイメージを基に“CCAnimation”を作成するメソッドを追加します:
- (CCAnimation *)animationFromPlist:(NSString *)animPlist delay:(float)delay { NSString *plistPath = [[NSBundle mainBundle] pathForResource:animPlist ofType:@"plist"]; // 1 NSArray *animImages = [NSArray arrayWithContentsOfFile:plistPath]; // 2 NSMutableArray *animFrames = [NSMutableArray array]; // 3 for(NSString *animImage in animImages) { // 4 [animFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:animImage]]; // 5 } return [CCAnimation animationWithFrames:animFrames delay:delay]; // 6 } |
このパートを理解する事は非常に大切なので、少しずつ解説していきましょう:
- プロパティリストは、プロジェクトファイルに含まれているので、アプリのメインバンドルであり、このヘルパーメソッドは、メインバンドル内の読み込むプロパティリストまでのパスを取得します。
- プロパティリストの読み込みは、NSArrayの“arrayWithContentsOfFile”メソッドを呼び出し、プロパティリストへのパスを渡せば良いんです。そして“NSArray”(ここではアニメーションのイメージ名の文字列のリスト)を返します。プロパティリストでRootをNSArrayに設定したことを思い出して下さい。代わりに“NSDictionary”に設定していたら、 [NSDictionary dictionaryWithContentsOfFile...]を使います。
- アニメーションのフレームを格納する為に、 空のArrayを作成します。
- プロパティリストからループでそれぞれのイメージ名を読み。
- それぞれのイメージのスプライトフレームを取得して、Arrayに追加。
- スプライトフレームのArrayを基にCCAnimationを返します。
次に、それぞれのアニメーション実行の為のヘルパー関数をコールする為に、以下のコードをinitメソッドの最後に加えます。
laughAnim = [self animationFromPlist:@"laughAnim" delay:0.1]; hitAnim = [self animationFromPlist:@"hitAnim" delay:0.02]; [[CCAnimationCache sharedAnimationCache] addAnimation:laughAnim name:@"laughAnim"]; [[CCAnimationCache sharedAnimationCache] addAnimation:hitAnim name:@"hitAnim"]; |
それぞれのアニメーションの設定の後、アニメーションキャッシュに追加します。これはそれぞれのアニメーションを保存してretainする為に必要な手順で、後に必要ならば名前によってアニメーションキャッシュから検索する事が出来る様になる為です。
最後のステップは、アニメーション(今回は笑うモグラのアニメーションだけ)を実行してみましょう。popMoleメソッドを以下の様に変更して下さい。
- (void) popMole:(CCSprite *)mole { CCMoveBy *moveUp = [CCMoveBy actionWithDuration:0.2 position:ccp(0, mole.contentSize.height)]; CCEaseInOut *easeMoveUp = [CCEaseInOut actionWithAction:moveUp rate:3.0]; CCAction *easeMoveDown = [easeMoveUp reverse]; CCAnimate *laugh = [CCAnimate actionWithAnimation:laughAnim restoreOriginalFrame:YES]; [mole runAction:[CCSequence actions:easeMoveUp, laugh, easeMoveDown, nil]]; } |
違いは、モグラが引っ込む前に1秒間delayさせていましたが、代わりにCCAnimationを実行します。先ほど設定したCCAnimation アクションのlaughAnimをresotreOriginalFrameを“Yes”に設定して、アニメーションが終わったら、元のモグラのイメージに戻します。
それではコンパイル&ランして下さい。これで、モグラが飛び出すと、小生意気に笑うようになりましたね。
次に、その小生意気な笑いを叩いて消してやりましょう!
ゲームロジックの追加
ここで、ゲームに少々のゲームロジックを追加しましょう。考え方としてはモグラに点数を与えて、それぞれを叩くと得点が得られます。そして、ユーザーはポイントをどれだけ稼げるかを競います。
ですから、得点を常に把握しなければなりません。加えて、その得点をユーザーの為に表示して、そしてモグラ叩きゲームが終了した時、ゲーム終了をユーザーに伝えます。
それでは“HelloWorldScene.h”を開いて、以下のインスタンス変数をHelloWorldLayerに追加しましょう:
CCLabelTTF *label; int score; int totalSpawns; BOOL gameOver; |
これらは、スコアラベル、現在のスコア、何匹のモグラが飛び出たかをゲームの終了に関わらず把握します。
次に以下をinitメソッドの最後に追加して下さい:
self.isTouchEnabled = YES; float margin = 10; label = [CCLabelTTF labelWithString:@"Score: 0" fontName:@"Verdana" fontSize:[self convertFontSize:14.0]]; label.anchorPoint = ccp(1, 0); label.position = ccp(winSize.width - margin, margin); [self addChild:label z:10]; |
まず始めにスクリーンをユーザーのタッチを検知する為に、レイヤーのタッチ検知をオンに設定します。そしてスコアを表示する為のラベルを作成し、右下に配置したいので、ラベルのアンカーポイントを右下に設定しします。
加えて、直接フォントサイズを渡すよりも、ヘルパー関数でフォントサイズを変換している事に注意して下さい。何故なら、iPad用のスクリンサイズに対応する様にフォントサイズを拡大する必要があるからです。以下のconvertFontSizeを実装して下さい:
- (float)convertFontSize:(float)fontSize { if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { return fontSize * 2; } else { return fontSize; } } |
ここでは難しい事は何もありませんね。iPad用の為にフォントサイズを2倍にしています。iPad用でなければ通常サイズが返されます。
次にモグラが叩かれた時の為にタッチ検知のコードを追加しましょう。しかしその前に、モグラに今現在叩く事が可能かどうかのフラグを追加する必要があります。モグラを叩く事が出来るのはモグラが笑っている時だけで、モグラが地面隠れている時や動いている最中は叩く事が出来ない様にします。
その為に、CCSpriteのサブクラスを作成する事も出来ますが、格納する情報がこの1つだけになるので代わりに、userDataプロパティを使います。以上の理由から、以下の様に2つのヘルパー関数を追加して、popMoleを変更してください:
- (void)setTappable:(id)sender { CCSprite *mole = (CCSprite *)sender; [mole setUserData:TRUE]; } - (void)unsetTappable:(id)sender { CCSprite *mole = (CCSprite *)sender; [mole setUserData:FALSE]; } - (void) popMole:(CCSprite *)mole { if (totalSpawns > 50) return; totalSpawns++; [mole setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"mole_1.png"]]; // Pop mole CCMoveBy *moveUp = [CCMoveBy actionWithDuration:0.2 position:ccp(0, mole.contentSize.height)]; CCCallFunc *setTappable = [CCCallFuncN actionWithTarget:self selector:@selector(setTappable:)]; CCEaseInOut *easeMoveUp = [CCEaseInOut actionWithAction:moveUp rate:3.0]; CCAnimate *laugh = [CCAnimate actionWithAnimation:laughAnim restoreOriginalFrame:YES]; CCCallFunc *unsetTappable = [CCCallFuncN actionWithTarget:self selector:@selector(unsetTappable:)]; CCAction *easeMoveDown = [easeMoveUp reverse]; [mole runAction:[CCSequence actions:easeMoveUp, setTappable, laugh, unsetTappable, easeMoveDown, nil]]; } |
popMoleで変更された事は:
- モグラが笑う直前にCCCallFuncアクションを実行してsetTappableをコールします。このメソッドは、スプライトのuserDataプロパティを“TRUE”に設定して、モグラを叩く事が出来る様にします。
- 同様に、モグラが笑った後CCCallFuncアクションで、unsetTappableを呼んで、userDataプロパティを“FALSE”に戻します。
- このメソッドはspawnの実行が50回を超えるとすぐにreturnします。
- スプライトのイメージをベースイメージである“mole_1.png”にリセットします。
これでモグラを叩く事が出来るかどうかのuserDataフラグをスプライトが表示する事が出来る様になりました。最後に以下の様にタッチ検知のコードを追加してください:
-(void) registerWithTouchDispatcher { [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:kCCMenuTouchPriority swallowsTouches:NO]; } -(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event { CGPoint touchLocation = [self convertTouchToNodeSpace:touch]; for (CCSprite *mole in moles) { if (mole.userData == FALSE) continue; if (CGRectContainsPoint(mole.boundingBox, touchLocation)) { mole.userData = FALSE; score+= 10; [mole stopAllActions]; CCAnimate *hit = [CCAnimate actionWithAnimation:hitAnim restoreOriginalFrame:NO]; CCMoveBy *moveDown = [CCMoveBy actionWithDuration:0.2 position:ccp(0, -mole.contentSize.height)]; CCEaseInOut *easeMoveDown = [CCEaseInOut actionWithAction:moveDown rate:3.0]; [mole runAction:[CCSequence actions:hit, easeMoveDown, nil]]; } } return TRUE; } |
“registerWithTouchDispatcher”メソッドで、それぞれのタッチを検出する度に“ccTouchBegan”コードが呼ばれる様に設定されます。これらのタッチ検知に関する詳細はCocos2Dでタイルベースゲームの作成チュートリアルで解説されています。参考にしてください。
“ccTouchBegan”メソッドはタッチをレイヤー座標に変換し、それぞれのモグラをループします。もし、モグラがタッチ可能ではない(userDataが“FALSE”の時)なら、次のモグラへスキップします。タッチが可能(userDataが“TRUE”の時)なら、“CGRectContainsPoint”を使ってタッチされたポイントがモグラのスプライトの領域“bounding box”内かどうかを確認します。
一度モグラが叩かれたら、もぐらをそれ以上叩く事の出来ない様に設定し、スコアを追加します。そして、アニメーションを一度全てストップして、“hit”アニメーションを実行し、モグラをすぐに穴の中に戻します。
最後のステップは、スコアのアップデートとレベル達成の条件の為のコードを“tryPopMoles”に追加します。:
if (gameOver) return; [label setString:[NSString stringWithFormat:@"Score: %d", score]]; if (totalSpawns >= 50) { CGSize winSize = [CCDirector sharedDirector].winSize; CCLabelTTF *goLabel = [CCLabelTTF labelWithString:@"Level Complete!" fontName:@"Verdana" fontSize:[self convertFontSize:48.0]]; goLabel.position = ccp(winSize.width/2, winSize.height/2); goLabel.scale = 0.1; [self addChild:goLabel z:10]; [goLabel runAction:[CCScaleTo actionWithDuration:0.5 scale:1.0]]; gameOver = true; return; } |
出来ました!完成です!コンパイル&ランしてください。モグラを叩く事が出来てスコアが追加される様になりましたね。それでは遊んでみてください!
サウンドエフェクトの追加
いつもの様にゲームにはサウンドが欠かせません!私が“Garage Band”で作ったサウンドエフェクトをダウンロードして“Resources”フォルダに追加してください。 “Copy items into destination group’s folder”にチェックを入れて“Add”をクリックしてください。
“HelloWorldScene.m”を以下に書き換えて下さい:
// Add to top of file #import "SimpleAudioEngine.h" // Add at the bottom of your init method [[SimpleAudioEngine sharedEngine] preloadEffect:@"laugh.caf"]; [[SimpleAudioEngine sharedEngine] preloadEffect:@"ow.caf"]; [[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"whack.caf" loop:YES]; // Add at bottom of setTappable [[SimpleAudioEngine sharedEngine] playEffect:@"laugh.caf"]; // Add inside ccTouchBegan, inside the CGRectContainsPoint case [[SimpleAudioEngine sharedEngine] playEffect:@"ow.caf"]; |
コンパイル&ランして下さい。これで出来上がりです!お疲れさまでした!やりましたね!
次にお勧めのチュートリアルを教えてください!
このチュートリアルのサンプルコードはこちらからどうぞ!
これで今のところはこのシリーズは終わりです!でも、何だったら、このプロジェクトを色々弄ってみてください。アイデアは尽きないでしょう!
如何だったでしょうか?皆さんはこんな感じのCocos2Dチュートリアルはお好きですか?それとも通常のコンセプトチュートリアルがお望みでしょうか?コメントは以下のフォーラムからどうぞ!有り難うございました!:]
Cocos2Dでモグラたたきアプリ作成チュートリアル: Part 2/2 is a post from: Ray Wenderlich
The post Cocos2Dでモグラたたきアプリ作成チュートリアル: Part 2/2 appeared first on Ray Wenderlich.