[7:完成] cocos2d-iPhoneとSpriteBuilderでゲームを1週間で作ろう

SpriteBuilderとcocos2dを使ったゲーム開発もついに最終回です!長い間お疲れ様でした!

今回は、物理演算を使って当たり判定を導入し、ポイントのカウントもします。最後なので少し書くコードの量が多目ですが、スクリーンショットやサンプルコードを見ながら頑張って実装してみてください。

前回まで

  1. ソフトをインストール。背景画像を設置
  2. 主人公をつくる
  3. 物理演算を導入する
  4. 主人公と背景を動かす
  5. 主人公を飛ばす
  6. 障害物を設置する

当たり判定の実装

Obstacle.mにcollisionTypeを設定します。

 

[code language=”objc” title=”Obstacle.m”]
// CCBファイルからロード
– (void)didLoadFromCCB {
// 上のパイプの当たり判定を設定
_topPipe.physicsBody.collisionType = @"level";
_topPipe.physicsBody.sensor = TRUE;
// 下のパイプの当たり判定を設定
_bottomPipe.physicsBody.collisionType = @"level";
_bottomPipe.physicsBody.sensor = TRUE;
}
[/code]

ここで設定した「level」という名前は、パイプや地面といったキャラクターが触れたら死ぬオブジェクトすべてに適応します。

続いてMainScene.hを編集します。CCPhysicsCollisionDelegateを実装します。
このデリゲートはふたつのオブジェクトが衝突した際に呼ばれます。

[code language=”objc” title=”MainScene.h” highlight=”2″]
#import "CCNode.h"
@interface MainScene : CCNode <CCPhysicsCollisionDelegate>
@end
[/code]

先ほどObstacle.mでも上下のパイプに当たり判定の設定(@levelで指定)をしましたが、MainScene.mでも同様に主人公と地面に対して当たり判定設定を行います。
didLoadFromCCBに追記していきましょう。

[code language=”objc” title=”MainScene.m” highlight=”15,16,21,22,23,24″]
// CCBファイルからロード
– (void)didLoadFromCCB {
// タッチ操作可能にする。
self.userInteractionEnabled = TRUE;
// 地面その1とその2を配列に追加
_grounds = @[_ground1, _ground2];
// 最初の障害物を設置する
_obstacles = [NSMutableArray array];
[self spawnNewObstacle];
[self spawnNewObstacle];
[self spawnNewObstacle];

// 地面の表示順を変更する
for (CCNode *ground in _grounds) {
// 地面の当たり判定設定
ground.physicsBody.collisionType = @"level";
ground.zOrder = DrawingOrderGround;
}
_hero.zOrder = DrawingOrdeHero;

// デリゲートとして設定する
_physicsNode.collisionDelegate = self;
// 主人公の当たり判定設定
_hero.physicsBody.collisionType = @"hero";
}
[/code]

続いて、SpriteBuilderでの設定に移ります。
上下のパイプを選択し、物理演算を可能にします。また、Dynamic→Staticに変更します。

SpriteBuilder SpriteBuilder

最後に衝突時の処理を実装します。

 

[code language=”objc” title=”MainScene.m”]
// 衝突時の処理
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair hero:(CCNode *)hero level:(CCNode *)level {
NSLog(@"Game Over");
return TRUE;
}
[/code]

 

それではビルドして確認してみましょう。

FlappyFly 8

障害物に接触するか、地面に接触すると、「Game Over」とログを出します。

ゲームオーバー時の動作

障害物にあたった際の動きを確認できたので、今度はゲームオーバーとリスタートのしくみを実装します。実際のゲームでは、

  • 地面に落下
  • 画面が揺れる
  • リスタートボタンが表示される
  • リスタートボタンを押したら、ゲームがスタートする

SpriteBuilderで、リスタートボタンを設置します。

SpriteBuilder 8

コネクションとセレクタを設定します。
あとでリスタートボタンが押された時に反応するようXcode側でも設定します。

SpriteBuilder

 

最後に位置を中央に設定し、表記をRestartに変更します。そして、Visibleのチェックを外し、見えないようにします。

SpriteBuilder

リスタートボタンは、ゲームオーバーになった時に表示されるようにします。
Xcodeを開き、MainScene.mに追記していきます。

まずは変数を宣言します。

[code language=”objc” title=”MainScene.m” highlight=”9″]
@implementation MainScene{
CCSprite *_hero; // SpriteBuilderと接続する主人公のスプライト
CCPhysicsNode *_physicsNode; // SpriteBuilderと接続する全画面の物理ノード
CCNode *_ground1; // SpriteBuilderと接続する地面その1
CCNode *_ground2; // SpriteBuilderと接続する地面その2
NSArray *_grounds; // 地面ループ処理用のArray
NSTimeInterval _sinceTouch; // 最後にタッチしてからどれだけ経過したか
NSMutableArray *_obstacles; // 障害物を格納する配列
CCButton *_restartButton; // リスタートボタン
}
[/code]

つぎに、衝突時の処理をするメソッドに、リスタートボタンを表示させるように設定します。

[code language=”objc” title=”MainScene.m” highlight=”4,5″]
// 衝突時の処理
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair hero:(CCNode *)hero level:(CCNode *)level {
NSLog(@"Game Over");
// リスタートボタンを表示させる
_restartButton.visible = TRUE;
return TRUE;
}
[/code]

最後に、リスタートの処理を実装します。

[code]
– (void)restart {
CCScene *scene = [CCBReader loadAsScene:@"MainScene"];
[[CCDirector sharedDirector] replaceScene:scene];
}
[/code]

これでリスタートが実装できました。

FlappyFly

ただ、このままではゲームオーバー後に地を這いながら進んでしまっています。ゲームオーバー後はスクロールが止まるよう修正しましょう。

ゲームオーバーの判定をする変数と、スクロールスピードを格納する変数を新たに宣言します。

[code language=”objc” title=”MainScene.m” highlight=”10,11″]
@implementation MainScene{
CCSprite *_hero; // SpriteBuilderと接続する主人公のスプライト
CCPhysicsNode *_physicsNode; // SpriteBuilderと接続する全画面の物理ノード
CCNode *_ground1; // SpriteBuilderと接続する地面その1
CCNode *_ground2; // SpriteBuilderと接続する地面その2
NSArray *_grounds; // 地面ループ処理用のArray
NSTimeInterval _sinceTouch; // 最後にタッチしてからどれだけ経過したか
NSMutableArray *_obstacles; // 障害物を格納する配列
CCButton *_restartButton; // リスタートボタン
BOOL _gameOver; // ゲームオーバーかどうか
CGFloat _scrollSpeed; // スクロールスピード
}
[/code]

MainScene.mでscrollSpeedと書いた箇所はすべて_scrollSpeedに置き換えます。
また、定数宣言をコメントアウトします。

[code language=”objc” title=”MainScene.m” highlight=”1″]
//static const CGFloat scrollSpeed = 80.f; // スクロール速度の定数を定義
static const CGFloat firstObstaclePosition = 280.f; // 最初の障害物の位置
static const CGFloat distanceBetweenObstacles = 160.f; // 障害物と次の障害物の距離
[/code]

さらに、didLoadFromCCBにスクロールスピードを追記します。

[code language=”objc” title=”MainScene.m” highlight=”6,7″]
// CCBファイルからロード
– (void)didLoadFromCCB {
// タッチ操作可能にする。
self.userInteractionEnabled = TRUE;
// 略…

// スクロールスピードを設定
_scrollSpeed = 80.f;
}
[/code]

続いてゲームオーバー時に実行されるメソッドを作成しましょう。

[code]
// ゲームオーバーになった時に実行する
– (void)gameOver {
if (!_gameOver) {
// スクロールを泊める
_scrollSpeed = 0.f;
// ゲームオーバーのフラグを立てる
_gameOver = TRUE;
// ボタンを見えなくする
_restartButton.visible = TRUE;
_hero.rotation = 90.f;
_hero.physicsBody.allowsRotation = FALSE;
[_hero stopAllActions];
CCActionMoveBy *moveBy = [CCActionMoveBy actionWithDuration:0.2f position:ccp(-2, 2)];
CCActionInterval *reverseMovement = [moveBy reverse];
CCActionSequence *shakeSequence = [CCActionSequence actionWithArray:@[moveBy, reverseMovement]];
CCActionEaseBounce *bounce = [CCActionEaseBounce actionWithAction:shakeSequence];
[self runAction:bounce];
}
}
[/code]

そして、衝突時のメソッドを書き換えます

[code language=”objc” title=”MainScene.m” highlight=”3,4,5,6,7,8″]
// 衝突時の処理
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair hero:(CCNode *)hero level:(CCNode *)level {
// NSLog(@"Game Over");
// // リスタートボタンを表示させる
// _restartButton.visible = TRUE;
// ゲームオーバー処理を実行
[self gameOver];
return TRUE;
}
[/code]

ゲームオーバー時にはタッチに反応しないように修正します。

[code language=”objc” title=”MainScene.m” highlight=”3,4,11″]
// 画面をタッチした時の挙動
– (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
// ゲームオーバー時にはタッチに反応しない
if (!_gameOver){
// 力積を与える
[_hero.physicsBody applyImpulse:ccp(0, 400.f)];
// 角力積を設定
[_hero.physicsBody applyAngularImpulse:10000.f];
// いまタッチしたので、経過時間を0にする
_sinceTouch = 0.f;
}
}
[/code]

これで、ゲームオーバー時にストップするようになりました。

 

スコアを表示して完成!

いよいよ完成です。最後に、スコアを表示するようにしましょう。

SpriteBuilderで画面上にラベルを設置します。位置を調整して、表記を「0」に、サイズを50にします。

LabelTTF

 

続いて、コネクションの設定をします。_scoreLabelと記入して下さい。

_scoreLabel

 

さらにObstacleを開き、パイプとパイプの間においてあるノードに対して、カスタムクラスとしてGoalと名前をつけます。

スクリーンショット 2014-06-29 18.36.35

 

さらに、物理演算を可能にして、Staticにチェックを入れます。

 

Static

続いてXcodeに移り、Goalクラスを作ります。

スクリーンショット 2014-06-29 19.09.01 スクリーンショット 2014-06-29 19.09.33 スクリーンショット 2014-06-29 19.09.46 スクリーンショット 2014-06-29 19.10.03

Goal.mにメソッドを追加します。

 

[code language=”objc” title=”Goal.m”]
// CCBファイルからロード
– (void)didLoadFromCCB {
self.physicsBody.collisionType = @"goal";
self.physicsBody.sensor = TRUE;
}
[/code]

 

続いて、MainScene.mに変数を追加します。

[code language=”objc” title=”MainScene.m” highlight=”4,5″]
@implementation MainScene{
CCSprite *_hero; // SpriteBuilderと接続する主人公のスプライト
// 略…
NSInteger _points; // ポイントをカウント
CCLabelTTF *_scoreLabel; // ポイント表示用のラベル
}
[/code]

そして、これでほんとうに最後。ゴール時のメソッドに追記をします。

[code language=”objc” title=”MainScene.m” highlight=”4,5″]
// ゴールした時に実行する
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair hero:(CCNode *)hero goal:(CCNode *)goal {
[goal removeFromParent];
_points++;
_scoreLabel.string = [NSString stringWithFormat:@"%d", _points];
return TRUE;
}
[/code]

おつかれさまです!以上でFlappuFlyの完成です!
FlappyFly 8

 

 

SpriteBuilderとcocos2dを使ったiphoneアプリ開発講座、いかがでしたでしょうか。

私も現在、SpriteBuilderを使ってゲームアプリを開発しています。簡単ツールを使って、iPhoneアプリをじゃんじゃん量産しましょう!

 

“[7:完成] cocos2d-iPhoneとSpriteBuilderでゲームを1週間で作ろう” への1件の返信

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です