本站文章均爲 李華明Himi 原創,轉載務必在明顯處註明:(作者新浪微博: @李華明Himi )
轉載自【黑米GameDev街區】 原文鏈接: http://www.himigame.com/lua1/1343.html
☞ 點擊訂閱 ☜ 本博客最新動態!及時將最新博文通知您!
在使用Cocos2d-x 時候,難免需要C/C++調用Lua函數、數據或Lua調用C/C++函數,那麼本篇講詳細介紹C/C++與Lua之間的數據、函數交互。
首先讓我們來簡單瞭解幾個Lua API函數:
int luaL_dofile (lua_State *L, const char *filename) :
加載並運行指定文件,沒有錯誤返回0
void lua_settop (lua_State *L, int index):
參數允許傳入任何可接受的索引以及 0 。 它將把堆棧的棧頂設爲這個索引。 如果新的棧頂比原來的大,超出部分的新元素將被填爲 nil 。 如果 index 爲 0 ,把棧上所有元素移除。
void lua_getglobal (lua_State *L, const char *name):
把全局變量 name 裏的值壓入堆棧。
void lua_pop (lua_State *L, int n):
從堆棧中彈出 n
個元素。相當於清除!
void lua_pushstring (lua_State *L, const char *s):
把指針 s 指向的以零結尾的字符串壓棧。 Lua 對這個字符串做一次內存拷貝(或是複用一個拷貝), 因此 s 處的內存在函數返回後,可以釋放掉或是重用於其它用途。 字符串中不能包含有零字符;第一個碰到的零字符會認爲是字符串的結束。
更多的API請參考:http://www.codingnow.com/2000/download/lua_manual.html
瞭解了以上幾個函數,爲了方便童鞋們使用,Himi直接貼出封裝好的類 HclcData,其中主要包括如下幾個功能:
1. C/C++ 調用 Lua 全局變量
2. C/C++ 調用 Lua 全局Table 某元素
3. C/C++ 調用 Lua 全局Table
4. C/C++ 調用 Lua 函數
5. Lua 調用C/C++ 函數
下面直接貼出代碼:HclcData.h
- //
- // HclcData.h
- // CppLua
- //
- // Created by Himi on 13-4-17.
- //
- //
- #ifndef __CppLua__HclcData__
- #define __CppLua__HclcData__
- #include "cocos2d.h"
- using namespace cocos2d;
- using namespace std;
- extern "C" {
- #include "lua.h"
- #include "lualib.h"
- #include "lauxlib.h"
- };
- class HclcData{
- public:
- static HclcData* sharedHD();
- //------------ c++ -> lua ------------//
- /*
- getLuaVarString : 調用lua全局string
- luaFileName = lua文件名
- varName = 所要取Lua中的變量名
- */
- const char* getLuaVarString(const char* luaFileName,const char* varName);
- /*
- getLuaVarOneOfTable : 調用lua全局table中的一個元素
- luaFileName = lua文件名
- varName = 所要取Lua中的table變量名
- keyName = 所要取Lua中的table中某一個元素的Key
- */
- const char* getLuaVarOneOfTable(const char* luaFileName,const char* varName,const char* keyName);
- /*
- getLuaVarTable : 調用lua全局table
- luaFileName = lua文件名
- varName = 所要取的table變量名
- (注:返回的是所有的數據,童鞋們可以自己使用Map等處理)
- */
- const char* getLuaVarTable(const char* luaFileName,const char* varName);
- /*
- callLuaFunction : 調用lua函數
- luaFileName = lua文件名
- functionName = 所要調用Lua中的的函數名
- */
- const char* callLuaFunction(const char* luaFileName,const char* functionName);
- //------------ lua -> c++ ------------//
- void callCppFunction(const char* luaFileName);
- private:
- static int cppFunction(lua_State* ls);
- static bool _isFirst;
- static HclcData* _shared;
- const char* getFileFullPath(const char* fileName);
- ~HclcData();
- };
- #endif /* defined(__CppLua__HclcData__) */
HclcData.cpp
- //
- // HclcData.cpp
- // CppLua
- //
- // Created by Himi on 13-4-17.
- //
- //
- #include "HclcData.h"
- #include "CCLuaEngine.h"
- bool HclcData::_isFirst;
- HclcData* HclcData::_shared;
- HclcData* HclcData::sharedHD(){
- if(!_isFirst){
- _shared = new HclcData();
- }
- return _shared;
- }
- const char* HclcData::getLuaVarString(const char* luaFileName,const char* varName){
- lua_State* ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();
- int isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));
- if(isOpen!=0){
- CCLOG("Open Lua Error: %i", isOpen);
- return NULL;
- }
- lua_settop(ls, 0);
- lua_getglobal(ls, varName);
- int statesCode = lua_isstring(ls, 1);
- if(statesCode!=1){
- CCLOG("Open Lua Error: %i", statesCode);
- return NULL;
- }
- const char* str = lua_tostring(ls, 1);
- lua_pop(ls, 1);
- return str;
- }
- const char* HclcData::getLuaVarOneOfTable(const char* luaFileName,const char* varName,const char* keyName){
- lua_State* ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();
- int isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));
- if(isOpen!=0){
- CCLOG("Open Lua Error: %i", isOpen);
- return NULL;
- }
- lua_getglobal(ls, varName);
- int statesCode = lua_istable(ls, -1);
- if(statesCode!=1){
- CCLOG("Open Lua Error: %i", statesCode);
- return NULL;
- }
- lua_pushstring(ls, keyName);
- lua_gettable(ls, -2);
- const char* valueString = lua_tostring(ls, -1);
- lua_pop(ls, -1);
- return valueString;
- }
- const char* HclcData::getLuaVarTable(const char* luaFileName,const char* varName){
- lua_State* ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();
- int isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));
- if(isOpen!=0){
- CCLOG("Open Lua Error: %i", isOpen);
- return NULL;
- }
- lua_getglobal(ls, varName);
- int it = lua_gettop(ls);
- lua_pushnil(ls);
- string result="";
- while(lua_next(ls, it))
- {
- string key = lua_tostring(ls, -2);
- string value = lua_tostring(ls, -1);
- result=result+key+":"+value+"\t";
- lua_pop(ls, 1);
- }
- lua_pop(ls, 1);
- return result.c_str();
- }
- const char* HclcData::callLuaFunction(const char* luaFileName,const char* functionName){
- lua_State* ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();
- int isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));
- if(isOpen!=0){
- CCLOG("Open Lua Error: %i", isOpen);
- return NULL;
- }
- lua_getglobal(ls, functionName);
- lua_pushstring(ls, "Himi");
- lua_pushnumber(ls, 23);
- lua_pushboolean(ls, true);
- /*
- lua_call
- 第一個參數:函數的參數個數
- 第二個參數:函數返回值個數
- */
- lua_call(ls, 3, 1);
- const char* iResult = lua_tostring(ls, -1);
- return iResult;
- }
- void HclcData::callCppFunction(const char* luaFileName){
- lua_State* ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();
- /*
- Lua調用的C++的函數必須是靜態的
- */
- lua_register(ls, "cppFunction", cppFunction);
- int isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));
- if(isOpen!=0){
- CCLOG("Open Lua Error: %i", isOpen);
- return;
- }
- }
- int HclcData::cppFunction(lua_State* ls){
- int luaNum = (int)lua_tonumber(ls, 1);
- int luaStr = (int)lua_tostring(ls, 2);
- CCLOG("Lua調用cpp函數時傳來的兩個參數: %i %s",luaNum,luaStr);
- /*
- 返給Lua的值
- */
- lua_pushnumber(ls, 321);
- lua_pushstring(ls, "Himi");
- /*
- 返給Lua值個數
- */
- return 2;
- }
- const char* HclcData::getFileFullPath(const char* fileName){
- return CCFileUtils::sharedFileUtils()->fullPathForFilename(fileName).c_str();
- }
- HclcData::~HclcData(){
- CC_SAFE_DELETE(_shared);
- _shared=NULL;
- }
大家可以直接拿來用的,使用簡單,測試如下:
首先C++測試代碼:
- #include "HclcData.h"
- CCLOG("Str = %s",HclcData::sharedHD()->getLuaVarString("Test.lua","luaStr"));
- CCLOG("Str2 %s",HclcData::sharedHD()->getLuaVarString("Test.lua","luaStr2"));
- CCLOG("age = %s",HclcData::sharedHD()->getLuaVarOneOfTable("Test.lua", "luaTable","age"));
- CCLOG("name = %s",HclcData::sharedHD()->getLuaVarOneOfTable("Test.lua", "luaTable","name"));
- CCLOG("sex = %s",HclcData::sharedHD()->getLuaVarOneOfTable("Test.lua", "luaTable","sex"));
- CCLOG("Table = %s",HclcData::sharedHD()->getLuaVarTable("Test.lua", "luaTable"));
- CCLOG("Call Lua Function Back: %s",HclcData::sharedHD()->callLuaFunction("Test.lua", "luaLogString"));
- HclcData::sharedHD()->callCppFunction("Test.lua");
- HclcData::sharedHD()->callLuaFunction("Test.lua", "call_Cpp");
對應測試的Test.Lua文件:
- luaStr = "I' m Himi"
- luaStr2 = "are you ok!"
- luaTable={age = 23,name="Himi",sex="男"}
- function luaLogString(_logStr,_logNum,_logBool)
- print("Lua 腳本打印從C傳來的字符串:",_logStr,_logNum,_logBool)
- return "call lua function OK"
- end
- function call_Cpp(_logStr,_logNum,_logBool)
- num,str = cppFunction(999,"I'm a lua string")
- print("從cpp函數中獲得兩個返回值:",num,str)
- end
運行測試結果如下:
- Cocos2d: Str = I' m Himi
- Cocos2d: Str2 are you ok!
- Cocos2d: age = 23
- Cocos2d: name = Himi
- Cocos2d: sex = 男
- Cocos2d: Table = name:Himi age:23 sex:男
- Lua 腳本打印從C傳來的字符串: Himi 23 true
- Cocos2d: Call Lua Function Back: call lua function OK
- Cocos2d: Lua調用cpp函數時傳來的兩個參數: 999 I'm a lua string
- 從cpp函數中獲得兩個返回值: 321 Himi
在Himi做這些交互時出現瞭如下錯誤:
- “PANIC: unprotected error in call to Lua API (attempt to index a nil value)
如下圖:
最後Himi發現造成此問題的原因有兩種:
1. 是你的lua文件位置路徑!
細心的童鞋應該看到,每次我使用 luaL_dofile 函數時傳入的都是調用了一個getFileFullPath的函數進行獲取文件的完整路徑!
在HclcData中包裝了一個函數:
- const char* HclcData::getFileFullPath(const char* fileName){
- return CCFileUtils::sharedFileUtils()->fullPathForFilename(fileName).c_str();
- }
2. 如果你是cpp調用lua函數,那麼你的這個lua函數不能是local的!
反之,如果你lua調用cpp函數,同理,cpp函數肯定是static的!
另外,如果你cpp調用lua,等同於重新加載了這個lua文件,是不同的對象!因此你應該建立一個新的lua文件,主要用於交互所用!
例如你a.lua中有一個tab的成員變量,那麼你使用cpp調用lua函數後,這個tab是新的對象!
最後附上HclcData和Test.lua 下載地址:http://vdisk.weibo.com/s/y0zws
OK,本篇就到這裏,有什麼問題及時聯繫Himi!