[整理]Unity3D遊戲開發之Lua

原文1:[Unity3D]Unity3D遊戲開發之Lua與遊戲的不解之緣(上)javascript

 各位朋友,你們好,我是秦元培,歡迎你們關注個人博客,我地博客地址是blog.csdn.net/qinyuanpei。若是提到遊戲開發,你們必定會想到C/C++、DirectX、OpenGL等這些東西,但是衆所周知,遊戲行業是一個需求變化極快地行業,若是咱們採用編譯型的語言,那麼咱們可能很難跟上這個時代的步伐,由於編譯型的語言每經歷一次重大地更新,整個項目都須要從新編譯,這樣無疑會影響咱們的開發效率。那麼,有沒有一種更爲高效的遊戲開發模式呢?或許答案你們已經看到了。如今在遊戲界廣泛採用的方式是將遊戲的底層邏輯交給C/C++這樣的底層語言,而將遊戲的上層邏輯交給腳本語言。由於底層邏輯更看重效率而上層邏輯更注重靈活、便捷地使用。例如咱們熟知的Unreal引擎是採用UnrealScripts,這是一種相似於Java/C語法地語言;Unity3D引擎是採用的C#/javaScript/Boo這三種腳本語言;cocos2d-x採用地是Lua/javaScript這兩種腳本語言,將來可能會支持更多的語言。你們可能想問一個問題:什麼是腳本語言?所謂腳本語言是一種用來控制軟件應用程序且只在被調用時進行解釋或編譯的編程語言,這種語言一般以文本的形式來存儲腳本代碼。換句話說,腳本語言相似於一種指令,它縮短了傳統應用程序的編寫-編譯-連接-運行(edit-compile-link-run)這個過程,是一種解釋執行的程序。或許人們發明腳本語言的那一刻起,從未想過要將腳本語言和遊戲開發聯繫在一塊兒,不過腳本語言註定會由於遊戲開發而開拓出更爲廣闊的世界。本文將以目前遊戲開發領域較爲流行的Lua語言爲線索,深度解密遊戲開發領域與腳本語言之間千絲萬縷的聯繫。html

 

     1、什麼是Lua?java

         Lua 是一個小巧的腳本語言,巴西里約熱內盧天主教大學裏的一個研究小組於1993年開發,其設計目的是爲了嵌入應用程序中,從而爲應用程序提供靈活的擴展和定製功能。Lua由標準C編寫而成,幾乎在全部操做系統和平臺上均可以編譯,運行。一個完整的Lua解釋器不過200k,在目前全部腳本引擎中,Lua的速度是最快的。這一切都決定了Lua是做爲嵌入式腳本的最佳選擇。相比Python和Per的內核,Lua的內核小於120KB,而Python的內核大約860KB,Perl的內核大約1.1MB。Lua語言支持面向對象編程和函數式編程,它提供了一個通用類型的表table,能夠實現數組、哈希表、集合、對象的功能。Lua支持協同進程機制。做爲一門可擴展的語言,Lua提供簡單而穩定的交互接口,如Lua和C程序可經過一個堆棧交換數據,這使得Lua語言能夠快速地和其它語言實現整合。整體來講,Lua語言具有如下優勢:(1)語言優美、輕巧 (2)性能優良、速度快  (3)可擴展性強。正由於Lua語言具有了這樣的特色,使得它能和遊戲開發領域的需求完美地結合起來,由於咱們須要這樣的一門語言,它可以和C/C++進行完美地交互,由於咱們須要它對底層進行封裝。它須要足夠地簡單,由於咱們須要簡單、靈活、快速地編寫代碼。那麼顯然Lua就是咱們一直在尋找地這種語言。ios

 

      2、Lua能夠作什麼?git

     儘管博主已經告訴了你們太多的關於Lua語言的優秀特性,相信你們仍然會對Lua語言的能力存在懷疑。你們或許會想,Lua到底能夠作什麼呢?在《Lua遊戲開發》一書中做者已經告訴了咱們答案:github

一、編輯遊戲的用戶界面
二、定義、存儲和管理基礎遊戲數據
三、管理實時遊戲事件
四、建立和維護開發者友好的遊戲存儲和載入系統
五、編寫遊戲的人工智能系統
六、建立功能原型,能夠以後用高性能語言移植
編程

這時候咱們彷佛以爲Lua語言在某種程度上就是專門爲遊戲開發而誕生的,由於它將大量的優秀特性所有指向了遊戲開發領域,所以Lua語言走進走進遊戲開發領域變得順利成章,那麼,讓咱們接着往下看吧,Lua在遊戲開發領域有那些成熟的案例吧。c#

 

 3、哪些遊戲使用了Lua?windows

       一、魔獸世界api

       若是提到Lua在遊戲領域中第一次嶄露頭角,咱們就不能不說《魔獸世界》這款遊戲,因爲《魔獸世界》在其客戶端中使用了Lua,使得Lua在遊戲領域的做用第一次被展現出來,Lua語言所以在遊戲開發領域成名。Lua語言的虛擬機很輕巧,能夠很容易地嵌入到客戶端程序中。若是須要更新客戶端,只須要更新腳本程序便可,無需從新編譯整個客戶端。這樣地優勢使得Lua在遊戲開發領域一戰成名,能夠說是《魔獸世界》爲遊戲開發領域帶來了這樣激動人心的偉大語言,做爲Lua在遊戲領域攻城略地的嘗試,《魔獸世界》功不可沒。

      二、大話西遊2

      若是說《魔獸世界》開闢Lua在國外遊戲領域地戰場,那麼網易的《大話西遊2》無疑是開啓了國內遊戲製做公司使用Lua的先河。2002年網易開發《大話西遊2》時,決定在客戶端內嵌入新的腳本語言,由於當時使用的微軟JScript存在較多Bug、維護不便、兼容性差。當時該項目技術負責人云風吸收了《大話西遊1》時外掛氾濫的教訓,決定選擇一個新的語言,這樣既能擺脫對JScript的依賴,又能有效地打擊外掛製做者,權衡再三,最終選擇了Lua 4.0。後來《大話西遊2》在市場上取得了成功,國內遊戲開發行業紛紛受此影響採用Lua,能夠說是網易Lua走進了國內開發者的視野,不過到今天爲止,Lua在國內仍然是一門較爲小衆的語言,從《大話西遊2》引領國內開發者將視角轉向Lua到今天將近10餘年地時間,此中原因,只有你們本身去想個清楚啦。

      三、古劍奇譚

      《古劍奇譚》系列遊戲是由上海燭龍信息科技有限公司研發的大型3DRPG單機遊戲。遊戲設定源自於《山海經》,故事則以武俠和仙俠爲創做題材,以中國神話時代爲背景,講述了中國古代俠骨柔情的仙俠文化。《古劍奇譚》系列遊戲初代做品與二代做品採用的是不一樣的遊戲引擎和不一樣的戰鬥模式,儘管如此,咱們依然能從中找到一個共同點,那就是在初代做品和二代做品中都毫無例外的使Lua做爲遊戲地腳本語言。例以下面是《古劍奇譚》紅葉湖迷宮場景的Lua腳本節選:

 

[plain]  view plain  copy
 
  1. require("Necessary")  
  2. require("StoryUtility")  
  3. require("BigMap")  
  4. require("Script_DLC4")  
  5.   
  6. --------------如下爲初始化函數-------------  
  7.   
  8. function OnEnterLevel()  
  9.      if GetStoryVersion() == 2 then  
  10.          OnDLCEnterLevelM01()  
  11.      else  
  12.          if GetMainStory() == 10100 then  
  13.              callTaskFunction("story10100")  
  14.          elseif GetMainStory() == 161900 then  
  15.              callTaskFunction("story161900")  
  16.          end  
  17.   
  18.          if gValue.MK == 1 then  
  19.              showNPC("NPC 06", false)  
  20.              showNPC("NPC 07", false)  
  21.              enableTrigger("Tri_MK",false)  
  22.          elseif gValue.MK >1 then  
  23.              showNPC("NPC 04", false)  
  24.              showNPC("NPC 05", false)  
  25.              showNPC("NPC 06", false)  
  26.              showNPC("NPC 07", false)  
  27.              enableTrigger("Tri_MK",false)  
  28.              enableTrigger("Tri_MK 02",false)  
  29.          end  

       4、仙劍奇俠傳

 

       既然提到了古劍奇譚,怎麼能不提仙劍奇俠傳呢?雖然和古劍奇譚初代做品發佈時間僅僅相差一年的《仙劍奇俠傳五》市場反響並無像遊戲製做方所預料地那樣成功,不過這部做品值得稱讚地地方仍是蠻多的,由於進步老是要比缺點多的嘛,畢竟時代在進步,咱們不能老是拿仙劍初代做品的高度去要求後續做品,由於咱們已經再也不是那個年齡的人,而仙劍依然要不斷地突破自身、大膽創新和進取。好了,咱們暫時先感慨到這裏,仙劍4、仙劍五以及仙劍五前傳都使用了RenderWare引擎,可能惟一的不一樣就是仙劍五和仙劍五前傳都使用了Lua吧,下面一樣是一段從遊戲中提取的腳本:

 

[plain]  view plain  copy
 
  1. function baoxiang(id,npcID)  
  2.       
  3.     player.Control(0)  
  4.     pid=player.GetMainPlayer()  
  5.     player.SetAnim(pid,203)   
  6.     global.Print(id)  
  7.     global.Wait(1)  
  8.     y=flag.GetValue(15093)  
  9.       
  10.     ---------江洋大盜稱號得到-------------    
  11.     jyd=flag.GetValue(15255)  
  12.     jyd=jyd+1  
  13.     flag.SetValue(15255,jyd)  
  14.     global.Print(jyd)  
  15.     global.AddTimer(0.5,13279)  
  16. -----------------------------------------  
  17.       
  18.     if id~=17711 then  
  19.         npc.SetAnim(npcID,501)  
  20.         global.Wait(1)  
  21.     end  
  22.           

     5、金庸羣俠傳Lua復刻版

 

 

    4、帶你走進Lua的世界

    最後想和你們分享是Lua語言編程的一個簡單的示例,由於博主以爲之後作遊戲用腳本語言的場景會愈來愈多,因此能學會一門腳本語言能爲你的遊戲開發之路增色很多。由於博主剛開始學,因此腳本中有不足之處,但願你們能諒解,在學校的時間一每天地在減小,博主但願能和你們共同度過最後的這段時間。博主使用的是Lua5.2,使用的Sublime Text2做爲腳本編輯器配合LuaDev插件進行編程的,若是你們想用懶惰點的辦法,可使用Lua for Windows這個集成環境。好了,下面開始吧,做爲第一個Lua程序,咱們直接給出代碼,具體的語法及API你們能夠本身去查閱。
[plain]  view plain  copy
 
  1. --while-do示例代碼  
  2. myValue=10  
  3. while(myValue <= 20) do  
  4.     print(myValue)  
  5.     myValue=myValue+1  
  6. end  
  7. --sample table && for-do示例代碼  
  8. myTables={"Item0","Item1","Item2","Item3"}  
  9. for i=1,table.maxn(myTables) do  
  10.     print(myTables[i])  
  11. end  
  12. --complex table示例代碼  
  13. myTables={}  
  14. myTables["A"]="ItemA"  
  15. myTables["B"]="ItemA"  
  16. myTables["C"]="ItemA"  
  17. myTables["D"]="ItemA"  
  18. print(myTables["A"])--"ItemA"  
  19. --function示例代碼  
  20. function fib(n)  
  21.   if(n<2) then   
  22.     return n  
  23.   else  
  24.     return fib(n-1)+fib(n-2)  
  25.   end  
  26. end  
  27. --math示例代碼  
  28. maxValue=math.max(12,23,56,18,10)--56  
  29. minValue=math.min(25,34,12,75,8)--8  
  30. print(maxValue-minValue)--48  
  31. --字符串演示  
  32. myString="Hello this is the cool program language called Lua";    
  33. print(string.find(myString,"Lua"))--48,50  
  34. --io演示  
  35. io.write("Hello I get a powerful program language called Lua \n")  
  36. io.write(string.format("This Lua is %s and now is %s \n",_VERSION,os.date()))  
 
運行結果是:
 
 
  經過前面的學習,咱們知道設計Lua語言的目的是爲了將Lua嵌入應用程序中,從而爲應用程序提供靈活的擴展和定製功能。Lua語言自己沒有像其它語言提供豐富的類庫,所以Lua語言必須依賴於其它語言來完成功能上的擴展(但是正是在功能上犧牲才換來了Lua精簡而穩定的核心)。若是咱們要深刻了解Lua語言的話,就必需要了解Lua語言與其它語言的交互接口,由於這將是咱們使用Lua語言的基礎。那麼,今天就讓博主來帶領你們一塊兒學習Lua語言與其它語言的交互吧!

 1、Lua堆棧

    若是咱們想要理解Lua語言與其它語言交互的實質,咱們首先就要理解Lua堆棧。簡單來講,Lua語言之因此能和C/C++進行交互,主要是由於存在這樣一個無處不在的虛擬棧。棧的特色是先進後出,在Lua語言中,Lua堆棧是一種索引能夠是正數或者負數的結構,並規定正數1永遠表示棧底,負數-1永遠表示棧頂。換句話說呢,在不知道棧大小的狀況下,咱們能夠經過索引-1取得棧底元素、經過索引1取得棧頂元素。下面呢,咱們經過一個實例來加深咱們對於這段話的理解:

 

[cpp]  view plain  copy
 
  1. #include <iostream>  
  2.   
  3. extern "C" {  
  4. #include "lua.h"  
  5. #include "lualib.h"  
  6. #include "lauxlib.h"  
  7. }  
  8.   
  9. using namespace std;  
  10.   
  11. int main()  
  12. {  
  13.     //建立Lua環境  
  14.     lua_State* L=lua_open();  
  15.     //打開Lua標準庫,經常使用的標準庫有luaopen_base、luaopen_package、luaopen_table、luaopen_io、  
  16.     //luaopen_os、luaopen_string、luaopen_math、luaopen_debug  
  17.     luaL_openlibs(L);  
  18.     //壓入一個數字20  
  19.     lua_pushnumber(L,20);  
  20.     //壓入一個數字15  
  21.     lua_pushnumber(L,15);  
  22.     //壓入一個字符串Lua  
  23.     lua_pushstring(L,"Lua");  
  24.     //壓入一個字符串C  
  25.     lua_pushstring(L,"C");  
  26.     //獲取棧元素個數  
  27.     int n=lua_gettop(L);  
  28.     //遍歷棧中每一個元素  
  29.     for(int i=1;i<=n;i++)  
  30.     {  
  31.         cout << lua_tostring(L ,i) << endl;  
  32.     }  
  33.     return 0;  
  34. }  

在上面的這段代碼中,咱們能夠能夠看到咱們首先建立了一個lua_State類型的變量L,咱們能夠將它理解成一個Lua運行環境的上下文(Context),這裏咱們在Lua堆棧中壓入了四個元素:20、1五、"Lua"、"C"而後將其輸出,若是你們理解了Lua堆棧中的索引,那麼最終輸出的結果應該是:20、1五、"Lua"、"C",由於索引1始終指向棧底,最早入棧的元素會處於棧底。所以當咱們按照遞增的索引順序來輸出棧中的元素的話,其實是自下而上輸出,這樣咱們就能獲得這樣的結果了。

       好了,若是這段代碼沒有什麼問題的話,接下來咱們來說解Lua爲C/C++提供的接口,它們均被定義在lua.h文件中。Lua提供的C/C++接口大部分與棧操做有關,所以深刻理解Lua堆棧是學習Lua語言的重點和難點。經過數據結構的知識,咱們能夠知道棧有出棧和入棧兩種基本操做,Lua提供的C API中入棧能夠經過push系列的方法來實現,以下圖所示:

而出棧或者說查詢的方法則能夠經過to系列的方法來實現,以下圖:

這兩部分是學習Lua語言必定要去了解的內容,由於之後若是須要咱們將Lua整合到其它項目中這些內容,這些東西能夠說是原理性、核心性的東西。好了,下面咱們利用這裏的API對一個示例代碼進行改造,這裏加入了對棧中元素類型的判斷:

 

[cpp]  view plain  copy
 
  1. #include <iostream>  
  2.   
  3. extern "C" {  
  4. #include "lua.h"  
  5. #include "lualib.h"  
  6. #include "lauxlib.h"  
  7. }  
  8.   
  9. using namespace std;  
  10.   
  11. int main()  
  12. {  
  13.     //建立Lua環境  
  14.     lua_State* L=lua_open();  
  15.     //打開Lua標準庫,經常使用的標準庫有luaopen_base、luaopen_package、luaopen_table、luaopen_io、  
  16.     //luaopen_os、luaopen_string、luaopen_math、luaopen_debug  
  17.     luaL_openlibs(L);  
  18.     //壓入一個數字20  
  19.     lua_pushnumber(L,20);  
  20.     //壓入一個字符串15  
  21.     lua_pushnumber(L,15);  
  22.     //壓入一個字符串Lua  
  23.     lua_pushstring(L,"Lua");  
  24.     //壓入一個字符串C  
  25.     lua_pushstring(L,"C");  
  26.     //獲取棧中元素個數  
  27.     int n=lua_gettop(L);  
  28.     //遍歷棧中每一個元素  
  29.     for(int i=1;i<=n;i++)  
  30.     {  
  31.         //類型判斷  
  32.         switch(lua_type(L,i))  
  33.        {  
  34.           case LUA_TSTRING:  
  35.             cout << "This value's type is string" << endl;  
  36.           break;  
  37.           case LUA_TNUMBER:  
  38.             cout << "This value's type is number" << endl;  
  39.           break;  
  40.         }  
  41.         //輸出值  
  42.         cout << lua_tostring(L ,i) << endl;  
  43.     }  
  44.   
  45.     //釋放Lua  
  46.     lua_close(L);  
  47. }  

 

 

    2、Lua與C++交互

   Lua與C++的交互從宿主語言的選擇劃分上能夠分爲C++調用Lua和Lua調用C++兩中類型:

   一、C++調用Lua

    使用C++調用Lua時咱們能夠直接利用C++中的Lua環境來直接Lua腳本,例如咱們在外部定義了一個lua腳本文件,咱們如今須要使用C++來訪問這個腳本該怎麼作呢?在這裏咱們可使用luaL_loadfile()、luaL_dofile()這兩個方法個方法來實現,其區別是前者僅加載腳本文件然後者會在加載的同時調用腳本文件。咱們一塊兒來看下面的代碼:

 

[cpp]  view plain  copy
 
  1. #include <iostream>  
  2.   
  3. using namespace std;  
  4.   
  5. #include <iostream>  
  6.   
  7. extern "C" {  
  8. #include "lua.h"  
  9. #include "lualib.h"  
  10. #include "lauxlib.h"  
  11. }  
  12.   
  13. using namespace std;  
  14.   
  15. int main()  
  16. {  
  17.     //建立Lua環境  
  18.     lua_State* L=luaL_newstate();  
  19.     //打開Lua標準庫,經常使用的標準庫有luaopen_base、luaopen_package、luaopen_table、luaopen_io、  
  20.     //luaopen_os、luaopen_string、luaopen_math、luaopen_debug  
  21.     luaL_openlibs(L);  
  22.   
  23.     //下面的代碼能夠用luaL_dofile()來代替  
  24.     //加載Lua腳本  
  25.     luaL_loadfile(L,"script.lua");  
  26.     //運行Lua腳本  
  27.     lua_pcall(L,0,0,0);  
  28.   
  29.     //將變量arg1壓入棧頂  
  30.     lua_getglobal(L,"arg1");  
  31.     //將變量arg2壓入棧頂  
  32.     lua_getglobal(L,"arg2");  
  33.   
  34.     //讀取arg一、arg2的值  
  35.     int arg1=lua_tonumber(L,-1);  
  36.     int arg2=lua_tonumber(L,-2);  
  37.   
  38.     //輸出Lua腳本中的兩個變量  
  39.     cout <<"arg1="<<arg1<<endl;  
  40.     cout <<"arg2="<<arg2<<endl;  
  41.   
  42.     //將函數printf壓入棧頂  
  43.     lua_getglobal(L,"printf");  
  44.     //調用printf()方法  
  45.     lua_pcall(L,0,0,0);  
  46.   
  47.     //將函數sum壓入棧頂  
  48.     lua_getglobal(L,"sum");  
  49.     //傳入參數  
  50.     lua_pushinteger(L,15);  
  51.     lua_pushinteger(L,25);  
  52.     //調用sum()方法  
  53.     lua_pcall(L,2,1,0);//這裏有2個參數、1個返回值  
  54.     //輸出求和結果  
  55.     cout <<"sum="<<lua_tonumber(L,-1)<<endl;  
  56.   
  57.     //將表table壓入棧頂  
  58.     lua_getglobal(L,"table");  
  59.     //獲取表內a的值  
  60.    lua_getfield(L,-1,"a"); 
  61.     //輸出表中元素  
  62.     cout <<"table.a="<<lua_tostring(L,-1)<<endl;  
  63.   
  64. }  
在這段代碼中咱們調用了一個外部的文件script.lua。這是一個Lua腳本文件,在調試階段,咱們須要將其放置在和C++項目源文件同級的目錄下,而在正式運行階段,咱們只須要將其和最終的可執行文件放在同一個目錄下就行了。下面是腳本代碼:

 

 

[cpp]  view plain  copy
 
  1. --在Lua中定義兩個變量  
  2. arg1=15  
  3. arg2=20  
  4.   
  5. --在Lua中定義一個表  
  6. table=  
  7. {  
  8.     a=25,  
  9.     b=30  
  10. }  
  11.   
  12. --在Lua中定義一個求和的方法  
  13. function sum(a,b)  
  14.   return a+b  
  15. end  
  16.   
  17. --在Lua中定義一個輸出的方法  
  18. function printf()  
  19.   print("This is a function declared in Lua")  
  20. end  
咱們注意到在腳本文件中咱們定義了一些變量和方法,在C++代碼中咱們首先用lua_getglobal()方法來說Lua腳本中的變量或函數壓入棧頂,這樣咱們就可使用相關的to系列方法去獲取它們,因爲每次執行lua_getglobal()都是在棧頂,由於咱們使用索引值-1來獲取棧頂的元素。C++能夠調用Lua中的方法,第一步和普通的變量相同,是將Lua中定義的方法壓入棧頂,由於只有壓入棧中,咱們纔可以使用這個方法,接下來,咱們須要經過push系列的方法爲棧中的方法傳入參數,在完成參數傳入後,咱們可使用一個lua_pcall()的方法來執行棧中的方法,它有四個參數,第一個參數是Lua環境狀態Lua_State,第二個參數是要傳入的參數個數,第三個參數是要返回的值的數目,第四個參數通常默認爲0。因爲Lua支持返回多個結果,所以,咱們能夠充分利用Lua的這一特色來返回多個值。執行該方法後,其結果會被壓入棧頂,因此咱們能夠索引值-1來獲取函數的結果。若是函數有多個返回值,則按照函數中定義的return 順序,依次入棧,索引值-1表明最後一個返回值。好了,這就是C++調用Lua的具體實現了。

 

     二、Lua調用C++

     首先咱們在C++中定義一個方法,該方法必須以Lua_State做爲參數,返回值類型爲int,表示要返回的值的數目。

 

[cpp]  view plain  copy
 
  1. static int AverageAndSum(lua_State *L)  
  2. {  
  3.     //返回棧中元素的個數  
  4.     int n = lua_gettop(L);  
  5.     //存儲各元素之和  
  6.     double sum = 0;  
  7.     for (int i = 1; i <= n; i++)  
  8.     {  
  9.         //參數類型處理  
  10.         if (!lua_isnumber(L, i))  
  11.         {  
  12.             //傳入錯誤信息  
  13.             lua_pushstring(L, "Incorrect argument to 'average'");  
  14.             lua_error(L);  
  15.         }  
  16.         sum += lua_tonumber(L, i);  
  17.     }  
  18.     //傳入平均值  
  19.     lua_pushnumber(L, sum / n);  
  20.     //傳入和  
  21.     lua_pushnumber(L, sum);  
  22.   
  23.     //返回值的個數,這裏爲2  
  24.     return 2;  
  25. }  
接下來咱們在C++中使用lua_register()方法完成對該方法的註冊

 

 

[cpp]  view plain  copy
 
  1. lua_register(L, "AverageAndSum", AverageAndSum);  
這樣咱們就能夠在Lua環境中使用這個方法啦,前提是定義必須在執行代碼以前完成,咱們在Lua腳本文件下加入對該方法的調用:

 

 

[plain]  view plain  copy
 
  1. --在Lua中調用C++中定義而且註冊的方法  
  2. average,sum=AverageAndSum(20,52,75,14)  
  3. print("Average=".average)  
  4. print("Sum=".sum)  
若是咱們須要在C++中查看該方法調用的結果,那麼這個在C++中調用Lua是同樣的。好了,C++和Lua的交互終於講完了,被這塊的代碼糾結了好幾天,這下總算是搞明白了。固然這只是對原理的一種學習和理解啦,若是但願更好的使用Lua調用C++,建議瞭解這幾個項目:

 

LuaPlusLuaBind。這樣相信你們對於C++中的方法如何在Lua中綁定會有更好的認識吧!

 

    3、Lua與C#交互

首先看下不一樣版本Lua介紹:

luainterface、nlua、ulua、unilua、cstolua、slua

luainterface:LuaInterface是開源的C#的lua橋接庫,配合開源庫luanet,能輕鬆實現Lua,C#相互調用和參數事件傳遞。但做者僅完成了windows程序的功能實現,跨平臺並無完成,做者於2013年4月30日中止更新luainterface,並推薦你們關注luainterface的一個分支Nlua。Nlua是實現跨平臺的Luainterface的升級版,uLua和NLua都是基於此庫升級編寫

nlua:是LuaInterface的一個分支,繼承了Luainterface的全部優勢,並將Luanet庫功能集成到代碼中,實現跨平臺(Windows, Linux, Mac, iOS , Android, Windows Phone 7 and 8),對ios平臺作了特殊處理,如支持了委託的橋接。
配合NLua有2種Lua實現,第一種是KeraLua,基於原生Lua,將C API 進行簡單的包裝,使C# 能夠方便使用 Lua C API,第二種是KopiLua,C#實現的Lua vm(對,和UniLua同樣也是純C#實現的Lua vm)。如下爲關於兩種方案的比較。
使用KeraLua,必須將lua 編譯成 Unity3D Plugin,並將編譯好的文件放到Plugins文件夾下相應的平臺文件夾中。並定義#define USE_KERALUA
使用KopiLua,定義#define USE_KERALUA便可

ulua:基於luainterface升級版,uLua = Lua + LuaJIT + LuaInterface,全平臺支持。在原生C的基礎上使用LuaJit進行加速,若是uLua效率高,LuaJit有很大功勞,做者僅僅提供了uLua插件包,並未提供整套插件源碼。此外,做者重寫了loadfile、print等api,使用很是簡單,導入package,就能夠開始編寫代碼了。

unilua:是雲風團隊阿南的做品,是lua5.2的C#版,

純C#的Lua 5.2實現,是否是感受似曾相識,對的,KopiLua也是純C#實現的Lua vm,雖然Unilua出名,可是沒有KopiLua的配套庫好用,其自身同的Ffi庫,是實驗性質的庫,不完善,做者不推薦使用,雖然做者在其商業項目中使用,可是這只是其中一部分代碼,Unilua和C#中間層的代碼做者並無開源。UniLua僅僅提供了Lua原生的接口,若是要在Lua代碼中調用C#,使用就須要把Luanet 移植到Unilua代碼中,總的來講很蛋疼,據推測Unilua方法都是使用Lua標準的命名方式,因此將luanet源碼中全部C接口所有手動改寫成Unilua 的接口,就可使用,這個工做量,等閒的時候把玩比較好。

cstolua:cstolua是做者對ulua的擴展,提升了效率

slua:也是從ulua擴展而來,官方說效率比cstolua還高,不過也有不少人質疑過 http://www.ulua.org/cstolua.html    http://www.slua.net/   http://www.sineysoft.com/post/164

效率

cstolua > ulua > nlua > luainterface > unilua

  

既然咱們已經知道了C++是怎樣和Lua完成交互的,理論上咱們能夠經過編寫dll的方式將前面完成的工做繼續在C#中運行,但是這樣作咱們須要花費大量時間在三種語言之間糾結,由於這樣會增長調試的難度。以前有個作coco2dx的朋友抱怨要在C++、Javascript、Lua之間來回跑,我當時沒以爲有什麼,由於我最困難的時候就是C#和Java項目混合的情形,現在我算是深有體會了啊,這算是報應嗎?哈哈,好了,不說這個了,好在C#與Lua的交互目方面前已經有了較好的解決方案,在開源社區咱們能夠找到不少的支持在C#中調用Lua的工具庫,博主這裏向你們推薦的是LuaInterface這個開源項目,這個開源項目我找到了兩個地址:

一、https://github.com/Jakosa/LuaInterface

二、http://code.google.com/p/luainterface

博主我的感受這應該是同一個項目,由於兩個項目的源代碼是同樣的,不過從Github上下載的項目在使用的時候會報錯,估計是我電腦裏的Lua版本和它項目裏所用的Lua的版本不一致形成的吧。

LuaInterface中的核心就是C#經過Pinvoke對Lua C庫調用的封裝,因此,在Unity中,LuaInterface就是C#與Lua進行交互的接口。

Lua是一種很好的擴展性語言,Lua解釋器被設計成一個很容易嵌入到宿主程序的庫。LuaInterface則用於實現Lua和CLR的混合編程。

LuaInterface.Lua類是CLR訪問Lua解釋器的主要接口,一個LuaInterface.Lua類對象就表明了一個Lua解釋器(或Lua執行環境),Lua解釋器能夠同時存在多個,而且它們之間是徹底相互獨立的。

 

下面的這個項目是可使用的,博主這裏寫了一個簡單的示例:

 

[csharp]  view plain  copy
 
  1. //------------------------------------------------------------------------------  
  2. // <summary>  
  3. //     這是一個用以演示LuaInterface的簡單程序,經過LuaInterface咱們能夠實如今C#與Lua的  
  4. //     的相互通訊。Lua是一個輕巧而高效的語言,它能夠和任何語言混合使用。Lua語言最初並非  
  5. //     爲遊戲開發而誕生,倒是由於遊戲開發而成名。目前,在世界上有大量的遊戲使用了Lua做爲它  
  6. //     的腳本語言。如圖Unity使用了C#做爲它的語言,Lua在遊戲開發領域發揮着不可忽視的重要做  
  7. //     用。使用LuaInterface的方法以下:  
  8. //     1.C#  
  9. //     註冊Lua中可調用方法:  
  10. //    mLua.RegisterFunction(Lua調用方法名, 類, 類.GetMethod(C#方法名));  
  11. //    注:C#不要使用方法級泛型,即 void Fun<T>(string str);,若是使用,系統自動斷定T爲第一個參數的類型。  
  12. //     加載Lua代碼  
  13. //     mLua.DoString(Lua代碼);  
  14. //    mLua.DoFile(Lua文件絕對路徑);  
  15. //     調用Lua方法  
  16. //     mLua.GetFunction(Lua方法).Call(參數);  注:此處參數不要傳遞dynamic類型的類,不然Lua中沒法獲取屬性值  
  17. //     2.Lua  
  18. //     調用C#方法時須要先註冊註冊後按照Lua方法處理  
  19. // </summary>  
  20. //------------------------------------------------------------------------------  
  21. using System;  
  22. using LuaInterface;  
  23. namespace LuaExample  
  24. {  
  25.     public class LuaScript  
  26.     {  
  27.         //定義LuaFile屬性以便於從外部調用一個Lua腳本  
  28.         private string mLuaFile;  
  29.         public string LuaFile {  
  30.             get {  
  31.                 return mLuaFile;  
  32.             }  
  33.             set {  
  34.                 mLuaFile = value;  
  35.             }  
  36.         }  
  37.   
  38.         //Lua虛擬機  
  39.         private Lua mLua;  
  40.   
  41.         //構造函數  
  42.         public LuaScript ()  
  43.         {  
  44.             //初始化Lua虛擬機  
  45.             mLua=new Lua();  
  46.             //註冊Printf方法  
  47.             mLua.RegisterFunction("Printf",this,this.GetType().GetMethod("Printf"));  
  48.         }  
  49.   
  50.         //定義一個C#方法供Lua使用  
  51.         public void Printf(string str)  
  52.         {  
  53.             Console.WriteLine("This Method is Invoked by Lua:" + str);  
  54.         }  
  55.   
  56.         //在C#中調用Lua方法  
  57.         public void DoFile()  
  58.         {  
  59.             if(mLuaFile!="")  
  60.                 //執行Lua腳本中的代碼  
  61.                 mLua.DoFile(mLuaFile);  
  62.         }  
  63.   
  64.         //在C#中調用Lau方法  
  65.         public void DoString()  
  66.         {  
  67.             //以字符串形式定義的Lua腳本  
  68.             string mFuncString="function Add(a,b) io.write(a+b) end";  
  69.             //在Lua中定義該方法  
  70.             mLua.DoString(mFuncString);  
  71.             //調用該方法  
  72.             mLua.GetFunction("Add").Call(4,8);  
  73.         }  
  74.   
  75.         //在Lua中調用C#腳本  
  76.         public void Invoke()  
  77.         {  
  78.             //調用註冊的Printf方法  
  79.             mLua.GetFunction("Printf").Call("Hello Lua");  
  80.         }  
  81.     }  
  82. }  
接下來咱們編寫一個主類來調用這個類:

 

 

[csharp]  view plain  copy
 
  1. using System;  
  2. using LuaInterface;  
  3.   
  4. namespace LuaExample  
  5. {  
  6.     class MainClass  
  7.     {  
  8.         public static void Main (string[] args)  
  9.         {  
  10.             //實例化LuaSxript  
  11.             LuaScript mLua=new LuaScript();  
  12.             //設置LuaFile  
  13.             mLua.LuaFile="D:\\test.lua";  
  14.             //調用字符串中定義的Lua方法  
  15.             mLua.DoString();  
  16.             //爲美觀考慮增長一個空行  
  17.             Console.WriteLine();  
  18.             //執行Lua文件中定義的腳本  
  19.             mLua.DoFile();  
  20.             //調用C#中定義的方法  
  21.             mLua.Invoke();  
  22.         }  
  23.     }  
  24. }  
好了,C#與Lua的交互解決了,更多的內容期待着你們自行到該項目源代碼中去尋找。好了,先這樣吧!

 

Lua和C++交互的文章能夠看另一篇:Lua和C++交互詳細總結

C#和Lua相互調用看文章:

一、 C#與Lua相互調用

二、在Unity中使用Lua腳本:語言層和遊戲邏輯粘合層處理

LuaInterface簡介:

LuaInterface.Lua類是CLR訪問Lua解釋器的主要接口,一個LuaInterface.Lua類對象就表明了一個Lua解釋器(或Lua執行環境),Lua解釋器能夠同時存在多個,而且它們之間是徹底相互獨立的。

  下面的簡單代碼展現瞭如下功能:

  (1)CLR訪問Lua的全局域: 下標/索引操做[]

  (2)CLR新建Lua的table:NewTable

  (3)CLR中執行Lua腳本代碼或腳本文件:DoFile、DoString

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LuaInterface;

namespace TestCSharpAndLuaInterface
{
        static void Main(string[] args)
        {
            // 新建一個Lua解釋器,每個Lua實例都相互獨立
            Lua lua = new Lua();

            // Lua的索引操做[]能夠建立、訪問、修改global域,括號裏面是變量名
            // 建立global域num和str
            lua["num"] = 2;
            lua["str"] = "a string";
            
            // 建立空table
            lua.NewTable("tab");

            // 執行lua腳本,着兩個方法都會返回object[]記錄腳本的執行結果
            lua.DoString("num = 100; print(\"i am a lua string\")");
            lua.DoFile("C:\\luatest\\testLuaInterface.lua");
            object[] retVals = lua.DoString("return num,str");

            // 訪問global域num和str
            double num = (double)lua["num"];
            string str = (string)lua["str"];

            Console.WriteLine("num = {0}", num);
            Console.WriteLine("str = {0}", str);
            Console.WriteLine("width = {0}", lua["width"]);
            Console.WriteLine("height = {0}", lua["height"]);
        }
    }
}
複製代碼

LuaIntrface自動對應Lua和CLR中的一些基礎類型
  [nil, null]
  [string, System.String]
  [number, System.Double]
  [boolean, System.Boolean]
  [table, LuaInterface.LuaTable]
  [function, LuaInterface.LuaFunction]
以上對應關係反之亦然。

特殊類型:userdata

  (1)CLR中不能自動匹配Lua類型的對象(以上基礎類型以外的類型)傳給Lua時,轉換爲userdata,當Lua把這些userdata傳回給CLR時,這些userdata又轉換回原類型對象;
  (2)Lua裏面生成的userdata從Lua傳到CLR時轉換爲LuaInterface.LuaUserData。

  LuaTable和LuaUserData都有索引操做[],用來訪問或修改域值,索引能夠爲string或number。
  LuaFunction和LuaUserData都有call方法用來執行函數,能夠傳入任意多個參數並返回多個值。

Lua調用CLR的函數:RegisterFunction方法用來將CLR函數註冊進Lua解釋器,供Lua代碼調用,看下面這個例子:

複製代碼
namespace TestCSharpAndLuaInterface
{
    class TestClass
    {
        private int value = 0;

        public void TestPrint(int num)
        {
            Console.WriteLine("TestClass.TestPrint Called! value = {0}", value = num);
        }

        public static void TestStaticPrint()
        {
            Console.WriteLine("TestClass.TestStaticPrint Called!");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Lua lua = new Lua();
            
            TestClass obj = new TestClass();
            // 註冊CLR對象方法到Lua,供Lua調用
            lua.RegisterFunction("LuaTestPrint", obj, obj.GetType().GetMethod("TestPrint"));    // 也可用 typeof(TestClass).GetMethod("TestPrint")
            // 註冊CLR靜態方法到Lua,供Lua調用
            lua.RegisterFunction("LuaStaticPrint", null, typeof(TestClass).GetMethod("TestStaticPrint"));

            lua.DoString("LuaTestPrint(10)");
            lua.DoString("LuaStaticPrint()");
        }
    }
}
複製代碼

(二)CLR from Lua

(1)加載和實例化CLR類型

測試環境有兩種方式:

  第一種:純Lua文件中進行測試

  將LuaForWindows安裝的LuaInterface.dll和luanet.dll都拷貝到本身註冊的環境變量的目錄下,好比個人是"C:\\luatest\\",而後就能夠在Lua編輯器中編寫測試代碼了,以下:

複製代碼
--package.cpath  = "C:\\luatest\\?.dll"

require "luanet"

--加載CLR的類型、實例化CLR對象
luanet.load_assembly("System.Windows.Forms")
luanet.load_assembly("System.Drawing")
Form = luanet.import_type("System.Windows.Forms.Form")
StartPosition = luanet.import_type("System.Windows.Forms.FormStartPosition")

print(Form)
print(StartPosition)
複製代碼

  上面的代碼演示了若是利用LuaInterface的luanet在Lua中加載CLR的類型。在配置編譯環境的時候必定要注意將兩個dll同時拷貝到一個目錄下,由於luanet.dll是依賴LuaInterfce.dll的。

  第二種:在C#工程中測試

  仍是在外部單獨編寫lua代碼文件,而後在C#工程中使用lua.DoFile接口運行lua代碼。這種方式比較靈活而且可以更方便的測試LuaInterface所提供的各項功能,咱們後面的測試代碼均是在這種模式系下進行測試。

  這種模式下就不須要在lua腳本中手動require "luanet"了,由於已經手動將LuaInterface的引用添加到工程中了,lua腳本中直接使用luanet就能夠訪問各接口了。

  luanet.load_assembly函數:加載CLR程序集;

  luanet.import_type函數:加載程序集中的類型;

  luanet.get_constructor_bysig函數:顯示獲取某個特定的構造函數;

  c#主要代碼以下:

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LuaInterface;

namespace TesLuaInterface
{
    class TestClass2
    {
        public TestClass2(string str)
        {
            Console.WriteLine("called TestClass2(string str) str = {0}", str);
        }

        public TestClass2(int n)
        {
            Console.WriteLine("called TestClass2(int n) n = {0}", n);
        }

        public TestClass2(int n1, int n2)
        {
            Console.WriteLine("called TestClass2(int n1, int n2) n1 = {0}, n2 = {1}", n1, n2);
        }
    }

        // 加載和實例化CLR類型
        static void Main(string[] args)
        {
            Lua lua = new Lua();

            lua.DoFile("C:\\luatest\\testLuaNet.lua");
        }
    }
}
複製代碼

  lua主要代碼以下:

複製代碼
-- 加載自定義類型,先加載程序集,在加載類型
luanet.load_assembly("TestEnvi")
TestClass = luanet.import_type("TesLuaInterface.TestClass2")

obj1 = TestClass(2, 3)    -- 匹配public TestClass2(int n1, int n2)
obj2 = TestClass("x")    -- 匹配public TestClass2(string str)
obj3 = TestClass(3)        -- 匹public TestClass2(string str)

TestClass_cons2 = luanet.get_constructor_bysig(TestClass, 'System.Int32')
obj3 = TestClass_cons2(3)    -- 匹配public TestClass2(int n)
複製代碼

  TestEnvi爲我建的工程代碼的程序集名字,這一項是能夠在工程屬性中進行設置的,TestLuaInterface爲測試代碼的命名空間。

  從上面的構造函數的匹配能夠看出,LuaInterface匹配構造函數的規律:

  LuaInterface匹配第一個可以匹配的構造函數,在這個過程當中,numerical string(數字字符串)會自動匹配number,而number能夠自動匹配string,因此TestClass(3)匹配到了參數爲string的構造函數。

  若是必定要手動匹配某個構造函數,則可使用luanet.get_constructor_bysic函數。

(2)訪問CLR類型對象的字段和方法

  Lua代碼中,訪問CLR類型對象的字段的方式和訪問table的鍵索引同樣,好比button1.Text、button["Text"];

  Lua代碼中,訪問CLR類型對象的函數的方式和調用table的函數同樣,好比form:ShowDialog()。

  規則很簡單,但在訪問函數的時候,有如下幾種狀況須要注意的:

  (a)當有重載函數時,函數的匹配過程和上面提到的構造函數的匹配過程同樣,自動匹配第一個可以匹配的函數。若是必定要手動調用某個特定參數的函數,可使用luanet.get_method_bysig函數來制定,好比:

  setMethod=get_method_bysig(obj,'setValue','System.String')"
  setMethod('str')

  (b)當函數有out或ref參數時,out參數和ref參數和函數的返回值一塊兒返回,而且調用函數時out參數不須要傳入,好比:

    -- calling int obj::OutMethod1(int,out int,out int)
    retVal,out1,out2 = obj:OutMethod1(inVal)
    -- calling void obj::OutMethod2(int,out int)
    retVal,out1 = obj:OutMethod2(inVal) -- retVal ser´a nil
    -- calling int obj::RefMethod(int,ref int)

  (c)若是一個對象有兩個Interface,而且兩個Interface都有某個同名函數好比,IFoo.method()和IBar.method(),這種狀況下obj["IFoo.method"]表示訪問前者。

  訪問CLR類型對象的字段和函數的示例代碼以下:

複製代碼
luanet.load_assembly("System.Windows.Forms")
luanet.load_assembly("System.Drawing")
Form = luanet.import_type("System.Windows.Forms.Form")
Button = luanet.import_type("System.Windows.Forms.Button")
Point = luanet.import_type("System.Drawing.Point")
StartPosition = luanet.import_type("System.Windows.Forms.FormStartPosition")

form1 = Form()
button1 = Button()
button2 = Button()
position = Point(10, 10)
start_position = StartPosition.CenterScreen

button1.Text = "OK"
button2["Text"] = "Cancel"
button1.Location = position
button2.Location = Point(button1.Left, button1.Height + button1.Top + 10)
form1.Controls:Add(button1)
form1.Controls:Add(button2)
form1.StartPosition = start_position
form1:ShowDialog()
複製代碼

(3)事件處理,添加和刪除事件委託

  LuaInterface爲Event提供了Add和Remove函數來註冊和移除事件處理函數。Add函數傳入一個Lua函數,將其轉換爲一個CLR委託(delegate),並返回這個委託。

function handle_mouseup(sender,args)
  print(sender:ToString() .. ’ MouseUp!’)
  button.MouseUp:Remove(handler)
end
handler = button.MouseUp:Add(handle_mouseup)

(4)LuaInterface三種擴展CLR的方法

  LuaInterface提供了三種擴展CLR的方法,第一種就是上面提到的添加委託的方式,在須要delegate的地方傳入Lua function,LuaInterface利用Lua function建立一個CLR delegate 並傳入CLR。  

  第二種是在須要CLR Interface實例的地方傳入一個Lua Table,好比:

複製代碼
-- interface ISample { int DoTask(int x, int y); }
-- SomeType.SomeMethod signature: int SomeMethod(ISample i)
-- obj is instance of SomeType
sum = {}
function sum:DoTask(x,y)
return x+y
end
-- sum is converted to instance of ISample
res = obj:SomeMethod(sum)
複製代碼

  若是Interface裏面有多個重載接口,那麼Lua Table須要實現每個版本的接口函數,而且要注意out和ref參數的處理:

複製代碼
-- interface ISample2 { void DoTask1(ref int x, out int y);
-- int DoTask2(int x, out int y); }
-- SomeType.SomeMethod signature: int SomeMethod(ISample i)
-- obj is instance of SomeType
inc = {}
function inc:DoTask1(x)
return x+1,x
end
function inc:DoTask2(x)
return x+1,x
end
res = obj:SomeMethod(sum)
複製代碼

  第三種是利用Lua Table繼承CLR Class,也就是用Table做爲其子類,這裏CLR Class必須擁有virtual函數,而且Lua Table必須至少重寫一個virtual函數。主要相關函數是luanet.make_object。

複製代碼
-- class SomeObject {
-- public virtual int SomeMethod(int x, int y) { return x+y; } }
-- SomeType.SomeMethod signature: int SomeMethod(SomeObject o)
-- obj is instance of SomeType
some_obj = { const = 4 }
function some_obj:SomeMethod(x,y)
local z = self.base:SomeMethod(x,y)
return z*self.const
end
SomeObject = luanet.import_type(’SomeObject’)
luanet.make_object(some_obj,SomeObject)
res = some_obj:SomeMethod(2,3) -- returns 20
res = some_obj:ToString() -- calls base method
res = obj:SomeMethod(some_obj) -- passing as argument
複製代碼

  由於Table做爲子類實例,那麼就能夠在須要Class的地方傳入這個Table實例。注意,若是Table沒有重寫任何virtual函數,則直接返回父類對象。固然,做爲子類,能夠直接訪問父類中其餘的還接口。

  以上三種概括起來就是:Lua Function-->CLR delegate、Lua Table-->CLR Interface、 Lua Table-->CLR Class。

 Unity3D基於Mono虛擬機,因此理論上.NET的類庫是能夠直接在Unity3D中使用的。但是考慮到Unity3D跨平臺的須要,咱們選擇的工具必須在各個平臺得到良好的支持。在前文中提到的LuaInterface理論上是能夠在Unity3D中使用的,但是因爲IOS不支持反射機制,因此這個類庫咱們沒法直接在Unity3D中使用的。

 ulua:基於luainterface升級版,uLua = Lua + LuaJIT + LuaInterface,全平臺支持。在原生C的基礎上使用LuaJit進行加速,若是uLua效率高,LuaJit有很大功勞,做者僅僅提供了uLua插件包,並未提供整套插件源碼。此外,做者重寫了loadfile、print等api,使用很是簡單,導入package,就能夠開始編寫代碼了。

uLua = Lua + LuaJit(解析器、解釋器) +LuaInterface。

uLua方案比較成熟,它並無太多本身的代碼,主要是把LuaInterface和Lua解釋器整合了一下,都是比較成熟的代碼,相對會穩定一些。

原文:Unity3D 預備知識:C#與Lua相互調用在Unity中使用Lua腳本:語言層和遊戲邏輯粘合層處理

 

入門例子

  以下是構建這個例子的步驟。

(1)下載ULua源碼。

(2)在Unity中新建一個項目,並將ULua源碼拷貝到Assets目錄下。

    

(3)將ulua.dll(就是上面提到的C庫)放到Assets下的Plugins文件夾中。(沒有Plugins文件夾就新建一個)

(4)在Assets下的Script文件夾中新建一個腳本CSharpLuaTest.cs,並將該腳本綁定到Main Camera上。

(5)在CSharpLuaTest.cs中編輯如下內容:

複製代碼
public class CSharpLuaTest : MonoBehaviour {

    private LuaState luaState = new LuaState(); // 建立lua虛擬機 
   
    void Start ()
    {
        // 在lua虛擬機(全局)中註冊自定義函數
        this.luaState.RegisterFunction("CSharpMethod", this, this.GetType().GetMethod("CSharpMethod"));

        // 加載lua文件(絕對路徑)  
        this.luaState.DoFile(Application.streamingAssetsPath + "/Test.lua");

        // 加載完文件後,使用GetFunction獲取lua腳本中的函數,再調用Call執行。  
        object[] objs = luaState.GetFunction("LuaMethod").Call(999);        
        Debug.Log(string.Format("{0} - {1}" ,objs[0], objs[1]));
    }

    //自定義功能函數,將被註冊到lua虛擬機中  
    public string CSharpMethod(int num)   
    {
        return string.Format("Hello World {0} !" , num+1);
    }
   
    void Update () {    
    }
}
複製代碼

(6)在Assets下的StreamingAssets文件夾中新建一個Lua腳本文件Test.lua,打開Test.lua文件,並編輯以下內容:

1
2
3
4
function  LuaMethod(i)
s = CSharpMethod(i); --調用C #方法
return  i,s;
end

(7)運行Unity項目,則能夠看到輸出:999 - Hello World 1000 ! 

要點說明

  最後簡單說一下上面代碼的要點:

1.若是一個C#方法要被Lua調用,則首先要將其註冊到Lua虛擬機中(LuaState.RegisterFunction)。以後,在Lua中就能夠經過註冊的名稱來調用這個C#方法。

2.若是C#要調用Lua中的函數,則

(1)首先要在Lua虛擬機中加載該函數(LuaState.DoFile)。

(2)拿到目標函數(LuaState.GetFunction)。  

(3)執行目標函數(LuaFunction.Call)。    

相關文章
相關標籤/搜索