跳轉到內容

BlitzMax/Modules/Axe/Lua

來自華夏公益教科書

初始化和反初始化

[編輯 | 編輯原始碼]

Lua指令碼通常在VM(虛擬機器)中執行。因此,我們首先啟動一個VM,然後載入基本的Lua庫

Local LuaState:byte ptr = luaL_newstate()
luaL_openlibs(LuaState)

luaL_newstate() 返回的位元組指標在每次Lua函式呼叫時都需要。現在你已經完成了初始化,可以開始執行指令碼了。

之後關閉Lua VM可以透過一個函式完成

lua_close(LuaState)

執行一個簡單的指令碼

[編輯 | 編輯原始碼]

Lua指令碼可以直接從Blitzmax字串中執行。

Local Script:String = "print(~qHello World!~q)"
luaL_loadString(LuaState,Script)

這段程式碼載入並編譯了指令碼。如果出現問題,luaL_loadString() 會返回一個 <> 0 的值。

lua_getfield(LuaState, LUA_GLOBALSINDEX, "debug")' get global "debug" 
lua_getfield(LuaState, -1, "traceback")          ' get "debug.traceback" 
lua_remove (LuaState, -2)                        ' remove "debug" table from stack 

這些命令現在不重要,它們只是為了獲得正確的錯誤資訊。

lua_pcall(LuaState,1,-1,-1)' use "debug.traceback" as err.hdlr

這行程式碼實際上執行了指令碼。同樣,如果它的返回值是 <> 0,則發生了錯誤。如果一切都正常,程式將按照預期顯示一個“Hello World!”。

從Lua呼叫BlitzMax函式

[編輯 | 編輯原始碼]

基礎知識

[編輯 | 編輯原始碼]

你可能已經注意到,僅僅能夠執行指令碼而不能與BlitzMax互動並沒有什麼用。因此,下一步就是學習如何從Lua指令碼呼叫BlitzMax函式。不幸的是,我們不能直接使用任意函式,因為函式必須遵循特定的佈局

Function BMXName:int (LuaState:Byte Ptr)   
   ...      ' handling of parameters passed from Lua (if required)     
      ...   ' actual function body   
   ...      ' passing results back to Lua (if required)   
   return 0 ' number of values returned to Lua function  
End Function

顯然,在Lua指令碼中使用函式提交的引數不會直接傳輸到BlitzMax函式。我們必須手動處理它們,但不要急於求成。首先,我們需要將函式註冊到Lua

lua_register(LuaState, "luaname", BMXName)

現在可以在Lua(全域性)中以luaname() 的形式呼叫BMXName()。你可能選擇將函式命名與BlitzMax中相同,但你沒有必要這樣做。

處理來自Lua的引數

[編輯 | 編輯原始碼]

假設我們嘗試從Lua呼叫

luaname(810, "Hallo!")

使用上面的示例函式。Lua沒有抱怨額外的引數,但我們也無法從函式內部訪問它們。它們到哪裡去了?Lua將它們放置在引數堆疊上。每個引數都被分配了一個索引,可以透過該索引識別。索引按順序編號,從1開始。因此,810 可以在索引1處找到,“Hallo!”可以在索引2處找到。現在我們只需要知道從引數堆疊讀取值的相應命令,我們就完成了

Function BMXName:Int (LuaState:Byte Ptr)   
   Local int_value:Int    = luaL_checkinteger(LuaState, 1)
   Local str_value:String = luaL_checkstring(LuaState, 2)
   Print int_value
   Print str_value
   Return 0 ' number of values returned to Lua function  
End Function 

返回值給Lua

[編輯 | 編輯原始碼]

返回一個或多個值到Lua指令碼的操作類似,但你不必指定索引,因為值會自動追加到堆疊。以下是一個示例

Function BMXName:Int (LuaState:Byte Ptr)   
   lua_pushnumber(LuaState, 810)
   lua_pushstring(LuaState, "Hallo!")
   Return 2 ' number of values returned to Lua function  
End Function 

在大多數情況下,你只會返回一個值或根本不返回值,但如你所見,也可以返回更多值。

從BlitzMax呼叫單個Lua函式

[編輯 | 編輯原始碼]

我們知道如何執行完整的Lua指令碼,但如果我們只需要一個函式呢?在這種情況下,你首先必須將要呼叫的函式放在堆疊上,然後是引數。以下是一個示例。Lua指令碼

function luafunc(para, str)
   print(para, str)
end

BlitzMax程式碼

lua_getfield(LuaState, LUA_GLOBALSINDEX, "luafunc") 'Puts the function onto the stack
lua_pushinteger(LuaState, 810)                'First Argument
lua_pushstring(LuaState, "Hallo!")            'Second Argument
lua_call(LuaState, 2, 0)                      'Call the function with 2 arguments and no result 

如果有返回值,它們會在函式執行後被放置到堆疊上。

從BlitzMax訪問Lua全域性變數

[編輯 | 編輯原始碼]

定義或更改全域性變數

[編輯 | 編輯原始碼]
lua_pushstring(LuaState, "Hallo!")
lua_setglobal (LuaState, "luaglobal") 

第一個命令將“Hallo!”寫入堆疊,第二個命令將它放入全域性luaglobal。

讀取全域性變數

[編輯 | 編輯原始碼]
lua_getglobal(LuaState, "luaglobal")
Local str:String = lua_tostring(LuaState, -1)

第一個命令將值放入堆疊,第二個命令將它放入BlitzMax變數。

訪問所有全域性變數

[編輯 | 編輯原始碼]

如果你想儲存當前Lua VM的狀態,你需要找到一種方法來獲取所有全域性變數。一種方法是使用lua_next函式

lua_pushnil(LuaState)                                         ' first key 
While (lua_next(LuaState, LUA_GLOBALSINDEX) <> 0)             ' iterate through all values of the global environment table
   ' uses 'key' (at index -2) and 'value' (at index -1) 
   Print(lua_typename(LuaState,lua_type(LuaState , - 1))+" - "+lua_tostring(LuaState,-2)+"-"+lua_tostring(LuaState,-1))
   ' removes 'value'; keeps 'key' for next iteration 
   lua_pop(LuaState, 1);
Wend

請注意,這種方法不僅返回全域性變數,還返回函式、表等,你需要使用lua_type命令來進行分類。

華夏公益教科書