今回のエンジニアブログ担当の岩原です。
今回はCocos2d-xに備わっているとある機能をご紹介します。
Hot Updating
Cocos2d-x+Luaには、Hot Updatingという仕組みが用意されています。
これはコードを変更すると、自動的に更新されてアプリにも反映されるという仕組みです。
Cocos Code IDEで主に利用されいる仕組みだったと思います。
大体の処理シーケンスは以下のとおりだと思います。
- ゲームが立ち上がると同時にTCPサーバーとファイルサーバーを立ち上げ、接続を待ち受ける。
- クライアント側(Cocos Code IDEなど)からTCPでコマンドを送信する。ファイルの場合はファイルも合わせて送信する。
- ゲームではそれを受け取り、処理を行い、結果をクライアント側に返す。
このHot Updatingですが、一部機能はC++からも利用可能です。
早速使ってみましょう。
用意するもの
・新規Cocos2d-x v3.x C++プロジェクト(サンプルコードはv3.2を使用)
・TCPクライアント
・開発マシンと通信できる端末とそのIPアドレス。
概要
ファイルの送受信は使用できませんが、コマンドの実行は可能です。
実行可能なコマンドは予めいくつか用意されており、さらに自分でコマンドを追加することも出来ます。
コマンドの追加は次回にかけたら書きたいと思います。
コマンドの機能はConsoleクラスにまとまっています。気になる方はコードを読んでみるとよいでしょう。
今回はいくつか用意されているコマンドから、「touch」コマンドを遠隔で実行することにします。
touchコマンドはtapとswipeの2種類あるのですが、今回はtapを行います。
C++で書いてみる
デフォルトのシーンからあの例の画像を表示している箇所をコメントアウトし、ラベルのみを表示するようにします。
また、タッチして動かしている間とタッチを離した時の座標をラベルに表示するようにしましょう。
// on "init" you need to initialize your instance bool HelloWorld::init() { //省略 // add the label as a child to this layer this->addChild(label, 1); //ここから追加 // touch event listener create. auto listener = EventListenerTouchOneByOne::create(); listener->onTouchBegan = [=](Touch* touch, Event* event) -> bool { return true; }; listener->onTouchMoved = [=](Touch* touch, Event* event) -> void { char msg[128]; snprintf(msg, sizeof(msg), "x[%f] y[%f]",touch->getLocationInView().x,touch->getLocationInView().y); label->setString(msg); CCLOG("move %s", msg); }; listener->onTouchEnded = [=](Touch* touch, Event* event) -> void { char msg[128]; snprintf(msg, sizeof(msg), "x[%f] y[%f]",touch->getLocationInView().x,touch->getLocationInView().y); label->setString(msg); CCLOG("end %s", msg); }; this->getEventDispatcher()->addEventListenerWithSceneGraphPriority (listener, this); //ここまで追加 //省略 }
次に、TCPサーバーの立ち上げを行います。
全体で行う場合はAppDelegate.cppのapplicationDidFinishLaunching関数、
シーン限定でやる場合は適用したいシーンのinit関数で以下のようなコードを書くとよいでしょう。
今回はシーン限定で書く想定で行こうと思います。
bool HelloWorld::init() { ////////////////////////////// // 1. super init first if ( !Layer::init() ) { return false; } //ここから追加 #ifdef COCOS2D_DEBUG //TCPサーバーを立ち上げ、接続要求を待ち受ける Director::getInstance()->getConsole()->listenOnTCP(6010); #endif //ここまで追加 //以下省略
6010はポート番号になります。
空いているポートであればいくつでも良いと思いますが、Cocos2d-x+Luaで使用しているポートが6010だったので、
それに合わせています。
シーン単位であれば、onExit辺りでTCPサーバーを停止するのを忘れないようにしましょう。
void HelloWorld::onExit() { #ifdef COCOS2D_DEBUG //TCPサーバーを停止する Director::getInstance()->getConsole()->stop(); #endif }
全体のコードは以下のとおりです。
#include "HelloWorldScene.h" USING_NS_CC; Scene* HelloWorld::createScene() { // 'scene' is an autorelease object auto scene = Scene::create(); // 'layer' is an autorelease object auto layer = HelloWorld::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } // on "init" you need to initialize your instance bool HelloWorld::init() { ////////////////////////////// // 1. super init first if ( !Layer::init() ) { return false; } #ifdef COCOS2D_DEBUG //TCPサーバーを立ち上げ、接続要求を待ち受ける Director::getInstance()->getConsole()->listenOnTCP(6010); #endif Size visibleSize = Director::getInstance()->getVisibleSize(); Vec2 origin = Director::getInstance()->getVisibleOrigin(); ///////////////////////////// // 2. add a menu item with "X" image, which is clicked to quit the program // you may modify it. // add a "close" icon to exit the progress. it's an autorelease object auto closeItem = MenuItemImage::create( "CloseNormal.png", "CloseSelected.png", CC_CALLBACK_1(HelloWorld::menuCloseCallback, this)); closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 , origin.y + closeItem->getContentSize().height/2)); // create menu, it's an autorelease object auto menu = Menu::create(closeItem, NULL); menu->setPosition(Vec2::ZERO); this->addChild(menu, 1); ///////////////////////////// // 3. add your codes below... // add a label shows "Hello World" // create and initialize a label auto label = Label::createWithSystemFont("Hello World", "Arial", 24); // position the label on the center of the screen label->setPosition(Vec2(origin.x + visibleSize.width/2, origin.y + visibleSize.height - label->getContentSize().height)); // add the label as a child to this layer this->addChild(label, 1); // touch event listener create. auto listener = EventListenerTouchOneByOne::create(); listener->onTouchBegan = [=](Touch* touch, Event* event) -> bool { return true; }; listener->onTouchMoved = [=](Touch* touch, Event* event) -> void { char msg[128]; snprintf(msg, sizeof(msg), "x[%f] y[%f]",touch->getLocationInView().x,touch->getLocationInView().y); label->setString(msg); CCLOG("move %s", msg); }; listener->onTouchEnded = [=](Touch* touch, Event* event) -> void { char msg[128]; snprintf(msg, sizeof(msg), "x[%f] y[%f]",touch->getLocationInView().x,touch->getLocationInView().y); label->setString(msg); CCLOG("end %s", msg); }; this->getEventDispatcher()->addEventListenerWithSceneGraphPriority (listener, this); // add "HelloWorld" splash screen" //auto sprite = Sprite::create("HelloWorld.png"); // position the sprite on the center of the screen //sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y)); // add the sprite as a child to this layer //this->addChild(sprite, 0); return true; } void HelloWorld::onExit() { #ifdef COCOS2D_DEBUG //TCPサーバーを停止する Director::getInstance()->getConsole()->stop(); #endif } void HelloWorld::menuCloseCallback(Ref* pSender) { #if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert"); return; #endif Director::getInstance()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) exit(0); #endif }
本番アプリにも立ち上がると大変なので、必ず#ifdef COCOS2D_DEBUGで囲むようにしましょう。
これで対象のシーンを表示した時のみTCPサーバーが立ち上がり、接続を待ち受けるようになります。
アプリをビルド&実行しましょう。
TCPクライアントを使用し、コマンドの送受信を行う
アプリを立ち上げた状態で、事前に調べたIPアドレスとポート番号を使用し、TCP接続を行います。
無事に接続できると、
>
と表示されていると思います。
次にコマンドの送信を行うのですが、いくつか注意点があります。
コマンドの切れ目を\nで判断しているため、\nを末尾に付けないと正しく認識されません。
気をつけましょう。
また、アプリ側から送られてくる応答データが途中で切れ、次回の応答の際にまとめて返ってくることが多々あります。
では、さっそくtouchコマンドを使用してタップを行いましょう。
コマンドは以下のとおりです。
touch tap x座標 y座標
ここでのx座標、y座標はsetPositionで指定する座標と同じです。
たとえば、x座標に100、y座標に200を指定する場合は
touch tap 100 200
というようにしています。
このコマンド文字列をアプリに送信すると、
ラベルにそれぞれの座標が表示されたと思います。
画面をタップしても同じ結果が得られると思います。
どのようなコマンドがあるか見てみる
helpと打つと、現在使用できるコマンドとその説明が表示されます。
結構な頻度でぶった切られるので、2回打つと良いでしょう。
デフォルトではこれだけ用意されています。
Available commands: config Print the Configuration object debugmsg Whether or not to forward the debug messages on the console. Args: [on | off] director director commands, type -h or [director help] to list supported directives exit Close connection to the console fileutils Flush or print the FileUtils info. Args: [flush | ] fps Turn on / off the FPS. Args: [on | off] help Print this message projection Change or print the current projection. Args: [2d | 3d] resolution Change or print the window resolution. Args: [width height resolution_policy | ] scenegraph Print the scene graph texture Flush or print the TextureCache info. Args: [flush | ] touch simulate touch event via console, type -h or [touch help] to list supported directives upload upload file. Args: [filename base64_encoded_data] version print version string
忘れていなければ、次回はコマンドの追加を行いたいと思います。