今天讲一下在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.
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中输入下面命令:
这句命令的意思为 新建一个项目名称为 Game_JJDDZ ,包名为:com.game.jjddz 开发语言为:c++ 目录在 d:/app(后面实例我用的是我自己做好的项目目录:c:/app),
我把game.pb.cc game.pb.h,这两个文件复制到Classes目录中,再用VS打开 pro.win32目录下的 Game_JJDDZ.sln项目,把这两个文件包含进去,
在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 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
另外还要修改一个protobuf文件,C:\app\Game_JJDDZ\Classes\google\protobuf\stubs\common.h
添加一个宏:
#ifndef HAVE_PTHREAD
#define HAVE_PTHREAD
#endif
经过上面操作后就可以编译APK了,进入CMD 输入命令:
经过几分钟等待,当出现下面时表示已编译成功,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处。
如有错误欢迎指导改正。