今天講一下在cocos2d-x-3.17.2項目中配置 protobuf-3.12.0-rc2。
1.下載cocos2d-x-3.17.2:https://www.cocos.com/cocos2dx 解壓到:c:/cocos2d-x-3.17.2
2.下載protobuf-3.12.0-rc2:https://github.com/protocolbuffers/protobuf/releases/,解壓到:c:/protobuf-3.12.0-rc2
要使cocos2d-x-3.17.2能編譯Android 還要配置JDK 、ADK 、 NDK ,這裏就不介紹。
我們首先用CMAKE把protobuf編譯成VS2017 項目:
打開CMAKE,源碼目錄選擇:C:/protobuf-3.12.0-rc2/cmake
編譯目錄選擇:C:/protobuf-3.12.0-rc2/cmake/vs2017(vs2017自己創建)
然後點擊Configure按鈕
這裏選2017,其它版本也可以,根據自己電腦裝的VS爲主。平臺選 win32 然後點Finish
會有錯誤,我們把 BULID_TESTS去掉 然後 點Generate
這樣就編譯成VS項目了
打開目錄C:\protobuf-3.12.0-rc2\cmake\vs2017
用VS2017打開protobuf.sln項目文件:
選擇release win32平臺然後 生成解決方案(之所以先用VS編譯,是爲了方便以後在VS中調試COCOS項目,最後再移置到Android。)
編譯成功後 在 C:\protobuf-3.12.0-rc2\cmake\vs2017\Release 有編譯好的庫和protoc.exe.
我們用記事本創建一個 game.proto文本文件:
syntax = "proto3";
package Game_DDZ;
message Packet{
int32 id = 1;
int32 len= 2;
bytes data= 3;
repeated bytes datas=4;
}
message Player{
int32 desk= 1;
int32 seat= 2;
bool ready=3;
string name=4;
int32 money=5;
int32 imgnum=6;
int32 baseScore=7;
}
message PlayerList{
repeated Player Players= 1;
}
message Cards{
int32 seat=1;
repeated int32 card=2;
}
message CardList{
repeated Cards Card= 1;
}
//一個牌型
message CardNode {
int32 cardType=1;
int32 mainNum=2;
int32 value=3;
int32 seralNum=4;
int32 subNum=5;
float aggregate=6;
repeated int32 cards=7;
}
這裏就是一些數據結構和類型,
package Game_DDZ;//這個相當於命名空間
message Packet{ //相當於 Packet結構體
int32 id = 1;//數字類型
int32 len= 2;//數字類型
bytes data= 3;//字節類型(也可以理解爲字符類型)
repeated bytes datas=4; epeated 相當於類型爲 bytes 的數組,
}//1 2 3 4 代表編號
其實這是個通用結構,可以用來裝所有類型的結構體,數字 字符 數組類型都可以,因爲protobuf中所有的結構體都可以序列爲string , 我們又可以把它裝入 datas中,在網絡傳輸中,知道id是什麼數據包,再爲datas解包爲指定結構。
然後再建一個 game.bat文件內容爲:
protoc --cpp_out=./ game.proto
然後雙擊生成2個文件:game.pb.cc game.pb.h,
portobuf文件就生成了,下面介紹使用方法:
首先我們用cocos生成一個 cpp項目,當然在這之前,你要安裝好 python2.7 Jdk ADK Ndk,在CMD中輸入下面命令:
cocos new Game_JJDDZ -p com.game.jjddz -l cpp -d d:/app
這句命令的意思爲 新建一個項目名稱爲 Game_JJDDZ ,包名爲:com.game.jjddz 開發語言爲:c++ 目錄在 d:/app(後面實例我用的是我自己做好的項目目錄:c:/app),
我把game.pb.cc game.pb.h,這兩個文件複製到Classes目錄中,再用VS打開 pro.win32目錄下的 Game_JJDDZ.sln項目,把這兩個文件包含進去,
再打開 game.pb.h文件加入宏定義 :#define PROTOBUF_USE_DLLS,其它錯誤不用管它。
在VS中配置Protobuf,先把 C:\protobuf-3.12.0-rc2\src 中的 google目錄複製到Classes目錄下,幷包含到項目中,還要引用DLL 和 Lib
把DLL也引入進來
經過上面的操作,VS中就可以使用protobuf 了,現在講如何使用,以網絡打牌,C++Socket爲例:
1.封包:
假如程序有一個結構體:
//牌型結構體
//cardType是牌型,只有三種,王炸,單純,連續;
//value 是牌型的值,單純類型爲牌的面值,連續類型爲起始牌的面值,相同牌型以此比較大小;
//mainNum是主牌張數,比如三帶二和飛機裏mainNum=3, 連對時, mainNum=2;
//seralNum是連續張數,seralNum=1是單純牌型,順子時seralNum>=5;
//subNum是副牌數目,三帶一和四帶二時subNum=1,三帶二和四帶兩對時,subNum=2;
//cards是牌型裏包括的牌的牌值,比如三帶一時,可能就是[3, 16, 42, 4], 連對時,可能就是 [3, 16, 4, 17, 5, 18, 6, 19]等等
//aggregate是權重,根據不同的情況求出權重,再按照權重排序所有牌型。可以是本牌型的權重,也可以是手牌裏除了本牌型外剩下所有牌加在一起的權重。
struct CardNode {
int32_t cardType;
int32_t mainNum;
int32_t value;
int32_t seralNum;
int32_t subNum;
float aggregate;
std::vector<int> cards;
};
game.proto中的定義:
syntax = "proto3";
package Game_DDZ;
message Packet{
int32 id = 1;
int32 len= 2;
bytes data= 3;
repeated bytes datas=4;
}
//一個牌型
message CardNode {
int32 cardType=1;
int32 mainNum=2;
int32 value=3;
int32 seralNum=4;
int32 subNum=5;
float aggregate=6;
repeated int32 cards=7;
}
現在用戶打出了一個牌型爲 mCardNode:
Game_DDZ::Packet gamePacket;
gamePacket.set_id(7);//7爲出牌
gamePacket.set_len(mCardGame->seatId);//mCardGame->seatId 爲打牌人的位置
Game_DDZ::CardNode gameCardNode;
gameCardNode.set_cardtype(mCardNode.cardType);
gameCardNode.set_mainnum(mCardNode.mainNum);
gameCardNode.set_value(mCardNode.value);
gameCardNode.set_seralnum(mCardNode.seralNum);
gameCardNode.set_subnum(mCardNode.subNum);
gameCardNode.set_aggregate(mCardNode.aggregate);
for (int value : mCardNode.cards)
{
gameCardNode.add_cards(value);
}
string cardData;
gameCardNode.SerializePartialToString(&cardData);//把Game_DDZ::CardNode序列化爲要要string
gamePacket.set_data(cardData);//裝入 bytes(data)中 如果mCardNode爲數組 就一個一個裝入:datas中(用 add_datas())。
string packetData;
gamePacket.SerializeToString(&packetData);
mTcpSocket->SendMsg((void *)packetData.c_str(), gamePacket.ByteSize());
mTcpSocket->Flush();
這樣一個包就封好了,並通過Socket發送走了,下面看解包:
2.解包:
char buffer[_MAX_MSGSIZE] = { 0 };
int inlen = recv(mTcpSocket->GetSocket(), buffer, _MAX_MSGSIZE, 0);
if (inlen > 0)
{
Game_DDZ::Packet gamePacket;
gamePacket.ParseFromArray(buffer, inlen);
int cmd = gamePacket.id();
//0 一般信息
//1準備信息
switch (cmd)
{
case 7://接收出的牌
{
Game_DDZ::CardNode gameCardNode;
gameCardNode.ParseFromString(gamePacket.data());
CardNode mycard;
mycard.cardType = gameCardNode.cardtype();
mycard.mainNum = gameCardNode.mainnum();
mycard.value = gameCardNode.value();
mycard.seralNum = gameCardNode.seralnum();
mycard.subNum = gameCardNode.subnum();
mycard.aggregate = gameCardNode.aggregate();
for (int card : gameCardNode.cards())
{
mycard.cards.push_back(card);
}
}
break;
...
...
...
}
}
這樣一個傳輸協議就形成了。
下面講如何移置到Android中,確保安裝了 JDK NDK ADK並設置好環境變量:
網上說的要修改 android.mk 文件,但我測試並不是修改這個文件,並且好像沒有用上:
我只修改項目目錄中的 CMakeLists.txt就可以了:
1.加入項目中所有源文件:
2.加入所有頭文件:
protobuf要加哪些文件可以到 C:\protobuf-3.12.0-rc2\src\Makefile.am中查看找到 libprotobuf_lite_la_SOURCES標記:
有2處。
還有一項要配置:NinJa
下載到c:\ninja-win,並添加環境變量
另外還要修改一個protobuf文件,C:\app\Game_JJDDZ\Classes\google\protobuf\stubs\common.h
添加一個宏:
#ifndef HAVE_PTHREAD
#define HAVE_PTHREAD
#endif
經過上面操作後就可以編譯APK了,進入CMD 輸入命令:
cocos compile -p android
經過幾分鐘等待,當出現下面時表示已編譯成功,build successful:
以上就把單機鬥地主遊戲移置爲網絡遊戲的經歷,
Window版本:Win10 64位
VS版本:2017 Enterprise
cocos2d-x版本:cocos2d-x-3.17.2
Protobuf版本:protobuf-3.12.0-rc2
Android SDK版本:android-18
Android NDK版本:android-ndk-r13b
JDK版本:jdk1.8.0_131
ninj版本:ninj-win
以上一個都不能少,在編譯中也許會有錯誤,我們要注意以下幾點:
1.代碼中不能有除C++11 cocos2d-x以外的數據類型,如VS的大部分數據類型(INT32 CString ...)
2. JDK ADK NDK環境變量要配置好,還有ninja。
3.項目中的所有文件都要包含到時項目根目錄文件CMakeLists.txt中,頭文件也包含進去,protobuf可根據Makefile.am中 libprotobuf_lite_la_SOURCES標記,有2處。
如有錯誤歡迎指導改正。