关于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库)
#pragma once
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include "people.pb.h"
#pragma comment(lib, "libprotobuf.lib")
#pragma comment(lib, "libprotoc.lib")
#include <string>
#include <iostream>
#include "person.pb.h" using namespace std;
class Server
{
private:
boost::asio::io_service& m_ios;
boost::asio::ip::tcp::acceptor m_apt;
typedef boost::shared_ptr<boost::asio::ip::tcp::socket> sock_prt;
int ncount;
public:
Server(boost::asio::io_service& nios)
:m_ios(nios),
m_apt(m_ios,boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),)),
ncount()
{
Start();
}
void Start()
{
sock_prt sock(new boost::asio::ip::tcp::socket(m_ios));
m_apt.async_accept(*sock,
boost::bind(&Server::accept_callback,this,boost::asio::placeholders::error,sock)
);
}
void accept_callback(const boost::system::error_code& e,sock_prt sock)
{
if(e)
{
cout<<"connect error"<<endl;
return;
}
cout<<"client:"<<sock->remote_endpoint().address()<<endl;
Person per;
per.set_email("calvin@etsoma.com");
per.set_id(ncount++);
per.set_name("calvin");
/*CPFS::People p;
p.set_email("calvin@etsoma.com");
p.set_id(ncount++);
p.set_name("calvin");*/
string strData;
per.SerializePartialToString(&strData);
char data[];
strcpy(data,strData.c_str()); sock->async_write_some(boost::asio::buffer(data),
boost::bind(&Server::write_callback,this,boost::asio::placeholders::error)
);
Start(); }
void write_callback(const boost::system::error_code& e)
{
if(e)
{
cout<<"write error"<<endl;
return;
} }
};
在main函数中调用
try
{
cout<<"server start"<<endl;
boost::asio::io_service mios;
Server srv(mios);
mios.run();
}
catch (exception& e)
{
cout<<e.what()<<endl;
}
好了,到目前为止,服务端就搭建好了。
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, );
size_t len;
const uint8_t* buffer = (uint8_t*)luaL_checklstring(L, , &len);
size_t pos = luaL_checkinteger(L, );
uint8_t out[];
buffer += pos;
c)新建一个pocoClient.h文件,代码如下 (使用了poco库)
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/SocketAddress.h"
#define BUFFER_SIZE 1024
#include <iostream>
using Poco::Net::SocketAddress;
using Poco::Net::StreamSocket;
#include "CCLuaEngine.h"
#include "cocos2d.h"
extern "C"{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
};
class PocoClient
{
public:
void GetData()
{
SocketAddress address("127.0.0.1", );
StreamSocket socket(address);
char buffer[BUFFER_SIZE];
while (true)
{
if (socket.available())
{
int len = socket.receiveBytes(buffer, BUFFER_SIZE);
buffer[len] = '\0';
std::cout << "" << buffer << std::endl;
LuaFunction(buffer);
}
}
}
protected:
private:
void LuaFunction(const char* str)
{
lua_State* plua=cocos2d::CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();
int result = -;
lua_getglobal(plua, "getdata");
if(!lua_isfunction(plua,))
{
return ;
}
lua_pushstring(plua, str);
int n= lua_pcall(plua, , ,);
result = (int)lua_tonumber(plua, -);
lua_pop(plua, );
printf("The result is %d\n", result);
}
};
以及代码是从服务端获取数据后,然后调用lua文件里面的getdata方法(注:getdata是全局唯一的,并且该函数所在的文件需要在main.lua里面进行require,不然无法识别到此lua函数)。
getdata函数lua代码如下
function getdata(str)
require "person_pb"
local person_pbeee=person_pb.Person()
person_pbeee:ParseFromString(str);
print(person_pbeee.id..person_pbeee.name..person_pbeee.email)
return ;
end
在AppDelegate.cpp文件开始部分加入
extern "C"{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
int luaopen_pb(lua_State *L);
}
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文件
关于Protobuf在游戏开发中的运用的更多相关文章
- [Unity游戏开发]向量在游戏开发中的应用(三)
本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/51088236 在上一篇博客中讲了利用向量点乘在游戏开发中应用的几种情景.本 ...
- [Unity游戏开发]向量在游戏开发中的应用(二)
本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/50972976 在上一篇博客中讲了利用向量方向的性质来解决问题.这篇博客将继 ...
- [Unity游戏开发]向量在游戏开发中的应用(一)
本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/50810102 向量在游戏开发中是非常实用的,我们在学校学完向量的知识后,只 ...
- Cocos2d-x游戏开发中的消息机制:CCNotificationCenter的使用
在HTML5游戏开发中,js可以使用Event对象的addEventListener(添加事件监听).dispatchEvent(触发事件)实现监听机制,如果在coocos2d-x中,去实现这种机制该 ...
- 二、Cocos2dx概念介绍(游戏开发中不同的坐标系,cocos2dx锚点)
注:ccp是cocos2dx中的一个宏定义,#define ccp(__X__,__Y__)CCPointMake((float)__X__, (float)__Y__),在此文章中表示坐标信息 1. ...
- [C++基金会]位计算 游戏开发中的应用
定义的位操作:通俗点说,,位计算是计算机操作二进制整数. 无论整数可以用二的方式来表示进度,不同类型的其长度的整数位的是不一样的.INT8要么char靠8个月2 位表示,INT16或者short是由1 ...
- 在基于TypeScript的LayaAir HTML5游戏开发中使用AMD
在基于TypeScript的LayaAir HTML5游戏开发中使用AMD AMD AMD是"Asynchronous Module Definition"的缩写,意思就是&quo ...
- 借助AMD来解决HTML5游戏开发中的痛点
借助AMD来解决HTML5游戏开发中的痛点 游戏开发的痛点 现在,基于国内流行引擎(LayaAir和Egret)和TypeScript的HTML5游戏开发有诸多痛点: 未采用TypeScript编译器 ...
- 游戏开发中IIS常见支持MIME类型文件解析
游戏开发中IIS常见支持MIME类型文件解析 .apkapplication/vnd.android .ipaapplication/vnd.iphone .csbapplication/octet- ...
随机推荐
- 聊聊 Xcode 项目文件中的 project.pbxproj
project.pbxproj 文件被包含于 Xcode 工程文件 *.xcodeproj 之中,存储着 Xcode 工程的各项配置参数.它本质上是一种旧风格的 Property List 文件,历史 ...
- Linux 下configure 参数配置与软件的安装与卸载
Linux环境下的软件安装,并不是一件容易的事情:如果通过源代码编译后在安装,当然事情就更为复杂一些:现在安装各种软件的教程都非常普遍:但万变不离其中,对基础知识的扎实掌握,安装各种软件的问题就迎刃而 ...
- uiscrollerview循环滚动(参考第三方库:HMBannerView)https://github.com/iunion/autoScrollBanner
#import <UIKit/UIKit.h> #import "HMBannerView.h" @interface ViewController : UIViewC ...
- Unity3D 使用 UI 的 Grid Layout Group 组件。
1.首先创建一个容器,用于存放列表项的内容. 这里使用 Panel 来做为容器. 这里要注意! “Grid Layout Group”是要增加在容器的游戏对象里. 同时,只有容器对象的子对象才有排列效 ...
- 对《[Unity官方实例教程 秘密行动] Unity官方教程《秘密行动》(十二) 角色移动》的一些笔记和个人补充,解决角色在地形上移动时穿透问题。
这里素材全是网上找的. 教程看这里: [Unity官方实例教程 秘密行动] Unity官方教程<秘密行动>(九) 角色初始设定 一.模型设置: 1.首先设置模型的动作无限循环. 不设置的话 ...
- [PHP] htaccess 探秘
.htaccess访问控制(Allow/Deny) 1. 验证是否支持.htaccess 在目录下新建一个.htaccess 文件,随笔输入一串字符(毫无意义),看看什么反应,如果是500错误,说明目 ...
- Android之旅:梦想、学习、坚持、自信、淡定
前段时间参加了2012年度IT博客大赛,进了前十强,写了一篇获奖感言,不过还没正式在CSDN发表出来.眼看2012年就要结束了,刚好借这个机会将2012年度IT博客大十强获奖感言发表出来,也算是对20 ...
- maven 添加中央仓库中没有的jar
jar文件需要放到当前目录下,执行以下命令,设置 groupId,artifactId,version信息,方便项目pom引用 mvn install:install-file -Dfile=xxx. ...
- nginx 代理服务器 502错误
在centos系统下,nginx做代理服务器总是出现502错误,百度各种搜索,出来的答案基本都是一样的,也不知道大家 从哪抄的,问题也没有解决,最后还是从谷歌找到的答案: 总归还是centos系统的问 ...
- 磁珠 磁环 双向二极管 TVS二极管
磁珠专用于抑制信号线.电源线上的高频噪声和尖峰干扰,还具有吸收静电脉冲的能力.磁珠是用来吸收超高频信号,像一些RF电路,PLL,振荡电路,含超高频存储器电路(DDR SDRAM,RAMBUS等)都需要 ...