Cocos2d-xとFirebaseでゲームランキングを作る #2

[前回](http://qiita.com/h-takauma/items/679efc0a72a02787826f)の続きです。
今回は、スコアを送信する所まで記述します。


#スコアクラス作成
次にスコアクラスを作成します。
ClassesフォルダのFirebaseフォルダに`FIBScoreBridge.h`を作成しましょう。

![s170.png](https://qiita-image-store.s3.amazonaws.com/0/154355/016fb83e-8861-ed07-ab2f-cc7306a707ce.png)

```objc:FIBScoreBridge.h
#include "cocos2d.h"

USING_NS_CC;

class FIBScoreBridge;

using FIBScoreCallback = std::function<void (const std::string &)>;

class FIBScoreBridge
{
public :
    
    // スコア送信処理
    static void submitScore(const std::string& userID, const long long int score, FIBScoreCallback callback);
};
```

次にiOSフォルダのFirebaseフォルダに下記を新規作成しましょう。
こちらも拡張子が`.mm`なので気をつけましょう。

- FIBScoreBridge.mm
- FIBScore.mm
- FIBScore.h

![s171.png](https://qiita-image-store.s3.amazonaws.com/0/154355/928ea4d3-ec5a-ad1a-4bac-57b4e6b08665.png)

```objc:FIBScoreBridge.mm
#import "FIBScoreBridge.h"
#import "FIBScore.h"

void FIBScoreBridge::submitScore(const std::string& userID, const long long int score, FIBScoreCallback callback)
{
    NSString *aUserID = [NSString stringWithCString:userID.c_str()
                                           encoding:[NSString defaultCStringEncoding]];
    
    // 対象ユーザーのスコアを送信する
    [FIBScore submitScoreWithCompletion:aUserID score:score completion:^(NSError *error) {
        if (error != nil) {
            callback("error");
        } else {
            callback("");
            
        }
    }];
}
```
```objc:FIBScore.mm
#import "FIBScore.h"
#import "Firebase.h"

@implementation FIBScore

+(void) submitScoreWithCompletion:(NSString *)userID score:(long long)score completion:(FIBScoreCompletionHandler)completion
{
    FIRDatabaseReference* ref = [[FIRDatabase database] reference].ref;
    
    [[[[ref child:@"score_ranking"] child:userID] child:@"score"]
     observeSingleEventOfType:FIRDataEventTypeValue
     withBlock:^(FIRDataSnapshot * _Nonnull snapshot)
     {
         // 現在のスコア取得
         if (snapshot.value != [NSNull null]) {
             long long cloudScore = [snapshot.value longLongValue];
             
             // Firebaseの値より大きければ更新する
             if (cloudScore < score) {
                 // スコア送信
                 [[[ref child:@"score_ranking"] child:userID]
                  setValue:@{@"score": [NSNumber numberWithLongLong:score]}
                  withCompletionBlock:^(NSError * _Nullable error, FIRDatabaseReference * _Nonnull ref)
                  {
                      completion(error);
                  }];
                 
             } else {
                 // スコア更新されない
                 completion(nil);
             }
         } else {
             // スコア未送信
             [[[ref child:@"score_ranking"] child:userID]
              setValue:@{@"score": [NSNumber numberWithLongLong:score]}
              withCompletionBlock:^(NSError * _Nullable error, FIRDatabaseReference * _Nonnull ref)
              {
                  completion(error);
              }];
             
         }
     }];
    
}
@end 
```

```objc:FIBScore.h
@class FIBScore;

typedef void (^FIBScoreCompletionHandler)(NSError *error);

@interface FIBScore : NSObject

+(void) submitScoreWithCompletion:(NSString * )userID
                            score:(long long int)score
                       completion:(FIBScoreCompletionHandler)completion;
@end 
```

#スコア送信処理を記述する
実際の処理を`HelloWorldScene.cpp`に記述しましょう

![s150.png](https://qiita-image-store.s3.amazonaws.com/0/154355/e9e107de-b5f8-5b5a-1071-66fc1d3f6495.png)

```cpp:HelloWorldScene.cpp
#include "HelloWorldScene.h"
#include "GSUtilCom.h"
#include "FIBUserBridge.h"
#include "FIBAccountBridge.h"
#include "FIBScoreBridge.h"

#define WINSIZE Director::getInstance()->getWinSize()
#define WINCENTER Point(WINSIZE.width*0.5, WINSIZE.height*0.5)

HelloWorld::HelloWorld()
: _userID("")
{
}

HelloWorld::~HelloWorld()
{
    
}

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);

    // スコア送信
    Sprite* btnSendScore_ON = Sprite::create();
    btnSendScore_ON->setTextureRect(Rect(0, 0, 200, 100));
    btnSendScore_ON->setColor(Color3B::GRAY);
    
    Sprite* btnSendScore_OFF = Sprite::create();
    btnSendScore_OFF->setTextureRect(Rect(0, 0, 200, 100));
    btnSendScore_OFF->setColor(Color3B(200,0,0));
    
    MenuItemSprite* btnSendScore = MenuItemSprite::create(btnSendScore_OFF,
                                                          btnSendScore_ON,
                                                          [this](Ref* sender) {this->sendScoreTapped((MenuItemSprite*)sender);});
    Label* lblSendScore = Label::createWithSystemFont("スコア送信", "arial", 26);
    lblSendScore->enableOutline(Color4B::BLACK, 1);
    lblSendScore->setPosition(Vec2(btnSendScore->getContentSize().width*0.5, btnSendScore->getContentSize().height*0.5));
    btnSendScore->addChild(lblSendScore);
    
    Menu* menu = Menu::create(btnCreateUser, btnSendScore, NULL);
    menu->alignItemsHorizontallyWithPadding(50);
    menu->setPosition(Vec2(WINCENTER.x, WINSIZE.height*0.9));
    this->addChild(menu);
}

// ユーザー作成
void HelloWorld::createUserTapped(cocos2d::MenuItemSprite *menuSpr)
{
    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;
            }
        }
    });

}

// スコア送信
void HelloWorld::sendScoreTapped(cocos2d::MenuItemSprite *menuSpr)
{
    // ユーザー登録されていればスコア送信
    if (_userID.length() != 0) {
        
        FIBScoreBridge::submitScore(_userID, 100, [=](std::string error)
        {
            if (error.length() == 0) {
                log("スコア送信完了!");
            } else {
                log("スコア送信失敗");
            }
        });

    }
}
```

```cpp:HelloWorldScene.h
#include "cocos2d.h"

USING_NS_CC;

class HelloWorld : public cocos2d::Layer
{
public:
    static cocos2d::Scene* createScene();

    HelloWorld();
    ~HelloWorld();
    
    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);
    
    // スコア送信処理
    void sendScoreTapped(MenuItemSprite* menuSpr);
    
};
```

ビルドして実行してください。ユーザー作成をした後、
スコア送信ボタンを押してください。

![s180.PNG](https://qiita-image-store.s3.amazonaws.com/0/154355/1898738d-9619-4258-5bea-a34519fcca1e.png)

Firebase上にスコアが送信されたことが確認出来ると思います。
![s181.png](https://qiita-image-store.s3.amazonaws.com/0/154355/61caf392-a3c1-bf47-f7b9-c788c7fa407f.png)

[次回](http://qiita.com/h-takauma/items/47c3eeda618bead3122e)はこの送信されたスコアを取得したいと思います。