九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

数据/配置 的存储方式 Lua篇

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

前言

    在《数据/配置 的存储方式 Json篇 以JsonCpp库使用为例
》《数据/配置 的存储方式 Json篇 以Cocos2D For Iphone+TouchJson为例
》中我总结了一下怎么使用Json作为配置,但是,其实Json虽然语法格式简单,同时解析库的使用也非常简单,但是,因为这种简单性,缺少有的时候的确非常需要的功能。比如配置文件中对另一行配置的引用,比如配置段的继承等,这些可以使用XML来解决,另外,有的时候还会需要在配置文件中进行简单的运算,比如我就常常会有想将某个图片缩放到目前分辨率的几分之几这样的需求,此时,用一个完整的可以进行运算的语言来做配置的诱惑实在太大了。。。。。。
    Lua就是为此而生的。。。。。。虽然现在Lua的功能已经远远不止这样了,但是最初设计Lua的目的就是一个简单的配置语言。见Lua的历史
。虽然其实用Python作为配置也完全可行,但是Lua有着速度快的优点,而且,Lua实在比Python小太多了,很适合嵌入到程序中去。

对于使用Lua作为配置,事实上就相当于在C++中嵌入Lua,只不过不用其脚本语言的一些复杂特性,仅仅将其作为一个配置文件来看。

这里还是以原来《数据/配置 的存储方式 Json篇 以JsonCpp库使用为例
》文中开始的SDL工程作为例子。

首先搭建可以内嵌使用Lua的C++编译环境,下载一个luabinaries
是简单的办法,现在我使用的是lua5_1_4_Win32_dll8_lib.zip
。这里我甚至不需要一个可以运行的Lua交互环境。。。。。

简单示例

首先建立一个简单的Lua文件,命名为picture.lua
name="dragon.png"

rotation=180



虽然只有简单的两行,也是个合法的Lua程序。

然后在C++程序中读取出来,这个过程实际上是使用了Lua的C API,具体的Lua的C API这里就不详细讲解了,那比较复杂,可以参考《Programming in Lua》,中文版也已经有了。这里仅仅作为一个例子提供参考吧。
现在可以开始完成C++的程序了:
首先,包含必要的头文件:
extern "C" {
#include "lua/lua.h"
#include "lua/lauxlib.h"
#include "lua/lualib.h"
}
因为lua的头文件完全是只考虑C语言的情况,所以在C++中使用的时候需要自己添加extern "C"。这是典型的对C++不屑的态度,因为现在大部分的纯C语言写的库,都会很自然的通过宏判断来自动使用extern "C"语句。

然后:
using
 namespace
 std;
struct
  PictureInfo {
  string name;
  float
 rotation;
}gPictureInfo;

void
  PictureInit() {
  lua_State *L = luaL_newstate();
  if
( luaL_dofile(L, "picture.lua"
) != 0
) {
    printf("Error happen."
);
    // handle the error.

    exit(1
);
  }

  lua_getglobal(L, "name"
);
  assert(lua_isstring(L, -1
) == 1
);

  gPictureInfo.name = lua_tostring(L, -1
);

  lua_getglobal(L, "rotation"
);
  gPictureInfo.rotation = (float
)lua_tonumber(L, -1
);

  lua_close(L);
}

这里我们使用的所有变量全部都是global的变量,所以调用起来非常方便。上面的主要内容就是对Lua C API的使用,由于其本身较为复杂,这里一下也讲不清楚。
基本流程是创建新的Lua state,然后用luaL_dofile
执行我们需要的配置文件,然后调用lua_getglobal
加适当的转换来获取我们需要的配置值。这里的例子是一个字符串和一个浮点数。
具体显示的效果和其他代码参考《数据/配置 的存储方式 Json篇 以JsonCpp库使用为例
》就好了,这里不重复描述了。

数组

    在Lua中数组也通过table的形式来表示,这里我仅仅介绍怎么从lua文件中获取配置,具体的显示等东西也不再累述了。
    这里,我们建立一个包含数组的lua文件作为配置:

data = {

    {
 name="dragon.png"
, rotation=180
 }
,
    {
 name="dragon.png"
, rotation=0
 }

}

下面看怎么将配置读取出来:

using
 namespace
 std;
struct
  PictureInfo {
  string name;
  float
 rotation;
};

vector gPictureInfoVec;

void
  PictureInit() {
  lua_State *L = luaL_newstate();
  if
( luaL_dofile(L, "picture.lua"
) != 0
) {
    printf("Error happen."
);
    // handle the error.

    exit(1
);
  }

  // get the table

  lua_getglobal(L, "data"
);
  assert(lua_istable(L, -1
) == 1
);

  /*
 table is in the stack at index 't'
*/

  lua_pushnil(L);  /*
 first key
*/

  while
 (lua_next(L, -2
) != 0
) {
    PictureInfo info;
    /*
'key' (at index -2) and 'value' (at index -1)
*/

    // push the key to stack for getting the value

    lua_pushstring(L, "name"
);

    // now the table is in the -2 and key in the top(-1)

    lua_gettable(L, -2
);
    assert(lua_isstring(L, -1
));

    info.name = lua_tostring(L, -1
);

    lua_pop(L, 1
);

    // push the key to stack for getting the value

    lua_pushstring(L, "rotation"
);

    // now the table is in the -2 and key in the top(-1)

    lua_gettable(L, -2
);
    assert(lua_isnumber(L, -1
));

    info.rotation = lua_tonumber(L, -1
);

    gPictureInfoVec.push_back(info);
    /*
 removes the key we pushed and the 'value' of the global table ;   keeps 'key' for next iteration
*/

    lua_pop(L, 2
);
  }

  lua_close(L);
}


代码中的注释解释的已经够详细了,但是因为Lua 的API的确不是太容易理解,这里也无法一下讲清楚,所以还是先了解Lua 的 API为好,具体的API的意义可以参考参考手册


上面仅仅使用了Lua API遍历数组以及从table中获取元素的方法。
假如仅仅只有上面这些,是看不出用lua作为配置的好处的,用lua做配置的好处在于可以利用lua的特性实现配置段之间的继承以及完整的运算功能。
比如说,完全不用修改上面的读取配置的代码,我们仅仅修改配置,看看在Lua中使用上述功能:

data1 = {
 name="dragon.png"
, rotation=180
 }

data2 = {
 name = data1.name, rotation = data1.rotation / 2
}

data = {

    data1, data2
}

此时,data2的数据完全依赖于data1的数据,当有任何东西需要修改的时候,你只需要修改一个地方,对于配置来说,don't repeat yourself也是很有意义的。最最重要的的是,在配置中能够进行计算那是非常强大,即使完全不用lua的函数功能与代码的交互,仅仅通过lua的计算,也可以完成游戏中所有sprite的布局。。。。。。

小结

    相对于使用XML,Json,使用Lua作为配置绝对是最最强大的。。。。。。作为一个完整的语言,它拥有你想在配置中实现的一切功能。但是缺点也是很明显的:
    首先,速度上,Lua需要解释运行,可能明显跟不上XML或者Json的解析速度。但是,配置的解析读取可以都放在初始化阶段,所以配置不是太多的时候,也不算太过难以接受,另外,还可以将lua的配置完全作为开发期的一种机制使用,在发布后完全转化为2进制数据。
    其次,Lua的配置读取需要手动调用Lua的API来完成,相对于XML,Json那种有很方便的库的情况,使用上还是麻烦一些,特别是Lua的API的使用并不是那么简单易懂,不算太直观。但是,这也不是不可以克服的,完全可以自己写个小型的库将Lua的API封装起来,形成一个类似JsonCpp那样的库,用Map来表示一切。这个可能需要限制一些Lua语法的时候,或者在解析的时候进行取舍,比如函数啥的可能需要过滤掉,不然那就不太像将Lua作为配置使用了。
    还有,Lua的配置的生成更加是没有XML,Json那样有完善库支持来的方便,这点暂时没有办法克服,写一个自动生成Lua配置文件的库感觉并不是太过容易的事情。而且,自动生成时,lua的优势其实并没有余地发挥出来,那样的话,还不如使用Json。
    总的来说,假如是没有工具,需要大量手写配置的话,对Lua的API进行一层简单的封装,然后使用Lua来做配置,那还是件很爽的事情,需要手写的时候,你才会感觉到一个完整语言作为配置给你带来的好处。

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

分类:  Lua  游戏开发 
标签:  Lua 

Posted By 九天雁翎 at 九天雁翎的博客 on 2010年07月23日

前一篇: 数据/配置 的存储方式 Json篇 以JsonCpp库使用为例 后一篇: 编程世界中惯性的力量