cocos2d-x SimpleGame ソースコード分析

スクリーンショット 2014-08-20 20.21.59
cocos2d-xのSampleのSimpleGameが分かれば、cocos2d-xの7割が理解できるだそうです。
ですから、まずはがんばってこのSampleを理解しましょう。

0.ゲームの詳細

まずはゲームを実行して、どういうゲームなのかを見る。
スクリーンショット 2014-08-19 18.40.28

  • 三つの物があって、Resourceフォルダに画像がある。
    • Player:プレイヤー
    • Projectile:弾丸
    • Target:敵
  • 基本ルール
    • Playerが画面左の真ん中にいって、画面をタップしてこの位置からProjectileを発射する、五つ当たったら勝利
    • Targetは右から左に移動、いずれの一個が左に到達した時点でゲーム失敗
    • 結果(勝利か失敗)が表示されたら、数秒後に最初から始まる。
  • 特徴
    • Projectileは連続発射できる、Playerの位置点とタップ点との直線が弾道
    • ProjectileがTargetに当たったら、TargetもProjectileも消える

1.三つのソースを理解する

github上には全部のソースがある。
https://github.com/cocos2d/cocos2d-x/tree/v2/samples/Cpp/SimpleGame/Classes

  • AppDelegate:監督が入っているファイル、アプリ起動の実装
  • HelloWorldScene:
    • 実はHelloWorldSceneというクラスがない、HelloWorldはLayerで、このLayerのstatic関数でSceneを作っている
    • さらに、このstatic関数でHelloWorld自分も作られている
  • GameOverScene:GameOverLayerとGameOverSceneの二つのクラスが入っている

2.ゲームアプリの起動からゲーム開始 (AppDelegate)

マクロで定義した文で、namespaceをインポートする。
しないと、cocos2dのクラスを使うときは「cocos2d::」から始めないといけない。

USING_NS_CC;  //using namespace cocos2d;

いよいよゲームアプリを起動する。

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    // 【監督Directorを呼び出す】
    CCDirector *pDirector = CCDirector::sharedDirector();
    
    // 【監督にOpenGLの画面を渡す】
    pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
    
    // 【画面の解像度を取得】
    CCSize screenSize = CCEGLView::sharedOpenGLView()->getFrameSize();
    CCSize designSize = CCSizeMake(480, 320);
    std::vector<std::string> searchPaths;
    
    // 【画面の高さが320以上の場合、高解像度hdと低解像度sdをベクタ末尾に追加】
    // 【以下の場合低解像度のみ】
    // 【そして、実際の解像度スケール係数を計算し、監督に渡す】
    if (screenSize.height > 320)
    {
        searchPaths.push_back("hd");
        searchPaths.push_back("sd");
        pDirector->setContentScaleFactor(640.0f/designSize.height);
    }
    else
    {
        searchPaths.push_back("sd");
        pDirector->setContentScaleFactor(320.0f/designSize.height);
    }
    
    // 【ファイルデータのツールクラスで、決まった解像度のフォルダ名を渡す】
    // 【インスタンスを作らなくて直接使うのは、CCFileUtilsがシングルトン】
    CCFileUtils::sharedFileUtils()->setSearchPaths(searchPaths);
    
    // 【機種を判定し、ゲーム画面の解像度を設定する】
    // 【三つ目の引数はResolutionPolicyという列挙型、具体的何だろうかまだ知らないが、iPhone5で古いアプリを実行するとき、両端に黒い部分と関係があるみたい】
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    CCEGLView::sharedOpenGLView()->setDesignResolutionSize(designSize.width, designSize.height, kResolutionShowAll);
#else
	CCEGLView::sharedOpenGLView()->setDesignResolutionSize(designSize.width, designSize.height, kResolutionNoBorder);
#endif

    // turn on display FPS
    // 【画面にFPSを表示する, Frame Per Secondsだっけ?一秒に何回画面を更新すること】
    pDirector->setDisplayStats(true);

    // set FPS. the default value is 1.0/60 if you don't call this
    // 【FPSを設定する、実際変更しても大丈夫なものですが】
    // 【1.0 / 5 に変えて見た、ゲームが途切れ途切れな感じになった】
    pDirector->setAnimationInterval(1.0 / 60);

    // create a scene. it's an autorelease object
    // 【シーンのインスタンスを作って、scene()はHelloWorldのstatic関数です】
    CCScene *pScene = HelloWorld::scene();

    // run
    // 【監督に渡して、実行させる】
    pDirector->runWithScene(pScene);

    return true;
}

このファイルにはまだ二つの関数があります。
ゲームが一時停止する時(iphoneのホームの動作)と一時停止から回復する時の処理です。

// This function will be called when the app is inactive. When comes a phone call,it's be invoked too
// 【EnterBackgroundの文字通り、バックグラウンドに転じるときの処理、例えば突然に電話がかかってきたなど】
void AppDelegate::applicationDidEnterBackground() {
    CCDirector::sharedDirector()->stopAnimation();
    // 【画面を一時停止】

    // if you use SimpleAudioEngine, it must be pause
    // CocosDenshion::SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic();
    // 【音楽も続くかどうか、たとえば一部のアプリが画面に表示されていない場合でも、音楽を聞ける】
}

// this function will be called when the app is active again
// 【EnterForegroundの文字通り、バックグラウンドから回復されたときの処理】
void AppDelegate::applicationWillEnterForeground() {
    CCDirector::sharedDirector()->startAnimation();
    // 【実際にこれをコメントしてみたところ、回復された後、弾丸も敵も動かなくなった】

    // if you use SimpleAudioEngine, it must resume here
    // CocosDenshion::SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic();
}

3.ゲームが開始 (HelloWorldScene前編)

HelloWorld::scene()が呼ばれたので、HelloWorldクラスからHelloWorldSceneファイルに入る

CCScene* HelloWorld::scene()
{
	CCScene * scene = NULL;
	do 
	{
		// 'scene' is an autorelease object
		// 【CCSceneクラスでsceneを作る】
		scene = CCScene::create();
		// 【作れなかったらここで処理中断。マクロで定義された:if (! scene) break;】
		CC_BREAK_IF(! scene);

		// 'layer' is an autorelease object
		// 【HelloWorldクラスでLayerを作る】
		HelloWorld *layer = HelloWorld::create();
		CC_BREAK_IF(! layer);

		// add layer as a child to scene
		// 【作ったレイヤーをシーンに入れる】
		scene->addChild(layer);
	} while (0);
	// 【不勉強ですが、今更でdo−whileのこんな使い方ゲット】
	// 【一つのコードブロックの途中で処理が中断でき、しかもフラグ変数が不要ということですね】

	// return the scene
	return scene;
}

上記コード中に、create()関数が二回出てきて、CCSceneのcreate()はAPIそのまま。
HelloWorld::create()の実装はマクロで定義され、実装コードを読むと、シーンと同じ方式のことが分かった。
マクロのそのまま貼っておく:

//HelloWorldScene.hファイルの一部
CREATE_FUNC(HelloWorld);

//マクロの定義:まずはコンストラクタでインスタンス化、そしてinit()を実行し、
//自動メモリ解放関数autorelease()を組み込む
#define CREATE_FUNC(__TYPE__) \
static __TYPE__* create() \
{ \
    __TYPE__ *pRet = new __TYPE__(); \
    if (pRet && pRet->init()) \
    { \
        pRet->autorelease(); \
        return pRet; \
    } \
    else \
    { \
        delete pRet; \
        pRet = NULL; \
        return NULL; \
    } \
}

ではレイヤーのinit()を読む:

bool HelloWorld::init()
{
	bool bRet = false;
	do 
	{
		//////////////////////////////////////////////////////////////////////////
		// super init first
		//////////////////////////////////////////////////////////////////////////
		// 【HelloWorldレイヤーの親クラスの関数で初期化、背景を白に設定】
		CC_BREAK_IF(! CCLayerColor::initWithColor( ccc4(255,255,255,255) ) );

		//////////////////////////////////////////////////////////////////////////
		// add your codes below...
		//////////////////////////////////////////////////////////////////////////

		// 1. Add a menu item with "X" image, which is clicked to quit the program.
		// 【ゲーム画面の右下のシャットダウン画像を挿入する処理、クリックするとアプリ終わり】

		// Create a "close" menu item with close icon, it's an auto release object.
		// 【シャットダウン画像を作る、同時にmenuCloseCallback()動作を入れる】
		// 【menuCloseCallback()が監督Directorの終了命令しかないので、説明を省略】
		CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
			"CloseNormal.png",
			"CloseSelected.png",
			this,
			menu_selector(HelloWorld::menuCloseCallback));
		CC_BREAK_IF(! pCloseItem);
        
		// Place the menu item bottom-right conner.
		// 【ボタンサイズと画面サイズを取り出し、ボタンを右下に置くための計算】
		CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
		CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();

		// 【位置を計算するだけで長いな〜見なかったことにしようw】
		// 【ccpはマクロ:	#define ccp(__X__,__Y__) cocos2d::CCPointMake((float)(__X__), (float)(__Y__))】
		pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width/2,
                                    origin.y + pCloseItem->getContentSize().height/2));

		// Create a menu with the "close" menu item, it's an auto release object.
		// 【CCMenuを作ると同時に、終了ボタンをメニューに入れる、メニューの位置も設定】
		CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
		pMenu->setPosition(CCPointZero);
		CC_BREAK_IF(! pMenu);

		// Add the menu to HelloWorld layer as a child layer.
		// 【thisはレイヤーみたいで、レイヤーにボタンのメニューを入れる、1は階層順】
		this->addChild(pMenu, 1);
		// 【Javaプログラマですが、JButtonを作ってJPanelに入れて、JPanelをJFrameに入れるみたいな感じですね】

		/////////////////////////////
		// 2. add your codes below...
		// 【プレイヤーの画像を作ってレイヤーに入れる実装、プレイヤーはCCSprite】
		CCSprite *player = CCSprite::create("Player.png", CCRectMake(0, 0, 27, 40) );
        
		player->setPosition( ccp(origin.x + player->getContentSize().width/2,
                                 origin.y + visibleSize.height/2) );
		this->addChild(player);

		// 【レイヤーを更新する関数をレイヤーに登録すると思ったが、gameLogic関数はaddTargetだけを呼び出し、敵を出す役割しかない】
		// 【1秒に一回、つまり1秒に1個の敵を描き出す】
		this->schedule( schedule_selector(HelloWorld::gameLogic), 1.0 );

		// 【レイヤーをタップ可能にする】
		this->setTouchEnabled(true);

		// 【いよいよ敵が来るのですね、弾丸も準備開始】
		_targets = new CCArray;
		_projectiles = new CCArray;

		// use updateGame instead of update, otherwise it will conflit with SelectorProtocol::update
		// see http://www.cocos2d-x.org/boards/6/topics/1478
		// 【ゲーム画面を更新する関数、頻度はFPSになっているようで】
		this->schedule( schedule_selector(HelloWorld::updateGame) );

		// 【ゲームBGM開始】
		CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("background-music-aac.wav", true);

		bRet = true;
	} while (0);

	return bRet;
}

gameLogic関数はaddTargetだけを呼び出すので、直接addTargetを見ていきます

void HelloWorld::addTarget()
{
	// 【一つ敵を作る、プレイヤーと同じようにCCSpriteです】
	CCSprite *target = CCSprite::create("Target.png", CCRectMake(0,0,27,40) );
    
	// Determine where to spawn the target along the Y axis
	// 【敵画像を画面外にはみ出さないように、縦軸Yの座標移動範囲を計算する、最大座標と最小座標そして範囲】
	CCSize winSize = CCDirector::sharedDirector()->getVisibleSize();
	float minY = target->getContentSize().height/2;
	float maxY = winSize.height -  target->getContentSize().height/2;
	int rangeY = (int)(maxY - minY);
	// srand( TimGetTicks() ); 【Sampleの最初からコメントされたコードです。】
	// 【ここで縦軸Yの座標移動値をランダムに出す】
	int actualY = ( rand() % rangeY ) + (int)minY;

	// Create the target slightly off-screen along the right edge,
	// and along a random position along the Y axis as calculated
	// 【敵の位置を決定し、レイヤーに入れる, 横軸Xは右端で、縦軸Yは上記で計算された移動値にプラス可視最小座標】
	target->setPosition( 
		ccp(winSize.width + (target->getContentSize().width/2), 
            CCDirector::sharedDirector()->getVisibleOrigin().y + actualY) );
	this->addChild(target);

	// Determine speed of the target
	// 【敵が画面上に存在する時間の長さを計算する、最短は2秒、最長は4秒】
	int minDuration = (int)2.0;
	int maxDuration = (int)4.0;
	int rangeDuration = maxDuration - minDuration;
	// srand( TimGetTicks() ); 【Sampleの最初からコメントされたコードです。】
	// 【敵の存在時間の長さをランダムに出す】
	int actualDuration = ( rand() % rangeDuration ) + minDuration;

	// Create the actions
	// 【敵の移動Actionを作る、CCMoveToはCCFiniteTimeActionの子クラス】
	CCFiniteTimeAction* actionMove = CCMoveTo::create( (float)actualDuration,
                                            ccp(0 - target->getContentSize().width/2, actualY) );
	// 【敵が移動完了、つまり失敗の場合のAction、CCCallFuncNはCCFiniteTimeActionの子クラスで、spriteMoveFinishedを呼び出す】
	CCFiniteTimeAction* actionMoveDone = CCCallFuncN::create( this, 
                                            callfuncN_selector(HelloWorld::spriteMoveFinished));
	// 【敵を移動そして移動完了の順に動かす】
	target->runAction( CCSequence::create(actionMove, actionMoveDone, NULL) );

	// Add to targets array
	// 【ノードを識別するために、タグを付ける】
	target->setTag(1);
	// 【敵を作り終わったので、敵リストに追加】
	_targets->addObject(target);
}

ゲームを操作しない場合、当然失敗になって、spriteMoveFinishedが実行されます。
操作のコードの先に、この関数を見る

void HelloWorld::spriteMoveFinished(CCNode* sender)
{
	// 【敵でも弾丸でも移動終わったので、まずはレイヤーから削除される】
	CCSprite *sprite = (CCSprite *)sender;
	this->removeChild(sprite, true);

	// 【そしてノードが敵の場合は、敵を消して、GameOverSceneを作り、失敗を表示し、監督で失敗画面に切り換える】
	if (sprite->getTag() == 1)  // target
	{
		_targets->removeObject(sprite);
        
		GameOverScene *gameOverScene = GameOverScene::create();
		gameOverScene->getLayer()->getLabel()->setString("You Lose :[");
		CCDirector::sharedDirector()->replaceScene(gameOverScene);
	} // 【弾丸の場合、弾丸自体がもう画面の外に出ちゃったので、弾丸リストから削除】
	else if (sprite->getTag() == 2) // projectile
	{
		_projectiles->removeObject(sprite);
	}
}

4.ゲームを動かせる (HelloWorldScene後編)

GameOverSceneの解読は最後にして、
先に弾丸を発射するためのタップイベントを実装する関数を見る

void HelloWorld::ccTouchesEnded(CCSet* touches, CCEvent* event)
{
	// Choose one of the touches to work with
	// 【タップの座標を取得】
	CCTouch* touch = (CCTouch*)( touches->anyObject() );
	CCPoint location = touch->getLocation();
	
	// 【タップの位置座標をコンソールに出力、デバッグ用情報】
	CCLog("++++++++after  x:%f, y:%f", location.x, location.y);

	// Set up initial location of projectile
	// 【弾丸Spriteを作り、最初の位置を決定する】
	CCSize winSize = CCDirector::sharedDirector()->getVisibleSize();
	CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
	CCSprite *projectile = CCSprite::create("Projectile.png", CCRectMake(0, 0, 20, 20));
	projectile->setPosition( ccp(origin.x+20, origin.y+winSize.height/2) );

	// Determinie offset of location to projectile
	// 【弾丸の方向を計算する、数学上のベクタ的なイメージでしょう】
	float offX = location.x - projectile->getPosition().x;
	float offY = location.y - projectile->getPosition().y;

	// Bail out if we are shooting down or backwards
	// 【後ろか下に向けて発射の場合、弾丸を表示しなくてこのまま弾丸作り終了】
	if (offX <= 0) return;

	// Ok to add now - we've double checked position
	// 【弾丸を画面に表示させる】
	this->addChild(projectile);

	// Determine where we wish to shoot the projectile to
	// 【弾丸の移動目標座標を計算する、X値は目標関係なく全部画面の右端で、Y値は相似三角形で計算される】
	float realX = origin.x+winSize.width + (projectile->getContentSize().width/2);
	float ratio = offY / offX;
	float realY = (realX * ratio) + projectile->getPosition().y;
	CCPoint realDest = ccp(realX, realY);

	// Determine the length of how far we're shooting
	// 【弾丸の目標座標と弾丸最初の座標で、直角三角形の三平方定理で弾丸の移動距離を計算し、一定の速度で弾丸の表示時間を計算する】
	float offRealX = realX - projectile->getPosition().x;
	float offRealY = realY - projectile->getPosition().y;
	float length = sqrtf((offRealX * offRealX) + (offRealY*offRealY));
	float velocity = 480/1; // 480pixels/1sec
	float realMoveDuration = length/velocity;

	// Move projectile to actual endpoint
	// 【ここは敵のところと同じ、移動Actionと移動後の削除Action】
	projectile->runAction( CCSequence::create(
		CCMoveTo::create(realMoveDuration, realDest),
		CCCallFuncN::create(this, 
                            callfuncN_selector(HelloWorld::spriteMoveFinished)), 
        NULL) );

	// Add to projectiles array
	// 【弾丸にタグ2を付けて、弾丸リストに追加】
	projectile->setTag(2);
	_projectiles->addObject(projectile);

	// 【ぴゅーぴゅーの発射音効を鳴らす】
	CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("pew-pew-lei.wav");
}

これで弾丸も敵もあって、当たったかどうかの判定をする機能が必要です。
この機能を持つ関数はupdateGameです。1秒に60回実行されて、接触判定と画面更新を行う。
この関数を見る前、CCARRAY_FOREACHマクロを知った方が良いです。

#define CCARRAY_FOREACH(__array__, __object__)                                                                         \
    if ((__array__) && (__array__)->data->num > 0)                                                                     \
    for(CCObject** __arr__ = (__array__)->data->arr, **__end__ = (__array__)->data->arr + (__array__)->data->num-1;    \
    __arr__ < = __end__ && (((__object__) = *__arr__) != NULL/* || true*/);                                             \
    __arr__++)

updateGame関数:

void HelloWorld::updateGame(float dt)
{
	// 【敵に衝突して、消される必要のある弾丸リストを作る】
	// 【itとjtは敵リストと弾丸リストを遍歴するときのインスタンス格納変数】
	CCArray *projectilesToDelete = new CCArray;
	CCObject* it = NULL;
	CCObject* jt = NULL;

	// for (it = _projectiles->begin(); it != _projectiles->end(); it++)
	// 【弾丸リストを遍歴、itはループ変数、CCARRAY_FOREACHはマクロで定義されたループ】
	CCARRAY_FOREACH(_projectiles, it)
	{
		// 【dynamic_castはC++特有なタイプキャストで、ダウンキャストということのようです】
		// 【基本型を派生型にキャストする、ObjectのitがSpriteにキャストされた】
		CCSprite *projectile = dynamic_cast<CCSprite*>(it);
		// 【弾丸リスト遍歴から取った弾丸のサイズで直角四角形を作る】
		CCRect projectileRect = CCRectMake(
			projectile->getPosition().x - (projectile->getContentSize().width/2),
			projectile->getPosition().y - (projectile->getContentSize().height/2),
			projectile->getContentSize().width,
			projectile->getContentSize().height);

		// 【消される敵を格納するリストを作る、削除待ち敵リスト】
		CCArray* targetsToDelete =new CCArray;

		// for (jt = _targets->begin(); jt != _targets->end(); jt++)
		// 【敵リストを遍歴、jtはループ変数】
		CCARRAY_FOREACH(_targets, jt)
		{
			// 【ObjectのjtがSpriteにキャストされた】
			CCSprite *target = dynamic_cast<CCSprite*>(jt);
			// 【敵リスト遍歴から取った敵のサイズで直角四角形を作る】
			CCRect targetRect = CCRectMake(
				target->getPosition().x - (target->getContentSize().width/2),
				target->getPosition().y - (target->getContentSize().height/2),
				target->getContentSize().width,
				target->getContentSize().height);

			// if (CCRect::CCRectIntersectsRect(projectileRect, targetRect))
			// 【弾丸の四角形と敵の四角形が交差がある場合は(当たったということね)、敵を削除待ち敵リストに入れる】
			if (projectileRect.intersectsRect(targetRect))
			{
				targetsToDelete->addObject(target);
			}
		}

		// for (jt = targetsToDelete->begin(); jt != targetsToDelete->end(); jt++)
		// 【すべての敵に衝突判定を一周実施した後、当たった敵を削除待ち敵リストから取り出して削除する、jtはループ変数で、削除予定の敵】
		CCARRAY_FOREACH(targetsToDelete, jt)
		{
			CCSprite *target = dynamic_cast<CCSprite*>(jt);
			// 【取り出した敵を、レイヤークラスの敵リストからも削除】
			_targets->removeObject(target);
			// 【当然、この敵Spriteがレイヤーからも削除される】
			this->removeChild(target, true);

			// 【一個当たったので、撃破敵数増やす】
			_projectilesDestroyed++;
			// 【そして5個の敵を撃破できたら、ゲーム終了し、GameOverSceneで勝利を表示】
			if (_projectilesDestroyed >= 5)
			{
				GameOverScene *gameOverScene = GameOverScene::create();
				gameOverScene->getLayer()->getLabel()->setString("You Win!");
				CCDirector::sharedDirector()->replaceScene(gameOverScene);
			}
		}

		// 【敵に当たった弾丸も消されるので、削除待ち弾丸リストに追加】
		if (targetsToDelete->count() > 0)
		{
			projectilesToDelete->addObject(projectile);
		}
		// 【削除待ち敵リストの敵はすでにすべて削除されたはずなので(上のループで)、リストを解放する】
		targetsToDelete->release();
	}

	// for (it = projectilesToDelete->begin(); it != projectilesToDelete->end(); it++)
	// 【削除待ち弾丸リストの中の弾丸も処理される必要がある、処理は敵とほぼ同じで、】
	// 【レイヤークラスの弾丸リストとレイヤー表示から削除された後、削除待ち弾丸リスト自体も解放される】
	CCARRAY_FOREACH(projectilesToDelete, it)
	{
		CCSprite* projectile = dynamic_cast<CCSprite*>(it);
		_projectiles->removeObject(projectile);
		this->removeChild(projectile, true);
	}
	projectilesToDelete->release();
}

5.ゲームの結果画面を表示する (GameOverScene)

最後にGameOverSceneファイルの中の二つのクラス:
GameOverSceneシーンとGameOverLayerレイヤー
重要な関数は両方ともinit()なので、それぞれ解読します。
まずはシーンのcreate関数が実行されたとき、マクロによってcreate関数の内部で、
コンストラクタとinit関数が実行されるんですね:GameOverScene::init()

bool GameOverScene::init()
{
	// 【ここは親クラスのinit関数を呼び出す】
	if( CCScene::init() )
	{
		// 【レイヤークラスのcreate関数の中にレイヤーのinit関数を呼び出す】
		this->_layer = GameOverLayer::create();
		// 【ここはいろいろ調べたのですが、意味不明で、とりあえずautoreleaseでなくて、ずっとレイヤーが保持されることと理解しておく】
		this->_layer->retain();
		// 【レイヤーをシーンに入れる】
		this->addChild(_layer);
		
		return true;
	}
	else
	{
		return false;
	}
}

次はSceneにaddされたレイヤーGameOverLayer、
上記のシーンのinit関数の中のGameOverLayerのcreate関数の実装内部のinitです。
(ここのcreateもマクロCREATE_FUNCです)
GameOverLayer::init()

bool GameOverLayer::init()
{
	// 【ここはHelloWorldシーンのinit関数にもあった白背景設定、親クラスCCLayerColorの関数ですね】
	if ( CCLayerColor::initWithColor( ccc4(255,255,255,255) ) )
	{
		// 【ここはHelloWorldシーンのinit関数にもあった白背景設定、親クラスの関数ですね】
		CCSize winSize = CCDirector::sharedDirector()->getWinSize();
		// 【_labelは下記マクロで定義されたCCLabelTTFのラベルインスタンス】
		// 【CC_SYNTHESIZE_READONLY(cocos2d::CCLabelTTF*, _label, Label);】
		// 【同時にgetLabelという関数も定義された;CC_SYNTHESIZE_READONLYは文章最後に貼る】
		this->_label = CCLabelTTF::create("","Artial", 32);
		// 【保持の特性・色・位置が設定された後、レイヤーに入れる】
		_label->retain();
		_label->setColor( ccc3(0, 0, 0) );
		_label->setPosition( ccp(winSize.width/2, winSize.height/2) );
		this->addChild(_label);
		
		// 【レイヤーの動作を定義する:CCDelayTimeで待機した後、GameOverLayer::gameOverDoneを実行】
		// 【ちなみにGameOverLayer::gameOverDoneでは、監督がシーンをゲームのHelloWorldSceneに切り替える、もう一回ゲームが始まる】
		this->runAction( CCSequence::create(
                                CCDelayTime::create(3),
                                CCCallFunc::create(this, callfunc_selector(GameOverLayer::gameOverDone)),
                                NULL));
		
		return true;
	}
	else
	{
		return false;
	}
}

GameOverLayer::gameOverDone()でもう一回ゲームが始まる。

void GameOverLayer::gameOverDone()
{
	CCDirector::sharedDirector()->replaceScene( HelloWorld::scene() );
}

CC_SYNTHESIZE_READONLYのマクロ定義は下記です。
(cocos2d-xのcocos2dx/platform/CCPlatformMacros.h)

#define CC_SYNTHESIZE_READONLY(varType, varName, funName)\
protected: varType varName;\
public: virtual varType get##funName(void) const { return varName; }

ちょっとした注意点

監督CCDirectorの関数runWithSceneとreplaceSceneの使い分けは下記です。

  • runWithScene:アプリの初回起動時だけに使う。
  • replaceScene:シーンを切り換えるときに使う。最初からこれを使うと、エラーが出て最初のcocos2d-xのLOGOのところで止まってしまった。

コメントを残す

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


*

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>