关于Protobuf在游戏开发中的运用

  最近在研究protobuf在项目中的使用,由于我们项目服务端采用的是C++,客户端是cocos2dx-cpp,客户端与服务端的消息传输是直接对象的二进制流。如果客户端一直用C++来写,问题到不大,但是后期有想法将客户端用lua来写(可以实现苹果平台的新增更新),这个时候问题就出现了(传输的消息定义无法在lua中得到扩展)。这个时候就想到了protobuf。

  protobuf原本是google的一个开源项目(网上有很多资料),它的功能是将类似的对象(class)转化成字符串,而且这个字符串比json数据少很多,解析也快了很多。为什么说类似,因为它所需的.proto文件的定义与C++中的class有点区别。

  下面我将介绍项目中使用protobuf。

  1、下载所需资源

    a) protobuf2.4.1 版本的下载(将定义的.proto文件转成 xx_pb.cc和 xx_pb_h)

    b) protoc-gen-lua的下载(将定义的.proto文件转成 xx_pb.lua)

  2、protobuf的使用(windows)

    a)解压下载的protobuf2.4.1文件到C:\protobuf-2.4.1(这个位置可以随便),进入在该目录下的vsprojects文件夹,用vs打开protobuf.sln,进行编译(最好一个一个编译),如果报错,可参考http://www.cnblogs.com/cindyOne/p/protobuf.html

编译得到的protoc.exe,libprotobuf.lib,libprotobuf-lite.lib,libprotoc.lib会在项目中用到。

    b)将.proto转成xx_pb.h和xx_pb.cc的方法是:将上步骤得到的
protoc.exe拷贝到.proto文件所在的文件夹,创建一个bat文件,里面写入protoc.exe --proto_path=./
--cpp_out=./ ./person.proto   
pause(注:person.proto  是该文件夹下需要转化proto文件)。执行bat文件后,就得到了xx_pb.h和xx_pb.cc文件。

    c)在项目中使用xx_pb.h和xx_pb.cc文件。创建一个c++的控制台程序,项目属性中设置

红线框是下载的protobuf的解压路径。

   将xx_pb.h和xx_pb.cc文件加入项目工程。创建一个Server.h文件。贴入代码(该项目中我引用了boost库)

 1 #pragma once
2 #include <boost/asio.hpp>
3 #include <boost/bind.hpp>
4 #include <boost/shared_ptr.hpp>
5 #include "people.pb.h"
6 #pragma comment(lib, "libprotobuf.lib")
7 #pragma comment(lib, "libprotoc.lib")
8 #include <string>
9 #include <iostream>
10 #include "person.pb.h"
11
12
13 using namespace std;
14 class Server
15 {
16 private:
17 boost::asio::io_service& m_ios;
18 boost::asio::ip::tcp::acceptor m_apt;
19 typedef boost::shared_ptr<boost::asio::ip::tcp::socket> sock_prt;
20 int ncount;
21 public:
22 Server(boost::asio::io_service& nios)
23 :m_ios(nios),
24 m_apt(m_ios,boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),6688)),
25 ncount(0)
26 {
27 Start();
28 }
29 void Start()
30 {
31 sock_prt sock(new boost::asio::ip::tcp::socket(m_ios));
32 m_apt.async_accept(*sock,
33 boost::bind(&Server::accept_callback,this,boost::asio::placeholders::error,sock)
34 );
35 }
36 void accept_callback(const boost::system::error_code& e,sock_prt sock)
37 {
38 if(e)
39 {
40 cout<<"connect error"<<endl;
41 return;
42 }
43 cout<<"client:"<<sock->remote_endpoint().address()<<endl;
44 Person per;
45 per.set_email("[email protected]");
46 per.set_id(ncount++);
47 per.set_name("calvin");
48 /*CPFS::People p;
49 p.set_email("[email protected]");
50 p.set_id(ncount++);
51 p.set_name("calvin");*/
52 string strData;
53 per.SerializePartialToString(&strData);
54 char data[100];
55 strcpy(data,strData.c_str());
56
57 sock->async_write_some(boost::asio::buffer(data),
58 boost::bind(&Server::write_callback,this,boost::asio::placeholders::error)
59 );
60 Start();
61
62 }
63 void write_callback(const boost::system::error_code& e)
64 {
65 if(e)
66 {
67 cout<<"write error"<<endl;
68 return;
69 }
70
71 }
72 };

在main函数中调用
  

  

 1 try
2 {
3 cout<<"server start"<<endl;
4 boost::asio::io_service mios;
5 Server srv(mios);
6 mios.run();
7 }
8 catch (exception& e)
9 {
10 cout<<e.what()<<endl;
11 }

好了,到目前为止,服务端就搭建好了。

  3、protoc-gen-lua的使用(windows)

     
  
a)将下载好的文件解压C:\protoc-gen-lua,里面有三个文件夹:example,plugin,protobuf。在plugin文件夹下创建protoc-gen-lua.bat文件,写入
@python
"%~dp0protoc-gen-lua",将最开始编译好的protoc.exe文件拷贝到C:\protoc-gen-lua目录下,创建build_for_lua.bat文件,写入(proto_demo是用来存放
proto文件的,方便转化成xx_pb.lua文件)


rem 切换到.proto协议所在的目录
cd C:\protoc-gen-lua\proto_demo
rem 将当前文件夹中的所有协议文件转换为lua文件
for %%i in (*.proto) do (
echo %%i
"..\protoc.exe" --plugin=protoc-gen-lua="..\plugin\protoc-gen-lua.bat" --lua_out=. %%i

)
echo end
pause

  b)执行build_for_lua.bat文件将会得到xx_pb.lua文件,将该lua文件加入到cocos2dx的Resources文件加下,另外将C:\protoc-gen-lua\protobuf目录下的所有lua文件(共9个)全部加入到Resources文件夹下,将C:\protoc-gen-lua\protobuf目录下的pb.c文件加入到class文件夹下。修改pb.c文件的
#include <endian.h>为#ifndef _WIN32  #include <endian.h> 
#endif。 函数struct_unpack中修改switch(format)之前的代码为

uint8_t format = luaL_checkinteger(L, 1);
size_t len;
const uint8_t* buffer = (uint8_t*)luaL_checklstring(L, 2, &len);
size_t pos = luaL_checkinteger(L, 3);
uint8_t out[8];
buffer += pos;

c)新建一个pocoClient.h文件,代码如下 (使用了poco库)

    

 1 #include "Poco/Net/StreamSocket.h"
2 #include "Poco/Net/SocketAddress.h"
3 #define BUFFER_SIZE 1024
4 #include <iostream>
5 using Poco::Net::SocketAddress;
6 using Poco::Net::StreamSocket;
7 #include "CCLuaEngine.h"
8 #include "cocos2d.h"
9 extern "C"{
10 #include "lua.h"
11 #include "lualib.h"
12 #include "lauxlib.h"
13 };
14 class PocoClient
15 {
16 public:
17 void GetData()
18 {
19 SocketAddress address("127.0.0.1", 6688);
20 StreamSocket socket(address);
21 char buffer[BUFFER_SIZE];
22 while (true)
23 {
24 if (socket.available())
25 {
26 int len = socket.receiveBytes(buffer, BUFFER_SIZE);
27 buffer[len] = ‘\0‘;
28 std::cout << "" << buffer << std::endl;
29 LuaFunction(buffer);
30 }
31 }
32 }
33 protected:
34 private:
35 void LuaFunction(const char* str)
36 {
37 lua_State* plua=cocos2d::CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();
38 int result = -1;
39 lua_getglobal(plua, "getdata");
40 if(!lua_isfunction(plua,1))
41 {
42 return ;
43 }
44 lua_pushstring(plua, str);
45 int n= lua_pcall(plua, 1, 1,0);
46 result = (int)lua_tonumber(plua, -1);
47 lua_pop(plua, 1);
48 printf("The result is %d\n", result);
49 }
50 };

  
以及代码是从服务端获取数据后,然后调用lua文件里面的getdata方法(注:getdata是全局唯一的,并且该函数所在的文件需要在main.lua里面进行require,不然无法识别到此lua函数)。

getdata函数lua代码如下

1 function getdata(str)
2 require "person_pb"
3 local person_pbeee=person_pb.Person()
4 person_pbeee:ParseFromString(str);
5 print(person_pbeee.id..person_pbeee.name..person_pbeee.email)
6 return 1;
7 end

在AppDelegate.cpp文件开始部分加入

1 extern "C"{
2 #include <lua.h>
3 #include <lualib.h>
4 #include <lauxlib.h>
5 int luaopen_pb(lua_State *L);
6 }

applicationDidFinishLaunching()函数中加入luaopen_pb(tolua_s);并在
 pEngine->executeScriptFile(path.c_str());代码后加入PocoClient
p;p.GetData();就可以调用方法,实现客户端和服务端的通信了。

    4、开发注意事项

   
a)在调用lua函数的时候,要得到相同的lua环境(与最开始hello.lua(创建cocos2dx-lua
自带的)环境一直),也许要在hello.lua中去引用 该函数所在的文件以及 xx_pb.lua文件

    

时间: 2024-11-05 04:27:31

关于Protobuf在游戏开发中的运用的相关文章

游戏开发中,图片资源的精简

在游戏开发中,包的大小总是与图片资源的大小密切相关,而图片资源中,大多为带有透明度信息的png图像. 那么,如何精简png图片资源呢? 1.图像压缩是一种方法,然而随着压缩率的增大.图片品质也越来越差.(舍弃) 2.我们另辟蹊径,采用png图像拆分.(近乎无损,资源精简) 一.原理:将png图像转化为两张jpeg图像进行存储 pngSplit下载 pngSplit使用说明 二.使用方法: 1.LibGdx中,通过Pixmap使用 // 如工程目录assets/texture/0_1.jpeg下:

游戏开发中的人工智能 复习

游戏开发中的人工智能 复习 (个人复习,一些仅是给自己的复习提示(=w=),转载注明出处:http://blog.csdn.net/hcbbt/article/details/42815479) 配套教材:游戏开发中的人工智能 知识点 移动 Bresenham,视线(略),拦截 // Bresenham if (deltaCol > deltaRow) { fraction = deltaRow * 2 - deltaCol; while (nextCol != endCol) { if (fr

&lt;游戏开发中的人工智能&gt; -- 阅读笔记

到家已经几天了, 休息了一阵, 是时候重新学习知识了. 接下去一段时间, 会啃<游戏开发中的人工智能>这本书, 顺便写写笔记. 马上就大三了, 想想自己选的游戏方向, 现在还蛋疼. 选了一个自己喜欢的方向, 但是确实最忙的一个,这也意味着少时间继续我的iOS学习. 也不知道是对是错. 既然选了,就学吧. 好不,不扯多了.接下去是该系列的笔记.(持续更新) 第一章: 游戏人工智能简介 1. 定性AI与非定性AI 定性行为或其表现是特定的,而且是可预测的,没有不确定性. 非定性行为有某种程度的不确

游戏开发中的一些基本方法

一.              检测对象变化的两种基本方式: 学过<微机原理>的人应该都了解这两种方式 1.       轮询 1) 每帧轮询 2) 定时轮询 按业务需求和性能问题选择 2.       中断(并非硬件中断,而是软件的事件通知方式) 两种模式: 1)       观察者模式 优点:① 扩展性强,事件发起接口不变,只需增加事件类型 ② 只通知对某件事有兴趣的对象,不会浪费性能 ③ 每种事件对应一种回调函数,对于回调函数,事件参数类型是固定的,MouseMove事件的参数类型肯定是

c++实现游戏开发中常用的对象池(含源码)

c++实现游戏开发中常用的对象池(含源码) little_stupid_child2017-01-06上传 对象池的五要素: 1.对象集合 2.未使用对象索引集合 3.已使用对象索引集合 4.当前使用量 5.最大使用量 http://download.csdn.net/download/little_stupid_child/9730912

Cocos2d-x手机游戏开发中-组合动作

动作往往不是单一,而是复杂的组合.我们可以按照一定的次序将上述基本动作组合起来,形成连贯的一套组合动作.组合动作包括以下几类:顺序.并列.有限次数重复.无限次数重复.反动作和动画.动画我们会在下一节介绍,本节我们重点顺序.并列.有限次数重复.无限次数重复和反动 下面我们通过一个实例介绍一下组合动作的使用,这个实例如下图所示,下图是一个操作菜单场景,选择菜单可以进入到下图动作场景,在下图动作场景中点击Go按钮可以执行我们选择的动作效果,点击Back按钮可以返回到菜单场景. 下面我们再看看具体的程序

游戏开发中的矩阵初探

游戏开发中的矩阵初探 1.矩阵在3d空间中的作用 (1)长方体A想绕(10,3,4)旋转50°且沿着x方向放大2倍且向(9,-1,8)方向平移2个单位,那么经过上面的变换后,新的长方体各个点的坐标是多少呢?应用矩阵可以很轻松的算出答案. (2)知道子坐标系在父坐标系中的位置,可以求出子坐标系中的店在父坐标系中的位置. 2.矩阵的基础知识 矩阵能描述任意线性变换.线性变换保留了直线和平行线,线性变换保留直线的同时,其他的几何性质如长度.角度.面积和体积可能被变换改变了.简单的说,线性变换可能“拉伸

[C++基础]位运算 游戏开发中的应用

位运算的定义:通俗点说,位运算就是对一个整数在计算机中二进制进行操作. 任何一个整数都可以用二进度的方式来表示的,不同类型的整数它的位数的长度也不一样,INT8或者char是由8个2进度 位表示,INT16或者short是由16个2进度位表示,INT32是由32位状态位表示. 位运算在游戏中的应用 往往,在游戏开发中做位运算的时候,我关注的主要是某一位的值是0,还是1,而并不是去关注这个整数的值是多少. 比如:00100010,这个8位的整数从右到左,它的第一位为0,第二位为1,第三位为0,第六

游戏开发中可能用到的【6个等级】

游戏开发中可能用到的[6个等级] 男爵.子爵.伯爵.侯爵.公爵.亲王 车神.车王.车侠.车迷.车夫.车屌(可以别的字眼替换如"赌"替换掉"车") 迷梦.觉醒.追影.怀旧.失忆.成才 皇帝.皇后.宰相.将军.宫女.太监 砖家.学神.学霸.学弱.学渣.文盲 土豪.员外.富农.贫农.长工.短工 皇帝.皇后.贵妃.常在.答应.宫女 土炮.铁炮.钢炮.银炮.金炮.神炮 呦喂.我去.花擦.次奥.尼玛.我日 路人.朋友.好友.挚友.兄弟.老铁 脑残.白痴.逗比.傻瓜.凡人.天才