cocos2d-x 使用Lua


轉自:http://www.benmutou.com/blog/archives/49

1. Lua的堆棧和全局表

我們來簡單解釋一下Lua的堆棧和全局表,堆棧大家應該會比較熟悉,它主要是用來讓C++Lua通信的,是的,它們並不認識對方,只能通過堆棧來溝通,就像寫信一樣。

Lua的全局表又是什么呢?可以想象成是一個map哈希表結構,比如Lua有一個變量:

name = “hello”

那么,全局表就存放了”name”和”hello”的對應關系,Lua可以通過name在全局表中查找到hello。應該是這樣的~

 

2. LuaC++的第一次通信

-- hello.lua 文件
myName = "beauty girl"

然后,C++想知道Lua叫什么名字,所以,它們必須要通信了。來看看通信流程:

 

請注意紅色數字,代表通信順序:

1) C++想獲取LuamyName字符串的值,所以它把myName放到Lua堆棧(棧頂),以便Lua能看到

2) Lua從堆棧(棧頂)中獲取myName,此時棧頂再次變為空

3) Lua拿着這個myNameLua全局表查找myName對應的字符串

4) 全局表返回一個字符串”beauty girl”

5) Lua把取得的“beauty girl”字符串放到堆棧(棧頂)

6) C++可以從Lua堆棧中取得“beauty girl”,也就是這位美麗的Lua小姐的名字了~

 

3. 引入頭文件

我們來看看要在C++中使用Lua,需要些什么東西

 

/* 
   文件名:    HelloLua.h 
   描 述:    Lua Demo
   創建人:    笨木頭 (CSDN博客:http://blog.csdn.net/musicvs) 

   創建日期:   2012.12.24 
*/  

#ifndef __HELLO_LUA_H_
#define __HELLO_LUA_H_

#include "cocos2d.h"

extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
};

using namespace cocos2d;

class HelloLua : public CCLayer {
public:
    CREATE_FUNC(HelloLua);
    virtual bool init();

    static CCScene* scene();
};

#endif

 

4. 開始使用

來看看我們的cpp文件,我們要開始使用Lua~

 

#include "HelloLua.h"

CCScene* HelloLua::scene() {
    CCScene* scene = CCScene::create();
    CCLayer* layer = HelloLua::create();
    scene->addChild(layer);

    return scene;
}

bool HelloLua::init() {
    lua_State* pL = lua_open();
    luaopen_base(pL);
    luaopen_math(pL);
    luaopen_string(pL);

    /* 1.執行Lua腳本,返回0代表成功 */
    /* 2.重置棧頂索引 */
    /* 3.判斷棧頂的值的類型是否為String, 返回非0值代表成功 */
  /* 4.獲取棧頂的值 */
  
    lua_close(pL);
    return true;
}

 

5. 執行Lua腳本

現在我們來一步步完善我們的代碼,執行Lua腳本很簡單,看看:

 

bool HelloLua::init() {
    lua_State* pL = lua_open();
    luaopen_base(pL);
    luaopen_math(pL);
    luaopen_string(pL);

    /* 1.執行Lua腳本,返回0代表成功 */
    int err = luaL_dofile(pL, "helloLua.lua");
    CCLOG("open : %d", err);

    /* 2.重置棧頂索引 */
    lua_settop(pL, 0);
    lua_getglobal(pL, "myName");

    /* 3.判斷棧頂的值的類型是否為String, 返回非0值代表成功 */
  /* 4.獲取棧頂的值 */
  
    lua_close(pL);
    return true;
}

 

6. 重置棧頂索引, 將全局變量放到堆棧中

lua_settop(pL, 0);是為了確認讓棧頂的索引置為0,因為我們操作棧的時候是根據索引來操作的。置0之后,我們入棧的第一個元素的索引就是1

那,lua_getglobal(pL, “myName”);又是什么呢?咋一看好像是從lua中取得myName這個全局變量的值,但並不是這樣的,雖然最終也是這樣。

我們之前說過了,LuaC++是不能直接通信的,要通過堆棧來通信。

因此,lua_getglobal(pL, “myName”);只是把myName放到了棧中,然后lua就會通過myName去全局表尋找,找到myName對應的字符串“beauty girl”,再放到棧中。

 

7. C++取得字符串

我們來看看完整的代碼:

bool HelloLua::init() {
    lua_State* pL = lua_open();
    luaopen_base(pL);
    luaopen_math(pL);
    luaopen_string(pL);

    /* 1.執行Lua腳本,返回0代表成功 */
    int err = luaL_dofile(pL, "helloLua.lua");
    CCLOG("open : %d", err);

    /* 2.重置棧頂索引 */
    lua_settop(pL, 0);
    lua_getglobal(pL, "myName");

    /* 3.判斷棧頂的值的類型是否為String, 返回非0值代表成功 */
    int isstr = lua_isstring(pL, 1);
    CCLOG("isstr = %d", isstr);

    /* 4.獲取棧頂的值 */
    const char* str = lua_tostring(pL, 1);
    CCLOG("getStr = %s", str);

    lua_close(pL);
    return true;
}

 

lua_getglobal已經完成了很多工作了,現在堆棧上就放着“beauty girl”字符串,我們只要去取就可以了。

獲取堆棧的值有很多種方法,分別對應不同的變量類型:

lua_toboolean

lua_toNumber

lua_tocfunction

lua_tostring

 

我就不全部舉例了,現在我們要用lua_tostring來獲取棧頂的值。

最后,在AppDelegate.cpp中把默認啟動場景設為我們的HelloLua場景,用調試模式運行項目,將看到以下日志:

open : 0

isstr = 1

getStr = beauty girl

堆棧索引

 

1. 正數索引,棧底是1

2. 負數索引,棧頂是-1

 

8. C++調用Lua函數

-- helloLua.lua文件
myName = "beauty girl"

helloTable = {name = "mutou", IQ = 125}

function helloAdd(num1, num2)
    return (num1 + num2)
end;

 

C++調用helloAdd函數

/* C++調用lua的函數 */
void HelloLua::demo3() {
    lua_State* pL = lua_open();
    luaopen_base(pL);

    /* 執行腳本 */
    luaL_dofile(pL, "helloLua.lua");

    /* 把helloAdd函數對象放到棧中 */
    lua_getglobal(pL, "helloAdd");

    /* 把函數所需要的參數入棧 */
    lua_pushnumber(pL, 10);
    lua_pushnumber(pL, 5);

    /* 
        執行函數,第一個參數表示函數的參數個數,第二個參數表示函數返回值個數 ,
        Lua會先去堆棧取出參數,然后再取出函數對象,開始執行函數
    */
    lua_call(pL, 2, 1);

    int iResult = lua_tonumber(pL, -1);
    CCLOG("iResult = %d", iResult);
}

 

簡單說明一下步驟:

1) 將helloAdd函數放到棧中:lua_getglobal(pL, “helloAdd”) 。(旁白:看吧,我就知道~!)

2) helloAdd2個參數,我們要把參數傳遞給lua,所以2個參數都要放到棧里。

3) 第2和第3步已經把函數所需要的數據都放到棧里了,接下來只要告訴lua去棧里取數據,執行函數~

9. Lua調用C++函數

創建一個c++函數先:

public:
  static int getNumber(int num);
  
  
int HelloLua::getNumber( int num ) {
    CCLOG("getNumber num = %d", num);
    return num + 1;
}

 

現在,我們想在Lua中調用這個函數,得多寫一個函數。

 

public:
static int cpp_GetNumber(lua_State* pL);


int HelloLua::cpp_GetNumber( lua_State* pL ) {
    /* 從棧頂中取一個值 */
    int num = (int)lua_tonumber(pL, 1);

    /* 調用getNumber函數,將返回值入棧 */
    lua_pushnumber(pL, getNumber(num));

    /* 返回值個數,getNumber只有一個返回值,所以返回1 */
    return 1;
}

 

這是怎么回事呢?我們很清楚,LuaC++只能通過堆棧通信,所以Lua是不可能直接調用getNumber函數的,所以我們建立一個cpp_GetNumber函數作為中介。

cpp_GetNumber函數有一個lua_State* pL參數,有了這個參數,c++就能從Lua的堆棧中取值了

1) 首先,Lua腳本里會調用cpp_GetNumber函數。

2) 當cpp_GetNumber被調用時,一切又回到C++Lua的操作了,棧頂里會存放函數所需要的參數,取出來用就可以的。

3) Lua調用cpp_GetNumber之后,需要一個結果,當然,這個結果同樣只能存放在棧里,所以理所當然地要把getNumber的結果入棧。

4) 最后,cpp_GetNumber return了一個值,這個值不是函數的執行結果,而是getNumber需要返回值的個數(Lua支持多個返回值的函數)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM