cocos2d-xでsocket.ioを使う iOS版
Cocos2d-xでsocket.ioを使う方法について調べました。
Cocos2d-xには標準でSocketIOクラスが用意されていますが、諸々の理由により使わずに、
iOS/Androidそれぞれ個別に設定を行います。
まずはiOSから。
iOSはAZSocketIOを使用します。AZSocketIOはcocoapodsを使用してインストールするのが一般的のようですが、
Cocos2d-xでcocoapodsを使えるようにするのも面倒そうなので、手動で設定します。
今回使用したバージョン
- Cocos2d-x 3.9
- AZSocketIO 0.0.6
- SocketRocket 0.4.2
- AFNetworking 2.6.3 (3.1.0だとビルドできませんでした)
ライブラリをセットアップ
まずはAZSocketIOと、それに必要なAFNetworking、SocketRocketをCocos2d-xプロジェクトに組み込みます。
下記サイトからそれぞれダウンロードしてzipを解凍します。
- https://github.com/AFNetworking/AFNetworking
- https://github.com/facebook/SocketRocket
- https://github.com/lukabernardi/AZSocketIO
解凍したら、それぞれの中にある下記フォルダをCocos2d-xプロジェクトのproj.ios_macなどにコピーして、Xcode上でプロジェクトに追加します。
- AFNetworking
- SocketRocket
- AZSocketIO
次に、Xcode上の「Compile Sources」で、AZ*、AF*、SR*の全てに-fobjc-arcを設定します。
「Link Binary With Libraries」に下記を追加します。
- SystemConfiguration
- CFNetwork
- MobileCoreServices
- libicucore.tbd
ここまでで、ビルドができるようになるかと思います。
実装
socket.ioサーバーへの接続や切断などはObjective-Cのコードで記述するので、NativeCodeLauncherというクラスに実装します。
また、接続完了した、切断した、エラーが発生したなどのイベントを受け取ることができますが、これらを受け取った時に
Cocos2d-x側でなにかしらのアクションを行えるよう、SocketManagerというシングルトンクラスを作っておきます。
Cocos2d-xのプログラムからSocket.io関連の何かをする場合は全てSocketManagerを通すという設計にします。
NativeCodeLauncher.h
#include <stddef.h> #include <string> namespace Cocos2dExt { class NativeCodeLauncher { public: // Socket.IO関連 static void connectToSocketIO(const char* host,int port); static void emitToSocketIO(const char* event,const char* message); static void disconnectFromSocketIO(); }; } // end of namespace Cocos2dExt
NativeCodeLauncher.mm
#include "NativeCodeLauncher.h" #include "NativeCodeLauncher_objc.h" // Socket.IO関連処理 static void static_connectToSocketIO(const char* host,int port) { [[NativeCodeLauncher sharedManager] connectToSocketIO: [NSString stringWithUTF8String:host] port: port]; } static void static_emitToSocketIO(const char* event,const char* message) { [[NativeCodeLauncher sharedManager] emitToSocketIO:[NSString stringWithUTF8String:event] message:[NSString stringWithUTF8String:message]]; } static void static_disconnectFromSocketIO() { [[NativeCodeLauncher sharedManager] disconnectFromSocketIO]; } namespace Cocos2dExt { // ソケット接続 void NativeCodeLauncher::connectToSocketIO(const char* host,int port) { static_connectToSocketIO(host,port); } // イベントとメッセージをソケットに送信 void NativeCodeLauncher::emitToSocketIO(const char* event,const char* message) { static_emitToSocketIO(event,message); } // ソケット切断 void NativeCodeLauncher::disconnectFromSocketIO() { static_disconnectFromSocketIO(); } }
NativeCodeLauncher_objc.h
@interface NativeCodeLauncher : NSObject{ } // シングルトンオブジェクトを返す + (NativeCodeLauncher *)sharedManager; // Socket.IO -(void)connectToSocketIO:(NSString *)host port:(int) port; -(void)emitToSocketIO:(NSString *)event message:(NSString *)message; -(void)disconnectFromSocketIO; @end
NativeCodeLauncher_objc.mm
#import "NativeCodeLauncher_objc.h" //#import "EAGLView.h" #import "AppController.h" #import "RootViewController.h" #import "AZSocketIO.h" #import "SocketManager.hpp" @implementation NativeCodeLauncher { AZSocketIO *socketIO; } // シングルトンオブジェクト static NativeCodeLauncher *sharedData_ = nil; // シングルトンオブジェクトを返す + (NativeCodeLauncher *)sharedManager { @synchronized(self) { if(!sharedData_) { sharedData_ = [NativeCodeLauncher new]; } } return sharedData_; } - (id)init { self = [super init]; if(self) { } return self; } // ソケット接続 -(void)connectToSocketIO:(NSString *)host port:(int) port { // ホストとポート番号を指定してAZSocketIOインスタンス生成 socketIO = [[AZSocketIO alloc] initWithHost:host andPort:[NSString stringWithFormat:@"%d",port] secure:NO]; socketIO.reconnectionDelay = 1.0; socketIO.reconnectionLimit = 4; socketIO.maxReconnectionAttempts = 1; // 接続開始 [socketIO connectWithSuccess:^{ NSLog(@"Socket接続成功."); // 接続成功 SocketManager::getInstance()->onConnected(); } andFailure:^(NSError *error) { NSLog(@"Socket接続失敗. error: %@", error); // 接続失敗 NSString *errorMessage = [NSString stringWithFormat:@"%@", error]; SocketManager::getInstance()->onError([errorMessage UTF8String]); }]; // イベントを受けとった時 [socketIO setEventReceivedBlock:^(NSString *eventName, id data) { // ただのキャストではうまくいかない NSString *message = [NSString stringWithFormat:@"%@",data[0]]; NSLog(@"Event : %@, message : %@",eventName,message); // イベントとメッセージを渡して仕分け SocketManager::getInstance()->receiveMessage([eventName UTF8String], [message UTF8String]); }]; // エラーを受信したときに実行されるBlocks [socketIO setErrorBlock:^(NSError *error) { NSLog(@"error: %@", error); // エラー NSString *errorMessage = [NSString stringWithFormat:@"%@", error]; SocketManager::getInstance()->onError([errorMessage UTF8String]); }]; // 切断されたときに実行されるBlocks [socketIO setDisconnectedBlock:^{ NSLog(@"Socket切断完了."); // 切断 SocketManager::getInstance()->onDisconnected(); }]; } // イベントとメッセージをソケットに送信 -(void)emitToSocketIO:(NSString *)event message:(NSString *)message { [socketIO emit:event args:message error:NULL]; } // ソケット切断 -(void)disconnectFromSocketIO { [socketIO disconnect]; } @end
SocketManager.hpp
#include "cocos2d.h" USING_NS_CC; class SocketManager { private: static SocketManager* mManager; public: SocketManager(); static SocketManager* getInstance(); // ソケット接続/切断 void connect(); void disconnect(); // 接続された void onConnected(); // 切断された void onDisconnected(); // エラーが発生した void onError(std::string error); // メッセージを送信 void emitMessage(std::string event,std::string message); // メッセージを受け取った時の処理 void receiveMessage(std::string event,std::string message); };
SocketManager.cpp
#include "SocketManager.hpp" #include "NativeCodeLauncher.h" SocketManager* SocketManager::mManager = NULL; #pragma mark - 初期化 SocketManager::SocketManager() { } SocketManager* SocketManager::getInstance() { if(mManager == NULL) { mManager = new SocketManager(); } return mManager; } // Socket接続 void SocketManager::connect() { // 接続先のホスト、ポートは変更 Cocos2dExt::NativeCodeLauncher::connectToSocketIO("192.168.0.1", 3150); } // Socket切断 void SocketManager::disconnect() { Cocos2dExt::NativeCodeLauncher::disconnectFromSocketIO(); } // Socket接続された void SocketManager::onConnected() { // Cocos2d-xのスレッドに実行させる。これがないと、Androidの描画関連がうまくいかないことがある。 Director::getInstance()->getScheduler()->performFunctionInCocosThread([=](){ // イベントを通知 Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("SocketConnected"); }); } // Socket切断された void SocketManager::onDisconnected() { // Cocos2d-xのスレッドに実行させる。これがないと、Androidの描画関連がうまくいかないことがある。 Director::getInstance()->getScheduler()->performFunctionInCocosThread([=](){ // イベントを通知 Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("SocketDisconnected"); }); } // Socketでエラーが発生した void SocketManager::onError(std::string error) { // Cocos2d-xのスレッドに実行させる。これがないと、Androidの描画関連がうまくいかないことがある。 Director::getInstance()->getScheduler()->performFunctionInCocosThread([=](){ // イベントを通知 Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("SocketError"); }); } // イベントを送信 void SocketManager::emitMessage(std::string event, std::string message) { Cocos2dExt::NativeCodeLauncher::emitToSocketIO(event.c_str(),message.c_str()); } // イベントを受信した void SocketManager::receiveMessage(std::string event, std::string message) { // Cocos2d-xのスレッドに実行させる。これがないと、Androidの描画関連がうまくいかないことがある。 Director::getInstance()->getScheduler()->performFunctionInCocosThread([=](){ // イベントを通知 EventCustom customEvent("SocketEventReceived"); auto messageValue = Value(message); customEvent.setUserData(&messageValue); // 受け取った内容をEventCustomに入れて通知 Director::getInstance()->getEventDispatcher()->dispatchEvent(&customEvent); }); }