はじめに
- cocos2d-x でゲームを作るときに、タイマーの表示やスコアの表示など、きまりきった機能を別レイヤーにまとめて整理したいと考えました。
- そこで、タイマー切れの処理やスコア更新処理など、レイヤーとレイヤーの間で連携をとるために、schedule_selector などで扱っているコールバックの仕組みについて調べてみました。
新しいプロジェクトの作成
- create_project.py で新しいプロジェクト MyGame を作成してテストします。
呼び出されるレイヤーの作成
- メインのレイヤーを HelloWorldScene として、呼び出される側のレイヤーとして MyLayer を作ります。
- MyLayer.h として作成します。
#ifndef MyGame_MyLayer_h
#define MyGame_MyLayer_h
#include "cocos2d.h"
USING_NS_CC;
class MyLayer : public CCLayer
{
public:
virtual bool init()
{
if (!CCLayer::init()) {
return false;
}
return true;
}
CREATE_FUNC(MyLayer);
};
#endif
コールバック呼び出し用の型とマクロの宣言
- CCObject.hを参照すると、コールバックの仕組みは関数ポインターというもので実装しているようです。
- 関数ポインターとはそのまま関数のポインターで、関数の参照を値として扱えるようになることで、動的な関数呼び出しができるようになるそうです。
- cocos2d-x のバージョン3だと無名関数まで使えるようになるそうですが、アルファなので怖いので指をくわえてスルーします。
- MyLayer.h に追記します。
typedef void (CCObject::* SEL_MySelector)(CCObject*);
#define my_selector(_SELECTOR) (SEL_MySelector)(&_SELECTOR)
コールバック関数を受け取るメソッドの作成
- MyLayer 側で、コールバック関数を呼び出す関数を作成します。
void doCallback(CCObject* caller, SEL_MySelector selector)
{
CCLOG("[doCallback]");
(caller->*selector)(caller);
}
コールバック関数の作成
- HelloWorldScene 側で、コールバック関数を作成します。
#include "MyLayer.h";
void HelloWorld::myCallback(CCObject* sender)
{
CCLOG("[myCallback]");
}
コールバック関数を呼び出す関数の呼び出し
- HelloWorldScene 側で、MyLayer の側の、コールバック関数を引数とする関数を実行します。
- 起動時に実行されるように HelloWorldScene::init() 内に追記します。
MyLayer* layer = MyLayer::create();
layer->doCallback(this, my_selector(HelloWorld::myCallback));
実行
- シミュレーターで実行して動作を確認します。CCLOGで指定した文字列が表示されれば成功です。
コード
MyLayer.h
#ifndef MyGame_MyLayer_h
#define MyGame_MyLayer_h
#include "cocos2d.h"
USING_NS_CC;
typedef void (CCObject::* SEL_MySelector)(CCObject*);
#define my_selector(_SELECTOR) (SEL_MySelector)(&_SELECTOR)
class MyLayer : public CCLayer
{
public:
virtual bool init()
{
if (!CCLayer::init()) {
return false;
}
return true;
}
CREATE_FUNC(MyLayer);
void doCallback(CCObject* caller, SEL_MySelector selector)
{
CCLOG("[doCallback]");
(caller->*selector)(caller);
}
};
#endif
HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include "MyLayer.h"
class HelloWorld : public cocos2d::CCLayer
{
public:
virtual bool init();
static cocos2d::CCScene* scene();
void menuCloseCallback(CCObject* pSender);
CREATE_FUNC(HelloWorld);
void myCallback(CCObject* caller);
};
#endif
HelloWorldScene.cpp
#include "HelloWorldScene.h"
USING_NS_CC;
CCScene* HelloWorld::scene()
{
CCScene *scene = CCScene::create();
HelloWorld *layer = HelloWorld::create();
scene->addChild(layer);
return scene;
}
bool HelloWorld::init()
{
if ( !CCLayer::init() )
{
return false;
}
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
this,
menu_selector(HelloWorld::menuCloseCallback));
pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width/2 ,
origin.y + pCloseItem->getContentSize().height/2));
CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
pMenu->setPosition(CCPointZero);
this->addChild(pMenu, 1);
CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", 24);
pLabel->setPosition(ccp(origin.x + visibleSize.width/2,
origin.y + visibleSize.height - pLabel->getContentSize().height));
this->addChild(pLabel, 1);
CCSprite* pSprite = CCSprite::create("HelloWorld.png");
pSprite->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
this->addChild(pSprite, 0);
MyLayer* layer = MyLayer::create();
layer->doCallback(this, my_selector(HelloWorld::myCallback));
return true;
}
void HelloWorld::menuCloseCallback(CCObject* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
CCMessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
#else
CCDirector::sharedDirector()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
#endif
}
void HelloWorld::myCallback(CCObject* sender)
{
CCLOG("[myCallback]");
}