Quick-cocos2d-x 接入 protobuf

  1. 下载云风的 pbc:

    1
    git clone [email protected]:cloudwu/pbc.git

    把 pbc 下 src 目录复制到 quick 的 lua_extensions 目录下,并重命名为 pbc。

  2. 下载 lpeg,并同样复制到 lua_extensions 下
  3. 下载 struct,并同样复制到 lua_extensions 下
  4. 编辑 lua_extensions\lua_extensions[_more].c 文件,添加:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // protobuf
    #include "pbc/pbc-lua.h"
    // lpeg
    #include "lpeg/lptree.h"
    // struct
    #include "struct/struct.h"
    ... ... static luaL_Reg luax_exts[] = {
    ... ...
    {"lpeg", luaopen_lpeg},
    {"struct", luaopen_struct},
    {"protobuf.c", luaopen_protobuf_c},
    ... ...
  5. 把 pbc,lpeg,struct 目录加入到 xcode 的工程中。比如游戏工程或是 quick-player 的工程

  6. 编译项目。

使用 Cocostudio 创建 listview item

为了快速开发一个小游戏,使用了 Cocostudio 2.0 作为 UI 编辑器。其实 Cocostudio 的功能比较弱,特别是针对稍微复杂一点的控件(Widget),无法做到所见即所得,不得不写一些代码。

游戏中用到了一个 listview,但是 Cocostudio2.0中没有 listview,所以只能写代码实现,这个是没有办法的。好在一个 listview 本身还是比较简单的,但是 listview 中的 item 可以很复杂,是否有办法简化呢?

尝试了很久之后,还真找到了一个迂回的方法,即:

  • 在 Cocostudio 中创建一个新的 Layer
  • 在 Layer 上编辑所需要的 item

这本来是一件比较简单的事情,但是问题在于 listview 的 item 只能是 widget,而 Cocostudio 2.0中 widget 有限,所以,尽量用 button 拼凑吧!

放出 listview 和 listitem 的 lua 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
local LISTVIEW_WIDTH = 340
local LISTVIEW_HEIGHT = 405
local listView = ccui.ListView:create()
-- set list view ex direction
listView:setDirection(ccui.ScrollViewDir.vertical)
listView:setBounceEnabled(true)
-- listView:setBackGroundImage("hall/di.png")
-- listView:setBackGroundImageScale9Enabled(true)
listView:setContentSize(cc.size(LISTVIEW_WIDTH, LISTVIEW_HEIGHT))
listView:setAnchorPoint(cc.p(0, 0))
listView:setPosition(cc.p(10, 12))
listView:addEventListener(listViewEvent)
-- SCISSOR
listView:setClippingType(1)
parent:addChild(listView)

local function createDefaultListItem()
local leaderboardItemLayer = cc.CSLoader:createNode("hall/LeaderboardItem.csb")
-- retrieve the widget. MUST be a widget
local listItemRoot = leaderboardItemLayer:getChildByTag(2001)
listItemRoot:removeFromParent()

local listItemRootSize = listItemRoot:getContentSize()
listItemRoot:setPosition(cc.p(LISTVIEW_WIDTH / 2.0, listItemRootSize.height / 2.0))

local default_item = ccui.Layout:create()
default_item:setTouchEnabled(true)
default_item:setContentSize(listItemRootSize)
default_item:addChild(listItemRoot)

return default_item
end

local default_item = createDefaultListItem()
listView:setItemModel(default_item)

for i = 1, 100 do
listView:pushBackDefaultItem()
end

-- set all items layout gravity
listView:setGravity(ccui.ListViewGravity.centerVertical)

--set items margin
listView:setItemsMargin(2.0)

常用正则表达式符号

转意符号

pattern meaning
\ % 改变下一个字符的属性,让普通字符包含特殊含义或者相反

字符匹配模式

pattern meaning
. 匹配除「\n」之外的任何单个字符。
x\ y 匹配x或y
[xyz] 匹配所包含的任意一个字符
[^xyz] 匹配未列出的任意字符
[a-z] 字符范围。匹配指定范围内的任意字符。
[^a-z] 匹配任何不在指定范围内的任意字符
….. 其他特殊的 patterns

数量限定

pattern meaning
* 匹配前面的子表达式零次或多次
+ 匹配前面的子表达式一次或多次。
? 匹配前面的子表达式零次或一次。
{n} 匹配确定的n次。
{n,} 至少匹配n次
{n,m} 最少匹配n次且最多匹配m次

贪婪模式取消

pattern meaning
? 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的

控制字符

pattern meaning
^ 匹配输入字符串的开始位置
$ 匹配输入字符串的结束位置

分组模式

pattern meaning
(xyz) 分组
\n 对分组的引用

tolua++:namespace导致的问题

在用cocos2d-x的时候,我们把自己的类和cocos2d-x的类混在一起,而是导出了一个独立的cpp文件。但是在pkg文件中没有注意处理cocos2d的namespace,导致了在lua脚本中,自定义类中获得的比如CCNode之类的对象无法访问成员函数。因为在cocos2d-x的pkg中,它是删除了namespace的,所以tolua++生成的代码是这样的:

 tolua_usertype(tolua_S,”CCNode”);

而在我的pkg文件中,由于没有处理namespace,生成的代码如下:

 tolua_usertype(tolua_S,”cocos::CCNode”);

这就导致了同一个类在lua脚本中被认为是两个不同的对象。

解决的方法很简单,去掉namespace即可。

指针vector初始化

C++中,在申明的时候初始化一个vector的时候可以指明vector的大小和默认值。但是如果用NULL来初始化指针的时候,会遇到问题。

class A;
vector<A > vtTest<2, NULL>;

在XCode中报错:说不能把int型赋值给A。原因就是NULL本身是不带具体的类型的,默认会被认为是一个int类型。

所以,解决的方法就是类型强制转换。

Ubuntu下部署Jenkins的一些要点

Jenkins是一个开源的持续集成的工具,安装什么的都没有问题,非常简单。但是在配置的时候还是遇到了几个问题:

  1. 配置权限管理的时候,一定要先创建一个用户之后再进行。如果没有创建用户,又去掉了”Allow users to sign up”的勾,那么只能去编辑config.xml了。

  2. jenkins用户需要提升到root权限来进行一些操作。先把他加入到root group,再编辑/etc/sudoers设在sudo的时候不需要密码。

    1
    2
    $ sudo gpasswd -a jenkins root
    $ sudo vim /etc/sudoers

在sudoers中加入

1
jenkins ALL=NOPASSWD:ALL

当然,也可以使用expect工具来提升脚本执行时的权限

  1. 多个命令执行的时候,无法正确的反映执行的状态,往往其中已经出现错误了,jenkins还是返回成功。这个问题可以通过检查每个命令的返回值来处理,比如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #!/bin/sh
    sudo make clean
    if [ $? -ne 0 ]; then
    exit 1
    fi

    sudo make
    if [ $? -ne 0 ]; then
    exit 1
    fi

    sudo make install
    if [ $? -ne 0 ]; then
    exit 1
    fi

Ubuntun下GIT服务器的建立

使用环境:小团队

安装openssh-server, git

1
2
sudo apt-get install git
sudo apt-get install openssh-server

添加git账号

这儿方案中,所有的人共享一个账号,即git账号:
sudo adduser git
按照提示设置账号内容,密码等。

配置登陆认证

使用公钥来避免每次都需要输入密码的麻烦。可以给每个用户配置一个不同的公钥。

这里直接在Ubuntu上为用户生成公钥-密钥对,把公钥加入认证文件,密钥发给用户。
ssh-keygen
为不同的用户指定不同的文件名,比如:id_rsa.wxue,将生成id_rsa.wxue和id_rsa.wxue.pub。

1
2
cat .ssh/id_rsa.wxue.pub
/home/git/.ssh/authorized_keys

这些密钥可以用来登陆git账号了。

创建GIT仓库

把git的仓库放在/opt/gitrepo下

1
2
3
4
5
6
cd /opt
mkdri gitrepo
cd gitrepo
mkdir project.git
cd project.git
git --bare init

修改权限

现在git账号的权限,只能做git相关的操作
sudo vim /etc/passwd
把git的shell从/bin/bash改为/usr/bin/git-shell

客户端配置

  1. 说说在osx下的配置,其他linux系统也是一样。
    把相关用户的密钥放在它电脑的.ssh目录下,然后编辑或是创建config文件
    $ vim ~/.ssh/config
    在config文件中加入如下内容:
    1
    2
    Host 192.168.1.222 192.168.1.222
    IdentityFile ~/.ssh/id_rsa.wxue

如果只有一个id_rsa密钥文件,也可以不创建config文件。

修改config文件的属性
$ chmod 644 ~/.ssh/config
文件的目录也可以做一下限制,只有git帐号才可以打开:
$ sudo chown git:git gitrepo

  1. windows下的配置
    Windows下一般使用图形化工具TortoiseGit。TortoiseGit如果使用plink则无法使用id_rsa,需要转换从PuTTY Key Generator格式的ppk密钥。在TortoiseGit目录下可以找到PuTTY Key Generator工具,把id_rsa导入进去,并保存成ppk文件,放在用户目录的.ssh目录下。

还没完,还需要在TortoiseGit目录中找到Pageant程序,把ppk文件加入到里面。或者,在添加remote的时候,把ppk绑定进来。

Lua与C/C++的一些简单交互

我只写简单的!

lua版本 5.1.4
环境 ubuntu 12.04

  1. 从C/C++调用lua中自定义的函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #include <lua.hpp>
    #include <iostream>
    using namespace std;

    int main(int argc, const char * argv[])
    {

    lua_State *L = luaL_newstate();
    luaopen_base(L);
    luaL_openlibs(L);

    int ret = luaL_dofile(L, "test.lua");
    if (ret != 0)
    {
    cout << "load test.lua failed" << endl;
    return 1;
    }

    lua_getglobal(L, "showValue");
    lua_pushstring(L, "William");
    lua_pushstring(L, "Hsueh");

    lua_pcall(L, 2, 0, 0);

    lua_close(L);

    return 0;c++
    }

test.lua文件:

1
2
3
function showValue(strFirstName, strLastName)
print(strFirstName .. "." .. strLastName)
end

  1. 从lua中返回一个值给C/C++

    main.cpp,注意lua_pcall中设置了返回3个值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    #include <lua.hpp>
    #include <iostream>
    using namespace std;

    int main(int argc, const char * argv[])
    {

    lua_State *L = luaL_newstate();
    luaopen_base(L);
    luaL_openlibs(L);

    int ret = luaL_dofile(L, "test.lua");
    if (ret != 0)
    {
    cout << "load test.lua failed" << endl;
    return 1;
    }

    lua_getglobal(L, "showValue");
    lua_pushstring(L, "William");
    lua_pushstring(L, "Hsueh");

    lua_pcall(L, 2, 3, 0);

    int nOld = lua_tonumber(L, -1);
    const char *pIs = lua_tostring(L, -2);
    const char *pName = lua_tostring(L, -3);

    cout << pName << " " << pIs << " " << nOld << endl;

    lua_pop(L, 3);

    lua_close(L);

    return 0;
    }

test.lua文件返回了3个值

1
2
3
4
5
function showValue(strFirstName, strLastName)
strName = strFirstName .. "." .. strLastName
print(strName)
return strName, "is", 18
end

  1. Lua调用C/C++中自定义的函数

    Lua不能调用任意的C/C++函数。可以向Lua中注册的函数,需要符合以下的规范:
    typedef int (*lua_CFunction) (lua_State *L);
    main.cpp,定义了一个名为lua_printValue的函数,并且注册为PrintValue的lua函数,以便于在Lua中使用。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    #include <lua.hpp>
    #include <iostream>
    using namespace std;

    int lua_printValue(lua_State *L)
    {

    // get the parameter
    const char *pValue = lua_tostring(L, -1);

    // do something here...

    lua_pushstring(L, pValue);
    lua_pushstring(L, "Year");

    // return 2 value
    return 2;
    }

    int main(int argc, const char * argv[])
    {

    lua_State *L = luaL_newstate();
    luaopen_base(L);
    luaL_openlibs(L);

    // register the lua_printValue function
    // as PrintValue which can be used in lua
    lua_pushcfunction(L, lua_printValue);
    lua_setglobal(L, "PrintValue");

    int ret = luaL_dofile(L, "test.lua");
    if (ret != 0)
    {
    cout << "load test.lua failed" << endl;
    return 1;
    }

    lua_getglobal(L, "showValue");
    lua_pushstring(L, "William");
    lua_pushstring(L, "Hsueh");

    lua_pcall(L, 2, 3, 0);

    int nOld = lua_tonumber(L, -1);
    const char *pIs = lua_tostring(L, -2);
    const char *pName = lua_tostring(L, -3);

    cout << pName << " " << pIs << " " << nOld << endl;

    lua_pop(L, 3);

    lua_close(L);

    return 0;
    }

test.lua文件:

1
2
3
4
5
6
function showValue(strFirstName, strLastName)
strName = strFirstName .. "." .. strLastName
strThis, strYear = PrintValue("this")
strAge = strThis .. " " .. strYear .. " is"
return strName, strAge, 18
end

PrintValue从Lua中接受一个参数,在C/C++中处理了一下,返回两个参数给Lua。

Lua与C++的集成

——一个最简单的Lua与C++的例子

很多人推荐脚本和引擎相结合,灵活又方便,但是以前没有这方面的经验,所以从头开始学习。

脚本方面选择了Lua,因为用得比较广,而且据说很简单!

开始之前要有心理准备,这东西,其实不简单!

或许Lua本身是简单的,或许Lua与C的集成也不难,但是如何在Lua中使用C++的类、对象和函数?特别是对以前没有使用过Lua的人来说,连语法和API都磕磕绊绊,要把Lua和C++集成在一起互相访问,其中需要打通的关节,很多……

第一个难点是:版本!

因为某些原因,我选择tolua++来生成Lua和C++之间的胶水文件。 Lua现在最新的版本是5.2.1,tolua++最新的版本是1.0.93,但是遗憾的是,这两个版本对不起来。1.0.93版的tolua++对应的是lua 5.1.4。我意识到这个问题,并就此请教google大神之前,已经在osx下奋战近2个小时了。

第二个难点是:编译!

理顺了版本只是第一步,接下来要把源代码编译成库文件。

一开始我是在osx下面,编译lua没有问题,但是tolua++失败了。tolua++需要使用scons来编译,scons需要python环境,这些都满足了之后,编译的时候还是提示很多错误,我没有细究,直接放弃了。

我也尝试像cocos2d-x那样把lua和tolua++放到工程中直接用xcode编译,但是也没有成功。于是不得不转战到Linux下面。

在Ubuntu下,用apt直接可以安装lua和tolua++。

tolua++安装好之后,你如果直接在shell里输入tolua++是找不到程序的。是用whereis tolua++命令可以发现,可执行文件的名字是:/usr/bin/tolua++5.1,为了方便,我建立了一个tolua++的链接。

第三个难点是:工具使用!

到这一步,已经不算难点了。在Linux下面,难度本身就减少了不少,但我还是遇到了一点点小问题。一开始,我只下载了tolua++,然后写了一个简单的头文件:

HelloWorld.h

1
2
3
4
5
6
7
8
class HelloWorld
{
public:
HelloWorld();

HelloWorld *GetInstance();
void PrintHello();
};

HelloWorld.cpp中是实现,随便写就可以了。

然后使用下面的命令,希望生成对应的cpp:
tolua++ -o hellulua.cpp HelloWorld.h
但是,貌似正确的结果中,却没有函数的内容。再在网上查了一些资料之后,我开始怀疑后缀名必须是.pkg才可以。测试了一下,结果正确了!

正确的pkg文件应该写成:

1
2
3
4
5
6
7
8
$#include "HelloWorld.h"

class HelloWorld
{
HelloWorld();
HelloWorld *GetInstance();
void PrintHello();
};

正确的命令,也不是使用.h文件,而是如下:
tolua++ -o hellulua.cpp HelloWorld.pkg

第四个难点是:集成!

或许是版本众多的原因,网上有很多相关的例子,但是都无法正确的运行。tolua++本身也带了一些测试,但是如果没有一定的基础,看起来也是非常困难的。总的来说,就是“不够简单”。经过多次失败,以及对lua文档的研习,再参考网上的例子,终于有了一个最简单的测试了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include &lt;lua.hpp&gt;
#include "HelloWorld.h"

int tolua_HelloWorld_open(lua_State* tolua_S);

int main(int argc, const char * argv[])
{

lua_State *L = luaL_newstate();//tolua_open()
luaopen_base(L);

tolua_HelloWorld_open(L);

int ret = luaL_dofile(L, "test.lua");

lua_close(L);
return 0;
}

这时候再回过去看tolua++的How to use toLua, 就可以很好的理解了。

用g++来编译:
g++ main.cpp HelloWorld.cpp hellolua.cpp -ltolua++5.1 -llua5.1 -I /usr/include/lua5.1
一开始我只下载了tolua++,所以编译的时候出现了一些reference没有找到的问题。再下载lua的开发库就OK了。

再写个test.lua文件:

1
2
hello = HelloWorld:new()
hello.PrintHello()

我在如何在Lua中使用C++类的构造函数上也花了很多时间。因为整个程序没有跑通,所以失败的时候,也不知道是语法不对,还是其他的问题。

然后运行编译出来的HelloWorld:
./a.out
就可以得到显示:
hello

至此,一个最简单的,在Lua脚本中使用C++对象的项目成功完成了。再回过来去看tolua++的文档,很多之前难懂的地方都清晰了。而且,有了这样一个最简单的项目,可以慢慢向里面添加更多Lua相关的东西,是深入学习Lua的一个好的起点。