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

_scoreLabel

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アプリをじゃんじゃん量産しましょう!

 

[6:障害物] cocos2d-iPhoneとSpriteBuilderでゲームを1週間で作ろう

Flappy Fly 7

SpriteBuilderをつかって、障害物を設置します。さらに、障害物の上下の高さをランダムにして出現させます。みためだけだと、ほぼ完成と言えるような状態になります。

前回まで

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

SpriteBuilderで障害物オブジェクトを作る

SpriteBuilderで新しくファイルを作ります。 Nodeを選択し、名前はObstacle.ccbとします。         SpriteBuilder SpriteBuilder   続いて、作ったObstacle.ccsに画像やNodeを追加していきます。 まずはObstacle.ccsを開きましょう。(ダブルクリック) SpriteBuilder   続いて、画像を置いていきます。 SpriteBuilder 次に、CCNodeを設置します。 位置やサイズはこの後に設定するので、とりあえず置いてみてください。 SpriteBuilder サイズを調整します。 まずはRootとなるCCNodeのサイズを調整します。(最後に追加したCCNodeではありません。) 幅80, 高さ568と指定します。 CCNode   次に、pipe_topの位置を調整します。 Positionを左上に指定し、Xを50「%」、Yを128に指定します。 Anchor pointのx,yを0.50と0.00にします。 Top 同様にpipe_bottomの位置も調整します。 Positionを左上に指定し、Xを50「%」、Yを228に指定します。 Anchor pointのx,yを0.50と1.00にします。 pipe_bottom 最後にカウント用のCCNodeの設定です。 Positionを左上に指定し、Xを22、Yを574に指定し、サイズを幅36と高さ790にします。 SpriteBuilder   それではこのObstacle.ccbを保存し、MainScene.ccbの上に設置してみましょう。 Obstacle.ccb 上図のように設置できることが確認できたら、設置した障害物を一度削除します。障害物の設置は、SpriteBuilder上ではなく、Xcodeで行います。 それではSpriteBuilderでPublishしたあと、Xcodeに移りましょう。 障害物を格納するための配列を作成します。

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

続いて、定数を追加します。

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

最初の障害物の座標と、障害物と障害物の間の距離を定義します。 続いて、障害物を生成するメソッドを作成します。

[code language=”objc” title=”MainScene.m”]
// 障害物を生成する
– (void)spawnNewObstacle {
CCNode *previousObstacle = [_obstacles lastObject];
CGFloat previousObstacleXPosition = previousObstacle.position.x;
if (!previousObstacle) {
// これが一番最初の障害物オブジェクトになります
previousObstacleXPosition = firstObstaclePosition;
}
// CCBファイルから障害物オブジェクトを読み込み、生成します
CCNode *obstacle = [CCBReader load:@"Obstacle"];
obstacle.position = ccp(previousObstacleXPosition + distanceBetweenObstacles, 0);
[_physicsNode addChild:obstacle];
[_obstacles addObject:obstacle];
}
[/code]

CCBファイルを読み込んで障害物オブジェクトを生成し、先ほど定義した距離の分だけ離して配置しています。 さて、障害物生成メソッドは作成したので、最初の障害物だけ設置しましょう。 didLoadFromCCBに以下を追加します。

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

[/code]

これで3つの障害物を設置することが出来ました。 Flappy fly 7 それでは、障害物が永遠に出続けるようにアップデートメソッドを更新します。

[code language=”objc” title=”MainScene.m”]
// アップデート
– (void)update:(CCTime)delta {
// 主人公のX座標を右に進める
_hero.position = ccp(_hero.position.x + delta * scrollSpeed, _hero.position.y);
// 略…

// 障害物の新規作成
// 画面外に出た障害物を格納する配列
NSMutableArray *offScreenObstacles = nil;
// _obstaclesの中にある障害物のうち、左の画面外に出たものをoffScreenObstaclesに格納する
for (CCNode *obstacle in _obstacles) {
CGPoint obstacleWorldPosition = [_physicsNode convertToWorldSpace:obstacle.position];
CGPoint obstacleScreenPosition = [self convertToNodeSpace:obstacleWorldPosition];
if (obstacleScreenPosition.x < -obstacle.contentSize.width) {
if (!offScreenObstacles) {
offScreenObstacles = [NSMutableArray array];
}
[offScreenObstacles addObject:obstacle];
}
}
// offScreenObstaclesに入っている障害物を親ノードから取り除く
for (CCNode *obstacleToRemove in offScreenObstacles) {
[obstacleToRemove removeFromParent];
[_obstacles removeObject:obstacleToRemove];
// 障害物を取り除いたら、新しい障害物を生成する
[self spawnNewObstacle];
}
}
[/code]

Flappy Fly 7

障害物の位置をランダムにする

障害物をX軸上に規則的に配置することができたので、今度は障害物のY軸の位置がランダムになるようにしましょう。 そのためには障害物のクラスを作成する必要があります。 SpriteBuilderのObstacle.ccbのCustom ClassにObstacleと記述します。 Obstacle.ccb さらに、Code Connectionの設定も行いましょう。 上側のパイプは_topPipe、下側のパイプは_bottomPipeとします。 Top Pipe Bottom Pipe   保存してPublishしたら、Xcodeでの作業に移ります。 Xcodeでは、新しいクラスを作成します。CCNodeのサブクラスとしてObstacleを作成しましょう。 Xcode Xcode Xcode Obstacleが作成できました。 Xcode 続いて実装をしていきましょう。 3.5インチでも4インチでも大丈夫なよう定数を調整しています。

[code language=”objc” title=”Obstacle.m”]
#import "Obstacle.h"

@implementation Obstacle {
CCNode *_topPipe; // 上のパイプ
CCNode *_bottomPipe; // 下のパイプ
}
#define ARC4RANDOM_MAX 0x100000000
// 上のパイプの最小値。3.5インチiPhoneにも考慮しています
static const CGFloat minimumYPositionTopPipe = 128.f;
// 下のパイプの最大値。3.5インチiPhoneにも考慮しています
static const CGFloat maximumYPositionBottomPipe = 440.f;
// 上下のパイプの間の距離
static const CGFloat pipeDistance = 142.f;
// 上のパイプの範囲を計算する
static const CGFloat maximumYPositionTopPipe = maximumYPositionBottomPipe – pipeDistance;
// 障害物の位置をランダムに設定
– (void)setupRandomPosition {
// 乱数は0.f〜1.fの範囲で変動
CGFloat random = ((double)arc4random() / ARC4RANDOM_MAX);
CGFloat range = maximumYPositionTopPipe – minimumYPositionTopPipe;
// 上のパイプの位置を指定
_topPipe.position = ccp(_topPipe.position.x, minimumYPositionTopPipe + (random * range));
// 下のパイプの位置を指定
_bottomPipe.position = ccp(_bottomPipe.position.x, _topPipe.position.y + pipeDistance);
}
@end
[/code]

Obstacle.hにもメソッドの宣言をします。

[code language=”objc” highlight=”5″ title=”Obstacle.h”]
#import <Foundation/Foundation.h>
#import "cocos2d.h"
@interface Obstacle : CCNode {
}
– (void)setupRandomPosition;
@end
[/code]

最後にMainScene.mを修正しましょう。 障害物を生成する際に、位置がランダムになるよう修正します。 まずはクラスをインポートします。

[code language=”objc” title=”MainScene.m” highlight=”2″]
#import "MainScene.h"
#import "Obstacle.h" // 障害物クラスをインポート
[/code]

続いてメソッドに書き加えましょう。

[code language=”objc” title=”MainScene.m” highlight=”10, 12,13″]
// 障害物を生成する
– (void)spawnNewObstacle {
CCNode *previousObstacle = [_obstacles lastObject];
CGFloat previousObstacleXPosition = previousObstacle.position.x;
if (!previousObstacle) {
// これが一番最初の障害物オブジェクトになります
previousObstacleXPosition = firstObstaclePosition;
}
// CCBファイルから障害物オブジェクトを読み込み、生成します
Obstacle *obstacle = (Obstacle *)[CCBReader load:@"Obstacle"];
obstacle.position = ccp(previousObstacleXPosition + distanceBetweenObstacles, 0);
// 位置をランダムにします
[obstacle setupRandomPosition];
[_physicsNode addChild:obstacle];
[_obstacles addObject:obstacle];
}
[/code]

これで障害物の位置がランダムになりました。 FlappyFly 7

表示順を変更する

障害物が地面の前に出てしまっているので、コードを加えて表示順を調整します。 定数の宣言の下あたりに、以下のコードを加えます。

[code language=”objc” title=”MainScene.m”]
typedef NS_ENUM(NSInteger, DrawingOrder) {
DrawingOrderPipes,
DrawingOrderGround,
DrawingOrdeHero
};
[/code]

さらに、didLoadFromCCBにZ軸の表示順を追記します。

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

// 地面の表示順を変更する
for (CCNode *ground in _grounds) {
ground.zOrder = DrawingOrderGround;
}
_hero.zOrder = DrawingOrdeHero;
}
[/code]

最後にspawnNewobstacleを修正します。

[code language=”objc” title=”MainScene.m” highlight=”12,13″]
// 障害物を生成する
– (void)spawnNewObstacle {
CCNode *previousObstacle = [_obstacles lastObject];
CGFloat previousObstacleXPosition = previousObstacle.position.x;
if (!previousObstacle) {
// これが一番最初の障害物オブジェクトになります
previousObstacleXPosition = firstObstaclePosition;
}
// CCBファイルから障害物オブジェクトを読み込み、生成します
Obstacle *obstacle = (Obstacle *)[CCBReader load:@"Obstacle"];
obstacle.position = ccp(previousObstacleXPosition + distanceBetweenObstacles, 0);
// 表示順を変更します。
obstacle.zOrder = DrawingOrderPipes;
// 位置をランダムにします
[obstacle setupRandomPosition];
[_physicsNode addChild:obstacle];
[_obstacles addObject:obstacle];
}
[/code]

以上で表示順の設定が終わりました。

 

Flappy Fly 7

次回は当たり判定とゲームオーバー、リスタートの設定を行います。

 

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

[5:飛ばす] cocos2d-iPhoneとSpriteBuilderでゲームを1週間で作ろう

SpriteBuilder

いままで作りこんできた主人公をついに飛ばします。タッチ操作に合わせて、ぴょこぴょこ飛びます。また、ぴょこぴょこ飛ぶのに合わせて、キャラクターが上下に傾きます。今回でかなりゲームっぽくなりますよ。

前回まで

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

 

重力を変更

まずは重力を-100から-700へ変更します。 SpriteBuilder 保存してPublishしたらXcodeに移ります。

タッチしたら飛ぶようにコードを追加

didLoadFromCCBに”self.userInteractionEnabled = TRUE;”を追加して、タッチイベントを取るようにします。

[code language=”objc” title=”MainScene.m”]
// CCBファイルからロード
– (void)didLoadFromCCB {
// 地面その1とその2を配列に追加
_grounds = @[_ground1, _ground2];
// タッチ操作可能にする。
self.userInteractionEnabled = TRUE;
}
[/code]

つぎに、タッチした時の挙動を追加します。 以下のコードを追加し、touchBegan:withEvent:をオーバーライドします。

[code language=”objc” title=”MainScene.m”]
// 画面をタッチした時の挙動
– (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
// 力積を与える
[_hero.physicsBody applyImpulse:ccp(0, 400.f)];
}
[/code]

これでタッチするとピョンピョン飛ぶようになりました。 SpriteBuilder

速度上限を設定する

ビルドしてみるとわかると思いますが、素早く画面をタッチすると、主人公が画面外へ大きく飛び出てしまいます。こうならないために、微調整をする必要があります。

物理演算を使用する際はこのような微調整がとても大事です。

[code language=”objc” title=”MainScene.m”]
// アップデート
– (void)update:(CCTime)delta {
// 主人公のX座標を右に進める
_hero.position = ccp(_hero.position.x + delta * scrollSpeed, _hero.position.y);
// 略….

// 速度をclamp。y軸の速度が200.fを超えた場合は200.fにする。
float yVelocity = clampf(_hero.physicsBody.velocity.y, -1 * MAXFLOAT, 200.f);
_hero.physicsBody.velocity = ccp(0, yVelocity);
}
[/code]

clampとは値を一定の範囲内に収める操作を言います。今回の場合は、上限を200.fと定めて、それ以上になってしまった場合は200.fとするようにして、_hero.physicsBody.velocityのyに代入しています。xは手動で設定しているので0で良いです。

主人公を回転させる

いまは主人公が水平のまま上昇したり下降したりしていますが、躍動感を加えるために主人公を回転させてみましょう。

  • タッチしたら上向きに回転
  • 何も触らなければ下向きに回転
  • 回転する角度には上限を設ける

まずは、最後にタッチした時間からどれだけ経っているかを記録する変数(NSTimeInterval _sinceTouch)を作ります。

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

続いて、タッチした時のメソッドに追記します。

[code language=”objc” title=”MainScene.m”]
// 画面をタッチした時の挙動
– (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
// 力積を与える
[_hero.physicsBody applyImpulse:ccp(0, 400.f)];
// 角力積を設定
[_hero.physicsBody applyAngularImpulse:10000.f];
// いまタッチしたので、経過時間を0にする
_sinceTouch = 0.f;
}
[/code]

AngularImpulseの値を大きくすると、角度の変化が素早くなります。

さて、最後にupdateメソッドを更新します。
角度の範囲を指定し、タッチしていない時は下向きに回転するようにします。

[code language=”objc” title=”MainScene.m”]
// アップデート
– (void)update:(CCTime)delta {
// 主人公のX座標を右に進める
_hero.position = ccp(_hero.position.x + delta * scrollSpeed, _hero.position.y);
// 略….

// 主人公の角度を変える
// 経過時間を追加
_sinceTouch += delta;
// 角度を上限(90度)と下限(-30度)の中に収める
_hero.rotation = clampf(_hero.rotation, -30.f, 90.f);
// 回転が可能かチェック。(死んだときは回転ができなくなっている)
if (_hero.physicsBody.allowsRotation) {
// 角速度を設定
float angularVelocity = clampf(_hero.physicsBody.angularVelocity, -2.f, 1.f);
_hero.physicsBody.angularVelocity = angularVelocity;
}
// 経過時間が0.5秒を超えると、急速に下に向くようにする。
if ((_sinceTouch > 0.5f)) {
[_hero.physicsBody applyAngularImpulse:-40000.f*delta];
}
}
[/code]

最初に_sinceTouchに時間を加えて、タッチしてからの経過時間を更新します。

次に、回転が可能かどうかをチェックします。あとで設定をしますが、死んだ時は回転が不可能なため。回転可能なとき、-30度から90度の範囲で回転できます。

最後に、経過時間が0.5秒以上だった場合は、急速に下向きに回転するようにします。

これで回転の設定は終わりました。ビルドしてみましょう。

SpriteBuilder

だいぶゲームっぽくなりました!

次回は障害物を設置します。

[6:障害物] cocos2d-iPhoneとSpriteBuilderでゲームを1週間で作ろう

[4:動かす] cocos2d-iPhoneとSpriteBuilderでゲームを1週間で作ろう

SpriteBuilder

今回は、キャラクターの動きに合わせて背景を動かします。

これまでSpriteBuilderで配置した主人公や背景を、ついに動かします。
今回はcocos2dのコードも書きます。といっても普通に開発する場合と比べるとコードを書く量はかなり少ないですが。

地面の無限ループなども実装します。

前回まで

  1. ソフトをインストール。背景画像を設置
  2. 主人公をつくる
  3. 物理演算を導入する

 

主人公を動かす

SpriteBuilderで設定したオブジェクトとXcodeで書くコードをつなげます。

主人公のノードを選択し、右カラムの左から2番目のタブを選択肢ます。

この画面でXcode上のコードと接続します。プルダウンで「Doc root var」を選択して、右には「_hero」と記述します。

SpriteBuilder

保存をしてPublishボタン(左上のボタン)を押した後、Xcodeに移ります。

[code language=”objc” title=”MainScene.m”]
#import "MainScene.h"
static const CGFloat scrollSpeed = 80.f; // スクロール速度の定数を定義
@implementation MainScene{
CCSprite *_hero; // SpriteBuilderと接続する主人公のスプライト
}

// アップデート
– (void)update:(CCTime)delta {
// 主人公のX座標を右に進める
_hero.position = ccp(_hero.position.x + delta * scrollSpeed, _hero.position.y);
}
@end
[/code]

これで、主人公が動くようになります。

早速ビルドしてみましょう。

SpriteBuilder

起動と同時に主人公が右に飛んでいきます。

カメラの設定

続いて、主人公の動きに合わせてカメラが動くように設定します。

ただcocos2dにはカメラという機能はないので、主人公に合わせて地面などが動くように設定します。

まずSpriteBuilderのMainScene.ccbを開き、CCPhysicsNodeを選択します。
続いてCode ConnectionsタブでDoc root varを選択し、_physicsNodeと記述します。

SpriteBuilder

保存してPublishしたらSpriteBuilder側はこれで完了。

続いてはXcodeにコードを書いていきます。
@implementationの部分に以下のように追記します。これでSpriteBuilderの_physicsNodeとXcode側の接続が完了しました。

[code language=”objc” title=”MainScene.m”]
@implementation MainScene{
CCSprite *_hero; // SpriteBuilderと接続する主人公のスプライト
CCPhysicsNode *_physicsNode; // SpriteBuilderと接続する全画面の物理ノード
}
[/code]

続いて、画面と主人公の位置が連動するようにコードを書きます。

[code language=”objc” title=”MainScene.m”]
// アップデート
– (void)update:(CCTime)delta {
// 主人公のX座標を右に進める
_hero.position = ccp(_hero.position.x + delta * scrollSpeed, _hero.position.y);
// 主人公の座標に合わせて_physicsNode全体を逆方向(左方向)に動かす
_physicsNode.position = ccp(_physicsNode.position.x – (scrollSpeed *delta), _physicsNode.position.y);
}
@end
[/code]

これで画面が主人公を追いかけるようになりました。
まだ地面をループさせていないので、しばらくすると奈落へ落ちていってしまいますが。

Untitled3

地面をループさせる

主人公が地獄に落ちないよう、地面をループさせます。

まずはスプライトビルダーの表示設定を変更して、画面外も見られるようにしましょう。

Document > Stage Border > None

と選択し、画面境界の表示を消します。

SpriteBuilder Stage Border

これで画面外にスプライトを設置できるようになりました。

続いて、2番目の地面を画面上に設置します。ArtPackからground.pngをドラッグアンドドロップし、その後画像の位置を変更します。座標を(348, 12)に、アンカーポイントのX座標を0にします。

また、このときCCPhysicsNodeの子ノードに入っていない場合は、下画面でドラッグアンドドロップして、groundがCCPhysicsNodeの中に入っているようにしましょう。

SpriteBuilder

続いて、物理演算を可能にします。

Enable physicsにチェックを入れ、Staticを選択します。このあたりは1つ目の地面を設定した時と同じですね。

スクリーンショット 2014-06-08 19.31.17

次に、ループを実装するため、connectionを設定する必要があります。

最初に設定したground.pngのスプライトに「_ground1」、さきほど設定した方に「_ground2」と設定します。

SpriteBuilder

SpriteBuilder

これでSpriteBuilderは完了。保存してPublishしたら、Xcode側に移ります。

[code language=”objc” title=”MainScene.m”]
#import "MainScene.h"
static const CGFloat scrollSpeed = 80.f; // スクロール速度の定数を定義
@implementation MainScene{
CCSprite *_hero; // SpriteBuilderと接続する主人公のスプライト
CCPhysicsNode *_physicsNode; // SpriteBuilderと接続する全画面の物理ノード
CCNode *_ground1; // SpriteBuilderと接続する地面その1
CCNode *_ground2; // SpriteBuilderと接続する地面その2
NSArray *_grounds; // 地面ループ処理用のArray
}
// CCBファイルからロード
– (void)didLoadFromCCB {
// 地面その1とその2を配列に追加
_grounds = @[_ground1, _ground2];
}
// アップデート
– (void)update:(CCTime)delta {
// 主人公のX座標を右に進める
_hero.position = ccp(_hero.position.x + delta * scrollSpeed, _hero.position.y);
// 主人公の座標に合わせて_physicsNode全体を逆方向(左方向)に動かす
_physicsNode.position = ccp(_physicsNode.position.x – (scrollSpeed *delta), _physicsNode.position.y);

// 地面をループさせる
for (CCNode *ground in _grounds) {
// 地面のワールド座標を取得
CGPoint groundWorldPosition = [_physicsNode convertToWorldSpace:ground.position];
// 地面のスクリーン城の位置を取得
CGPoint groundScreenPosition = [self convertToNodeSpace:groundWorldPosition];
// ひとつの地面が画面の左端から完全に外れたら、それを右に移動させる。
if (groundScreenPosition.x <= (-1 * ground.contentSize.width)) {
ground.position = ccp(ground.position.x + 2 * ground.contentSize.width, ground.position.y);
}
}
}
@end
[/code]

これで地面が永遠につづくようになりました。

SpriteBuilder

次回は画面をタッチして主人公を飛ばします。

[5:飛ばす] cocos2d-iPhoneとSpriteBuilderでゲームを1週間で作ろう

[3:物理演算] cocos2d-iPhoneとSpriteBuilderでゲームを1週間で作ろう

CCPhysicsNode SpriteBuilder

今回は物理演算を実装します。

cocos2d v3では物理演算がデフォルトで入るようになりました。さらに、SpriteBuilderを使うと、ワンクリックで物理演算を導入できるようになります。これは本当にすごい。

前回まで

  1. ソフトをインストール。背景画像を設置
  2. 主人公をつくる

 

画面全体にCCPhysicsNodeを配置する

まずは画面全体に物理演算ノードを設置します。

MainSceneを開き左カラムの立方体のタブを開きます。

そこからPhysicsNodeという項目を画面下の「CCNode」の上にドラッグアンドドロップします。

CCPhysicsNode SpriteBuilder

続いて、画面全体をカバーするようサイズを変更します。

幅と高さを100%に変更します。

SpriteBuilder

地面に物理演算を導入する

地面のスプライトを選択し、右カラムの物理演算のタブ(四角が跳ねてるアイコン)を選択します。

ここで「Enable physics」を選択することで、対象ノードが物理演算可能となります。

また、ラジオボタンはStaticを選択します。

SpriteBuilder

続いて、groundをCCPhysicsNodeの子ノードにします。

groundをCCPhysicsNodeの上にドラッグアンドドロップすることで、子ノードにすることができます。

SpriteNode

SpriteNode

最後に、CCPhysicsNodeの表示順を変更します。
(すでにCCPhysicsNodeが最下部にある場合は変更する必要はないです。)

SpriteBuilder

主人公を設置する

ついに前回作った主人公を設置します。

Hero.ccbをMainSceneの画面上にドラッグ&ドロップします。
(ドラッグアンドドロップの前に、Hero.ccbを保存しておきましょう。)

SpriteBuilder

つぎに、追加した主人公のノードをCCPhysicsNodeの子ノードにします。

SpriteBuilder

続いて、対象物の物理演算にチェックを入れます。
地面とは異なり、Dynamicで良いです。

SpriteBuilder

最後に茂みを設置しましょう。
まずドラッグアンドドロップをして。

SpriteBuilder

続いて、位置を修正します。(左下、160,106)

さらに、表示順を変更します。
CCPhysicsNodeの上になるように。

SpriteBuilder

それではビルドしてみましょう!
左上のPublishボタンを押します。

SpriteBuilder Publish

SpriteBuilder publish

保存が済んでない場合は上記の表示が出てくるので、Save Allを押します。

今度はXcodeからビルドしてみましょう。
SpriteBuilderのプロジェクトが入っているフォルダを開き、プロジェクトファイルを開きます。

Xcode Project SpriteBuilder

ビルドします。

Xcode

シュミレーターではこんなかんじで表示されるはず。

Flappy Fly

というわけで今回はここまで!

次回

[4:動かす] cocos2d-iPhoneとSpriteBuilderでゲームを1週間で作ろうす

[2: 主人公] cocos2d-iPhoneとSpriteBuilderでゲームを1週間で作ろう

SpriteBuilder

前回までで背景の設置が終わりました。今回は主人公を設置します。

SpriteBuilderを使えばアニメーションを簡単に作れます。普通の開発ではコードを書いてコマ送りを実装ますが、SpriteBuilderでは、動作を見ながらアニメーションを作ることができます。

前回まで

  1. ソフトをインストール。背景画像を設置

 

主人公用のファイルを新規作成

スクリーンショット 2014-06-03 23.03.16

Heroという名前で、「Sprite」を選択してファイルを作成します。

SpriteBuilder

作成したファイルに画像を設定します。

  1. 作成したHero.ccbを選択
  2. CCSpriteを選択
  3. Sprite frameからfly1.pngを選択

SpriteBuilder

続いて、時間間隔を設定します。

Set Timeline Duration

10秒を1秒に変更します。

SpriteBuilder

主人公のアニメーションを作成

アニメーション作成のため、Sprite Frameを追加します。

Sprite Frame

タイムラインを動かしながら、SpriteFrameを6つ追加します。

SpriteFrame

つぎに、Sprite Frameを一つおきにfly2.pngに変更し、羽がパタパタするアニメーションを作成します。

Sprite Frame

最後に動画がループするように設定します。

SpriteBuilder

これで主人公のアニメーションが作成できました。

Sprite Builder アニメーション

 

次回

[3:物理演算] cocos2d-iPhoneとSpriteBuilderでゲームを1週間で作ろう

[1: インストール] cocos2d-iPhoneとSpriteBuilderでゲームを1週間で作ろう

Cocos2d-iPhone 3.0と SpriteBuilderでFlappyBirdっぽいゲームを作ります。

というか、こちらの記事の和訳+補足になります。

https://www.makegameswith.us/gamernews/369/build-your-own-flappy-bird-with-spritebuilder-and

FlappyFly 8

↑こういったアプリが作れます。

まずはインストール

まず以下の2つをDLし、インストールしましょう。

  • Cocos2d 3.0
  • SpriteBulider

両方共インストールが完了したら早速始めちゃいましょう。
参照元は「チュートリアルはやっておいてね」と書いていたのですが、無視します。

プロジェクトを作ろう

SpriteBuliderを起動して、プロジェクトを作ります。

新しいプロジェクトを作るFile > New > Project と選択して、

プロジェクトをデスクトップに保存「FlappyFly」という名前をつけて、デスクトップに保存。
(保存先はどこでも良いです。)

起動画面するとプロジェクトが作成されて、最初の画面が表示されます。

デフォルトの画面を消す

SpriteBuilderという文字と、その後ろの青い背景を消します。

スクリーンショット 2014-05-25 19.39.50

スクリーンショット 2014-05-25 19.40.05

これで何もなくなりました。
ちなみにオブジェクトを選択してdeleteボタン押しても消せます。

スクリーンショット 2014-05-25 19.42.44

画面を縦にする

横画面だと具合がわるいので、縦にします。

スクリーンショット 2014-05-25 19.45.53

File > ProjectSettings… を選択し、
Default scaling from: を 2x(iPhonehd)に、
Orientation: を Portraitにして
「Done」を押します。

スクリーンショット 2014-05-25 19.48.23

これで画面が縦になりました。

続いてはSpriteBuilderで背景画像を設置します。

画像をダウンロードする

こちらのページから画像をダウンロードします

https://s3.amazonaws.com/mgwu-misc/FlappyFlyArtPack.zip

スクリーンショット 2014-05-31 19.02.39
画像一覧

画像をプロジェクトに追加

ダウンロードして解凍したフォルダを、プロジェクトに追加します。
ドラッグアンドドロップで追加できます。

スクリーンショット 2014-05-31 19.03.31

背景画像を設置

追加した画像から、背景画像(background.png)を設置します。
左側の追加したフォルダを開き、ドラッグアンドドロップで画面上に画像を設置します。

スクリーンショット 2014-05-31 19.07.26

スクリーンショット 2014-05-31 19.09.35

位置の調整

設置した画像を選択すると、右側のバーに位置などの設定画面が出ます。
そこのPosition, AnchorPointを変更します。

スクリーンショット 2014-05-31 19.12.28

まずPositonは「Top-left」で、Xが「0.0」Yも「0.0」に、
Anchor Pointは、Xを「0.0」に、Yを「1.0」に設定します。

これで位置を整列できました。

Top-Left、つまり左上を基準として設定したわけですが、こうすることで、iPhone4でもiPhone5でも対応できるようになります。

iPhone4での見え方を確認したい場合はこちらの設定から確認できます。

スクリーンショット 2014-05-31 19.23.26

地面を設置

背景画像と同様に、地面も設置します。
ground.pngをドラッグアンドドロップします。

スクリーンショット 2014-05-31 20.46.13

その後、先程と同様に位置指定をします。

スクリーンショット 2014-05-31 20.50.24

PositionはBottom-leftでXが0.0、Yが12.0。
Anchor pointはXが0.00、Yが0.50。

同様に、雲も設置してみます。

スクリーンショット 2014-05-31 23.57.34

PositionはTop-leftでXが187.0、Yが134.0。

とりあえず保存

というわけで今日はここまで。

スクリーンショット 2014-05-31 23.59.50

次回

[2: 主人公] cocos2d-iPhoneとSpriteBuilderでゲームを1週間で作ろう

cocos2d-iPhone 2.x から3.0への移行 (v2 to v3)

cocos2d-xばかりで、cocos2d-iPhoneについてどこも書いてないので、メモしておきます。

参照元はこちら。
https://www.makegameswith.us/gamernews/359/cocos2d-30-a-brief-transition-guide

  • CCLayerがなくなった
  • タッチイベントの
  • CCMenueがなくなって、CCLayoutを使う

 

おっと、こちらに日本語で詳しく書かれていました。。。

http://the3rddevblog.blog.shinobi.jp/cocos2d%E3%83%A1%E3%83%A2/cocos2d%20v3%20-rc-%20%E3%82%92%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%81%8B%E3%82%89%E8%A6%B3%E5%AF%9F

【Cocos2d】Unknown register name ‘q0’ in asm というエラーが出た時

実機でテストしようとしたら以下の様なエラーが出てしまいました。

Unknown register name ‘q0’ in asm

そんなときの対処法。

__ARM_NEON__

↑このように書いてある箇所を↓のように変更します。

__ARM_NEON_7

mat4.cとneon_matrix_impl.cで1箇所ずつ書かれている箇所があるので、両方修正します。

参考元 : http://stackoverflow.com/questions/21510187/unknown-register-name-q0-in-asm

デバッグ用にスロー再生する cocos2d

デバッグ用にスロー再生したいとき。

cocos2dのテンプレートで作成したプロジェクトのAppDelegate.mのapplication:didFinishLaunchingWithOptions:の最後の方に以下の一文を入れます。(return YES;の前。)

[objc]
– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//【中略】

// 時間を5倍にする
[director_ scheduler].timeScale = 0.2;

return YES;
}
[/objc]

これで通常の5分の1のスロースピードで動きます。
timeScaleを2とかにすると、2倍の早さで動きます。