Cocos2d-xとFirebaseを使って、ゲームランキングを実装してみます。
今回はFirebaseを設定してユーザー登録まで行います。
プロジェクト作成
Cocos2d-xの導入部分は割愛します。
cocosコマンド
を使って、新規プロジェクトを作成します。
$ cocos new RankingTest -p jp.co.ienter.RankingTest -l cpp -d RankingTest
Firebase導入
Firebaseのコンソール画面から、プロジェクトの追加を行います。
プロジェクト名を入力して、プロジェクトを作成
をクリック。
今回はiOSアプリでランキングを作成しますので、iOSを選択しましょう。
iOSのバンドルIDを入力しましょう。
Cocos2d-xのプロジェクト作成時に設定したパッケージ名を入力します。
入力後右下のREGISTER APP
ボタンをクリックしてください。
XCodeにFirebase導入に必要なplistファイルをダウンロードします。
ダウンロードしたplistはXCodeプロジェクトに追加しておきましょう。
Firebase設定
Realtime Databaseを誰でも編集出来るように、ルールを変更します。
下記のように修正します。
.read
と.write
をそれぞれtrue
にします。
.indexOn
という部分は大量にあるデータをindex化して高速にアクセスするためのものです。
{
"rules": {
".read": true,
".write": true,
"score_ranking":{
".indexOn": ["score"]
}
}
}
XCodeに導入する
ターミナルを起動して、Podfileを作成します。
$ cd RankingTest/proj.ios_mac
$ pod init
するとproj.ios_macフォルダにPodfile
が生成されます。
viコマンドでPodfileを編集します。
$ vi Podfile
platform :ios, '8.0'
target 'RankingTest-mobile' do
pod 'Firebase'
pod 'Firebase/Database'
end
Podfileを編集後、podをインストールしましょう。
この時、開いているXCodeは閉じておきます。
$ pod install
インストールが完了すると、RankingTest.xcworkspace
ファイルが作成されます。
これを開いてみましょう。今後はこちらを起動します。
Build SettingsにあるOther Linker Flags
を編集して$(inherited)
を追加します。
次にLibrary Search Paths
も同様に$(inherited)
を追加します。
このままビルドするとエラーが出てしまうので、
足りないFrameworkを追加します。
GameController.frameworkを追加します。
これで一度ビルドを行います。
Firebase初期化
ここからFirebaseに関するプログラムを記述していきます。
iOSフォルダにあるAppController.mm
を開きましょう。
// Firebaseをインポート
#import "Firebase.h"
// 省略 //
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// 省略 //
// Firebase初期化
[FIRApp configure];
}
ユーザークラス作成
ユーザークラスを作成します。Firebaseフォルダを作成し、FIBUserBridge.h
を新規作成しましょう。
#include "cocos2d.h"
USING_NS_CC;
class FIBUserBridge;
// コールバック用
using FIBUserBridgeCallback = std::function<void (FIBUserBridge *, const std::string &)>;
class FIBUserBridge
{
public:
std::string userID; // ユーザーID
std::string userName; // ユーザー名
long long createTM; // 作成日時
static FIBUserBridge* userBridge(const void* user);
static FIBUserBridge* create(const std::string& userID, const std::string& userName);
};
次に、iOSフォルダにFirebaseフォルダを追加し以下の3ファイルを新規作成しましょう。
拡張子は.mm
という事に注意してください。
※ .mmファイルになるとC++言語を書くことが出来ます。
- FIBUserBridge.mm
- FIBUser.mm
- FUBUser.h
#import "FIBUserBridge.h"
#import "FIBUser.h"
// FIBUserからFIBUserBridgeに変換します
FIBUserBridge* FIBUserBridge::userBridge(const void *user)
{
FIBUser *aUser = (FIBUser *)user;
FIBUserBridge* userBridge = new FIBUserBridge();
userBridge->userID = aUser.userID ? [aUser.userID UTF8String] : "";
userBridge->userName = aUser.userName ? [aUser.userName UTF8String] : "";
userBridge->createTM = [aUser.createTM longLongValue];
return userBridge;
}
FIBUserBridge* FIBUserBridge::create(const std::string &userID, const std::string &userName)
{
FIBUserBridge* userBridge = new FIBUserBridge();
userBridge->userID = userID;
userBridge->userName = userName;
userBridge->createTM = 0;
return userBridge;
}
#import "FIBUser.h"
@implementation FIBUser
-(instancetype) initWithUserID:(NSString *)userID
userName:(NSString *)userName
createTM:(NSString *)createTM
{
if ((self = [super init])) {
_userID = [userID copy];
_userName = [userName copy];
_createTM = [createTM copy];
}
return self;
}
@end
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class FIBUser;
typedef void (^FIBUserCompletionHandler)(FIBUser *user, NSError *error);
@interface FIBUser : NSObject
@property (nonatomic, readonly) NSString *userID; // ユーザーID
@property (nonatomic, readonly) NSString *userName; // ユーザー名
@property (nonatomic, readonly) NSString *createTM; // 作成日時
-(instancetype) initWithUserID:(NSString *)userID
userName:(NSString *)userName
createTM:(NSString *)createTM;
@end
アカウントクラス作成
次にアカウントクラスを作成します。
ClassesフォルダのFirebaseフォルダにFIBAccountBridge.h
を作成しましょう。
#include "cocos2d.h"
#include "FIBUserBridge.h"
USING_NS_CC;
class FIBAccountBridge
{
public:
static void loginUser(const std::string& userID, const FIBUserBridgeCallback callback);
static void signupUser(FIBUserBridge* user, const FIBUserBridgeCallback callback);
static void updateUser(FIBUserBridge* user, const FIBUserBridgeCallback callback);
};
次にiOSフォルダのFirebaseフォルダに下記を新規作成しましょう。
こちらも拡張子が.mm
なので気をつけましょう。
- FIBAccountBridge.mm
- FIBAccount.mm
- FIBAccount.h
#import "FIBAccountBridge.h"
#import "FIBAccount.h"
#import "FIBUser.h"
// ユーザー取得
void FIBAccountBridge::loginUser(const std::string &userID, const FIBUserBridgeCallback callback)
{
NSString *aUserID = [NSString stringWithCString:userID.c_str()
encoding:[NSString defaultCStringEncoding]];
[FIBAccount loginUserWithCompletion:aUserID completion:^(FIBUser *user, NSError *error) {
if (user) {
FIBUserBridge* userBridge = FIBUserBridge::userBridge(user);
callback(userBridge, "");
} else {
callback(nullptr, "error");
}
}];
}
// ユーザー作成
void FIBAccountBridge::signupUser(FIBUserBridge *user, const FIBUserBridgeCallback callback)
{
NSString* aUserID = [NSString stringWithCString:user->userID.c_str() encoding:[NSString defaultCStringEncoding]];
NSString* aUserName = [NSString stringWithCString:user->userName.c_str() encoding:NSUTF8StringEncoding];
FIBUser* aUser = [[FIBUser alloc] initWithUserID:aUserID
userName:aUserName
createTM:0];
[FIBAccount singupUserWithCompletion:aUser completion:^(FIBUser *user, NSError *error) {
if (user) {
FIBUserBridge* userBridge = FIBUserBridge::userBridge(user);
callback(userBridge, "");
} else {
callback(nullptr, "error");
}
}];
}
// ユーザー更新
void FIBAccountBridge::updateUser(FIBUserBridge *user, const FIBUserBridgeCallback callback)
{
NSString* aUserID = [NSString stringWithCString:user->userID.c_str() encoding:[NSString defaultCStringEncoding]];
NSString* aUserName = [NSString stringWithCString:user->userName.c_str() encoding:NSUTF8StringEncoding];
FIBUser* aUser = [[FIBUser alloc] initWithUserID:aUserID
userName:aUserName
createTM:0];
[FIBAccount singupUserWithCompletion:aUser completion:^(FIBUser *user, NSError *error) {
if (user) {
FIBUserBridge* userBridge = FIBUserBridge::userBridge(user);
callback(userBridge, "");
} else {
callback(nullptr, "error");
}
}];
}
#import "FIBAccount.h"
#import "Firebase.h"
#import "FIBUser.h"
@implementation FIBAccount
// ログイン処理
+(void) loginUserWithCompletion:(NSString *)userID completion:(FIBUserCompletionHandler)completion
{
// 対象ユーザーIDの情報を取得する
FIRDatabaseReference* ref = [[FIRDatabase database] reference].ref;
[[ref child:userID] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot)
{
// snapshotに取得したデータが入っている
NSDictionary *snapshotData = snapshot.value;
NSString* aUserID = [snapshot key];
NSString* aUserName = [snapshotData valueForKey:@"name"];
NSString* aCreateTM = [snapshotData valueForKey:@"createTM"];
// FIBUserに格納する
FIBUser* fibUser = [[FIBUser alloc] initWithUserID:aUserID
userName:aUserName
createTM:aCreateTM];
completion(fibUser, nil);
}];
}
// ユーザー登録処理
+(void) singupUserWithCompletion:(FIBUser *)fibUser completion:(FIBUserCompletionHandler)completion
{
FIRDatabaseReference* ref = [[FIRDatabase database] reference].ref;
[[[ref child:@"users"] child:fibUser.userID] setValue:@{@"name": fibUser.userName,
@"createTM": [FIRServerValue timestamp]}
withCompletionBlock:^(NSError * _Nullable error, FIRDatabaseReference * _Nonnull ref)
{
if (error != nil) {
completion(nil, error);
} else {
// 登録完了後、Firebaseからデータを取得する
[[[[[FIRDatabase database] reference].ref child:@"users"] child:fibUser.userID] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
NSDictionary * data = snapshot.value;
FIBUser* wkUser = [[FIBUser alloc] initWithUserID:snapshot.key
userName:[data valueForKey:@"name"]
createTM:[data valueForKey:@"createTM"]];
completion(wkUser, nil);
}];
}
}];
}
// ユーザー更新
+(void) updateUserWithCompletion:(FIBUser *)fibUser completion:(FIBUserCompletionHandler)completion
{
FIRDatabaseReference* ref = [[FIRDatabase database] reference].ref;
[[[ref child:@"users"] child:fibUser.userID] updateChildValues:@{@"name": fibUser.userName}
withCompletionBlock:^(NSError * _Nullable error, FIRDatabaseReference * _Nonnull ref) {
if (error != nil) {
completion(nil, error);
} else {
[[[[[FIRDatabase database] reference].ref child:@"users"] child:fibUser.userID] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
NSDictionary * data = snapshot.value;
FIBUser* wkUser = [[FIBUser alloc] initWithUserID:snapshot.key
userName:[data valueForKey:@"name"]
createTM:[data valueForKey:@"createTM"]];
completion(wkUser, nil);
}];
}
}];
}
@end
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "FIBUser.h"
@interface FIBAccount : NSObject
+(void) loginUserWithCompletion:(NSString * )userID
completion:(FIBUserCompletionHandler)completion;
+(void) singupUserWithCompletion:(FIBUser *)fibUser
completion:(FIBUserCompletionHandler)completion;
+(void) updateUserWithCompletion:(FIBUser *)fibUser
completion:(FIBUserCompletionHandler)completion;
@end
GSUtilComクラスを作成する
共通関数を作成しましょう。
ここでは、端末固有のIDを取得する処理を記述します。
このIDがユーザーIDとなります。
#include <string.h>
#include "cocos2d.h"
using namespace std;
using namespace cocos2d;
class GSUtilCom
{
public:
// ユーザーID
static std::string getUserID();
};
次にGSUtilCom.mm
ファイルを新規作成します。
#import "GSUtilCom.h"
// ユーザーID取得
std::string GSUtilCom::getUserID()
{
// 端末固有のIDを取得する
NSString* userID = [[UIDevice currentDevice].identifierForVendor UUIDString];
return [userID UTF8String];
}
ユーザー登録処理を記述する
実際の処理をHelloWorldScene.cpp
に記述しましょう
#include "HelloWorldScene.h"
#include "GSUtilCom.h"
#include "FIBUserBridge.h"
#include "FIBAccountBridge.h"
#define WINSIZE Director::getInstance()->getWinSize()
#define WINCENTER Point(WINSIZE.width*0.5, WINSIZE.height*0.5)
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;
}
// セットアップ
this->setup();
return true;
}
// セットアップ処理
void HelloWorld::setup()
{
// ユーザー作成
Sprite* btnCreateUser_ON = Sprite::create();
btnCreateUser_ON->setTextureRect(Rect(0, 0, 200, 100));
btnCreateUser_ON->setColor(Color3B::GRAY);
Sprite* btnCreateUser_OFF = Sprite::create();
btnCreateUser_OFF->setTextureRect(Rect(0, 0, 200, 100));
btnCreateUser_OFF->setColor(Color3B(0,0,200));
MenuItemSprite* btnCreateUser = MenuItemSprite::create(btnCreateUser_OFF,
btnCreateUser_ON,
[this](Ref* sender) {this->createUserTapped((MenuItemSprite*)sender);});
Label* lblCreateUser = Label::createWithSystemFont("ユーザー作成", "arial", 26);
lblCreateUser->enableOutline(Color4B::BLACK, 1);
lblCreateUser->setPosition(Vec2(btnCreateUser->getContentSize().width*0.5, btnCreateUser->getContentSize().height*0.5));
btnCreateUser->addChild(lblCreateUser);
Menu* menu = Menu::create(btnCreateUser, NULL);
menu->setPosition(Vec2(WINCENTER.x, WINSIZE.height*0.9));
this->addChild(menu);
}
// ユーザー作成
void HelloWorld::createUserTapped(cocos2d::MenuItemSprite *menuSpr)
{
// ユーザーIDを取得
std::string aUserID = GSUtilCom::getUserID();
std::string userName = "テスト";
FIBUserBridge* fibUser = FIBUserBridge::create(aUserID, userName);
// ユーザー登録処理
FIBAccountBridge::signupUser(fibUser, [=](FIBUserBridge* user, std::string error)
{
if (error.length()) {
log("登録失敗=%s", error.c_str());
} else {
if (user) {
log("登録しました!");
log("id=%s", user->userID.c_str());
log("name=%s", user->userName.c_str());
log("createTM=%lld", user->createTM);
// データ保持
_userID = user->userID;
}
}
});
}
#include "cocos2d.h"
USING_NS_CC;
class HelloWorld : public cocos2d::Layer
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
private:
// ユーザーID保持
CC_SYNTHESIZE(std::string, _userID, UserID);
// セットアップ
void setup();
// ユーザー作成ボタンタップ処理
void createUserTapped(MenuItemSprite* menuSpr);
};
これでビルドして実行すると、下記のような画面が表示されます。
ユーザー登録ボタンをタップすると、Firebase上にユーザーが書き込まれます。
次回はスコア送信処理を実装します。