▲前のページへ
次のページへ▼

キャラクタ同期(2) RPC通信制御の実装


【目次】
実装前の心得
バトル詳細情報の定数定義
バトル詳細情報の送受信メソッド定義
スタブに展開
送受信用構造体リストの定義
Battleサーバの送受信処理
クライアントの送受信処理
クライアントの送受信処理
バトル終了後にキャラクタを削除する


実装前の心得

実質ここからがモノビットエンジンによる実装になります。
モノビットエンジンはRPCによる通信同期をサポートしており、基本的な作業の流れとしては
以下のような手順になります。
1. どういった情報をサーバーとやり取りするか考える。
2. やり取りする情報を構造体として定義する。
3. やり取りするための関数(メソッド)を定義する。
4. 受信側の関数(メソッド)の内部処理を実装する。
5. 送信側の関数(メソッド)の呼び出しを実装する。

1.についてですが、先にも触れているように、今回やり取りする情報は『キャラクタの姿勢』です。
『キャラクタの姿勢』をデータで表すには、cocos2d-xの場合以下の情報が必要となります。
・キャラクタの座標値。float型×2。
これに加え、クライアント端末上で複数人のリアルタイム同期を実現するためには、以下の情報が欠かせません。
・キャラクタの固有ID。どのプレイヤーがどのキャラクタを操作しているのかを監視するための情報。
ちなみにこの『キャラクタの固有ID』の情報は既に prefork_battle_matching のシステムに組み込み済みですので
それをそのまま利用しましょう。

2, 3, 4, 5 については後述します。

バトル詳細情報の定数定義

■ battle.rb に定数定義を追加する
  モノビットエンジンのRPC通信はRubyスタブコードによって定数定義や関数定義が出来るようになっています。
  battle.rb を開き、その編集を行っていきましょう。
   ※ battle.rb は prefork_battle_matching_lite/tools/rpc 内に存在しています。

  開いたら、以下の定数定義を 65行目付近 に挿入します。
	# バトル詳細情報定数定義
	structure( :BattlePlayerInfo ) do
		member( :chara_id, :uint64 )		# キャラクタID
		member( :position, :float32, 3 )	# 座標値
		member( :rotation, :float32, 4 )	# 回転量
		member( :animId, :int32 )			# アニメーションID
	end
  

  今回は、先に公開している Unity版と互換を取るため以下の情報を定義しています。
・キャラクタの固有ID。どのプレイヤーがどのキャラクタを操作しているのかを監視するための情報。UInt64型。
・キャラクタの座標値。Vector3型 = float型×3。
・キャラクタの回転量。Quartanion型 = float型×4。
・キャラクタのアニメーションの種類。今回の場合 Animator Controller で使用しているので Int型。
  実際に使われるのは、キャラクタの固有IDと、キャラクタの座標値(2DなのでXY座標のみ)です。

バトル詳細情報の送受信メソッド定義

■ battle.rb に送受信メソッドを追加する
  続けて、上記 BattlePlayerInfo を送受信する関数(メソッド)の定義を追加します。
  後述になりますが、この送受信はほぼチャット送受信と似た内容になります。

  まずは battle.rb にて「:Chat」で検索して探してください(上記の定数定義を追加した段階で 150行目付近にあると思います)。
  その記述の上側に、以下のメソッド定義を挿入してください。
		function( :c2s, :PlayerInfo ) do
			argument( :playerInfo, :BattlePlayerInfo )
			retfunc( :playerInfo, :BattlePlayerInfo )
		end
  

  BattlePlayerInfo の情報を通じて、クライアント→サーバ および サーバ→クライアントの送受信を実装します。
  送受信しあっている情報は文字列チャットと異なりますが、イメージとしては似た内容になります。
・あるクライアントからサーバに対し、自身が制御しているキャラクタ姿勢情報を送信する。
・サーバは受信したら、送信してきたクライアント以外の全クライアントに対し、キャラクタ姿勢情報を送信する。
・クライアントはサーバから受信したら、それを「非プレイヤーキャラクタ」として姿勢制御を反映させる。
  これだけで実は今回の導入編は組み込めます。


スタブに展開

■ RPCスタブコードを生成する
  battle.rbを編集し終えたら、同フォルダ内に含まれている convert.bat をダブルクリックしスタブコードを生成してください。
  

  実行し終えたら、outフォルダにRPCコードが生成されます。
  これを copy_file.bat を実行することで、server および client にコピーします。
  


送受信用構造体リストの定義

■ RPCで送受信されるデータの構造体リストを作成する
  続けて、RPC で送受信されるデータを構造体リストで保持するための情報群を、クライアント側で用意します。

  Eclipse にある prefork_battle_matching プロジェクト内の Classes/ClientScene04.h を開き、
  まずは、MonobitRealtimeEngine の名前空間を使用するための宣言をを記述します。
MLN_NAMESPACE_USE
  

  上記を宣言した上で、 ClientScene04.h 内の ClientScene04 宣言部分に以下の項目を追加します。
    ※ 自分以外のユーザーのキャラクタデータを保持するための構造体リストを宣言します。
        // サーバからの同期情報
        class       RecvInfo
        {
        public:
            clock_t                                 receiveTime;    // 最後に受信したときの時間
            BTL::BattlePlayerInfo   info;                   // 受信情報
            cocos2d::Point                  target;                 // 移動目標地点
            cocos2d::Sprite *               pSprite;                // 自身のスプライト情報

            RecvInfo()
            {
                receiveTime = clock();
                target = cocos2d::Point(400,240);
                pSprite = NULL;
            }
        };
        static  std::map<uint64, RecvInfo>      m_ReceiveList;
  

■ m_ReceiveList の実体を生成する
  上記で宣言している m_ReceiveList は静的変数(グローバル変数扱い)ですので、実体の宣言が別途必要になります。
  ClientScene04.cpp を開き、冒頭に以下の1文を加えてください。
std::map<uint64, ClientScene04::RecvInfo>		ClientScene04::m_ReceiveList;
  



Battleサーバの送受信処理

■ Battleサーバの送受信処理を記述する
  続けて、サーバプログラムでの送受信処理に移ります。

  prefork_battle_matching_lite/server/battle/src フォルダを開き、その中の RPC_BTL_Battle.hpp の 51行目あたりに
  以下のメソッドの宣言を行ってください。
        virtual void Recv_PlayerInfo( uint64 conid, BTL::BattlePlayerInfo &playerInfo );
  

  続けて、同フォルダにある RPC_BTL_Battle.cpp の 127行目あたりに、メソッドの実装を追加します。
void RPC_BTL_Battle::Recv_PlayerInfo( uint64 conid, BTL::BattlePlayerInfo &playerInfo )
{
        for ( CharaInfoMap::iterator itr = m_CharaInfoMap.begin(); m_CharaInfoMap.end() != itr; ++itr ){
                CharaInfo* pInfo = &(itr->second);

                // 自分以外の全員に通知
                if( pInfo->rpcId != conid )
                {
                        DNALOG_DEBUG( "Send_PlayerInfoResult CharaId=0x%llx RpcId=0x%llx ClientId=0x%llx",
                                pInfo->charaId, pInfo->rpcId, pInfo->clientId );
                        GetInterface_Battle( pInfo->rpcId ).Send_PlayerInfoResult( playerInfo );
                }
        }
}
  

  先の説明にもあるとおり、
・サーバは受信したら、送信してきたクライアント以外の全クライアントに対し、キャラクタ姿勢情報を送信する。
  ように作り上げます。既にチャットでは
・サーバは受信したら、全クライアントに対し、文字列情報を送信する。
  を組み上げていますので、これを「送信してきたクライアント以外」「キャラクタ姿勢情報」を変更することで
  受信処理の中身が組み込まれることになります。


■ RoomHubクライアントの受信処理を記述する(ダミー)
  一方こちらはダミーコードになりますが、RoomHubクライアントにも受信処理を組み込みます。
  prefork_battle_matching_lite/server/battle/src フォルダを開き、RPC_BTL_RoomHub.hpp の 64行目付近に
  以下の宣言を追加してください。
        virtual void Recv_PlayerInfoResult( uint64 conid, BTL::BattlePlayerInfo &player_info );
  

  続けて、同フォルダにある RPC_BTL_RoomHub.cpp の 258行目あたりに、メソッドの実装を追加します。
void RPC_BTL_RoomHub::Recv_PlayerInfoResult( uint64 conid, BTL::BattlePlayerInfo &player_info )
{
        DNALOG_DEBUG_D( "Recv_PlayerInfoResult" );
}
  


クライアントの受信処理

■ クライアントの受信処理を組み込む
  cocos2d-x に戻り、クライアント側の送受信処理を組み込みましょう。
  まずは受信処理からです。

  prefork_battle_matching プロジェクトの Classes/sample/pu/GameRpcClientPU.hpp を開き、以下の関数を宣言してください。
        virtual void Recv_PlayerInfoResult( uint64 conid, BTL::BattlePlayerInfo &playerInfo );
  

  続けて、受信処理にて ClientScene04 クラス内にて宣言した構造体リスト m_ReceiveList に
  受信情報を代入するために、 ClientScene04.h をインクルード宣言します。

  Classes/sample/pu/GameRpcClientPU.cpp を開き、冒頭に以下の1文を追加してください。
#include        "../../ClientScene04.h"
  

  ヘッダーをincludeしたところで、実際の受信処理を記述します。
  受信処理は至ってシンプルで、構造体リスト m_ReceiveList に受信したデータを保存しておくだけです。
void GameRpcClientPU::Recv_PlayerInfoResult( uint64 conid, BTL::BattlePlayerInfo &playerInfo )
{
        // 受信データを代入する
        ClientScene04::m_ReceiveList[playerInfo.chara_id].info = playerInfo;

        // 受信時間を代入
        ClientScene04::m_ReceiveList[playerInfo.chara_id].receiveTime = clock();
}
  


クライアントの送信処理

■ クライアントの送信処理を組み込む
  続けてクライアントの送信処理です。
  Classes/sample/pu/GameRpcClientPU.hpp に戻り、以下の関数を宣言します。
        virtual void SendPlayerInfo( BTL::BattlePlayerInfo info );
  

  関数の宣言をしましたので、今度は関数の実体を作成します。
  Classes/sample/pu/GameRpcClientPU.cpp に移り、以下の関数を実装します。
void GameRpcClientPU::SendPlayerInfo( BTL::BattlePlayerInfo info )
{
        GetInterface_Battle( m_RpcConnector.GetRPCID() ).Send_PlayerInfo( info );
}
  

  送信処理本体は上述のようになりますが、この SendPlayerInfo パラメータに「自身が動かしている
  プレイヤーキャラクタの情報」を与える導線が必要になります。

  今度は Classes/sample/core/Client.h に移ります。
  クラスの宣言部に以下の関数を追加してください。
    void SendPlayerInfo( BTL::BattlePlayerInfo info );
  

  そしてお決まりですが、この関数の実装を行ないます。
void Client::SendPlayerInfo(BTL::BattlePlayerInfo info)
{
        m_pPU->SendPlayerInfo(info);
}
  

  これで送受信のコア部分については完成しました。


▲前のページへ
次のページへ▼