SpriteBuilderとcocos2dを使ったゲーム開発もついに最終回です!長い間お疲れ様でした!
今回は、物理演算を使って当たり判定を導入し、ポイントのカウントもします。最後なので少し書くコードの量が多目ですが、スクリーンショットやサンプルコードを見ながら頑張って実装してみてください。
前回まで
当たり判定の実装
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に変更します。
最後に衝突時の処理を実装します。
[code language=”objc” title=”MainScene.m”]
// 衝突時の処理
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair hero:(CCNode *)hero level:(CCNode *)level {
NSLog(@"Game Over");
return TRUE;
}
[/code]
それではビルドして確認してみましょう。
障害物に接触するか、地面に接触すると、「Game Over」とログを出します。
ゲームオーバー時の動作
障害物にあたった際の動きを確認できたので、今度はゲームオーバーとリスタートのしくみを実装します。実際のゲームでは、
- 地面に落下
- 画面が揺れる
- リスタートボタンが表示される
- リスタートボタンを押したら、ゲームがスタートする
SpriteBuilderで、リスタートボタンを設置します。
コネクションとセレクタを設定します。
あとでリスタートボタンが押された時に反応するようXcode側でも設定します。
最後に位置を中央に設定し、表記をRestartに変更します。そして、Visibleのチェックを外し、見えないようにします。
リスタートボタンは、ゲームオーバーになった時に表示されるようにします。
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]
これでリスタートが実装できました。
ただ、このままではゲームオーバー後に地を這いながら進んでしまっています。ゲームオーバー後はスクロールが止まるよう修正しましょう。
ゲームオーバーの判定をする変数と、スクロールスピードを格納する変数を新たに宣言します。
[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にします。
続いて、コネクションの設定をします。_scoreLabelと記入して下さい。
さらにObstacleを開き、パイプとパイプの間においてあるノードに対して、カスタムクラスとしてGoalと名前をつけます。
さらに、物理演算を可能にして、Staticにチェックを入れます。
続いてXcodeに移り、Goalクラスを作ります。
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]
SpriteBuilderとcocos2dを使ったiphoneアプリ開発講座、いかがでしたでしょうか。
私も現在、SpriteBuilderを使ってゲームアプリを開発しています。簡単ツールを使って、iPhoneアプリをじゃんじゃん量産しましょう!
二日で完成しました!とても分かりやすくて良かったです。