[Unity3D]Unity3D游戏开发Lua随着游戏的债券(于)
? ? ?
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
喜歡我的博客請記住我的名字:秦元培。我的博客地址是blog.csdn.net/qinyuanpei。
轉載請注明出處,本文作者:秦元培, 本文出處:http://blog.csdn.net/qinyuanpei/article/details/39910099
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
? ? ? ?大家好,我是秦元培,歡迎大家關注我的博客。我的博客地址是blog.csdn.net/qinyuanpei。在前一篇文章《Unity3D游戲開發(fā)之Lua與游戲的不解
之緣(上)》中,博主帶領大家初步探索了Lua語言與游戲開發(fā)領域之間的緊密聯(lián)系,今天讓我們來繼續(xù)將Lua語言進行究竟吧!通過前面的學習,我們知道設計Lua語言的目的是為了將Lua嵌入應用程序中,從而為應用程序提供靈活的擴展和定制功能。Lua語言本身沒有像其他語言提供豐富的類庫,因此Lua語言必須依賴于其他語言來完畢功能上的擴展(但是正是在功能上犧牲才換來了Lua精簡而穩(wěn)定的核心)。假設我們要深入了解Lua語言的話,就必須要了解Lua語言與其他語言的交互接口,由于這將是我們使用Lua語言的基礎。那么。今天就讓博主來帶領大家一起學習Lua語言與其他語言的交互吧!
? ??? 一、Lua堆棧
? ??假設我們想要理解Lua語言與其他語言交互的實質,我們首先就要理解Lua堆棧。
簡單來說。Lua語言之所以能和C/C++進行交互。主要是由于存在這樣一個無處不在的虛擬棧。
棧的特點是先進后出,在Lua語言中,Lua堆棧是一種索引能夠是正數(shù)或者負數(shù)的結構,并規(guī)定正數(shù)1永遠表示棧底。負數(shù)-1永遠表示棧頂。
換句話說呢,在不知道棧大小的情況下。我們能夠通過索引-1取得棧底元素、通過索引1取得棧頂元素。以下呢,我們通過一個實例來加深我們對于這段話的理解:
#include <iostream>extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" }using namespace std;int main() {//創(chuàng)建Lua環(huán)境lua_State* L=lua_open();//打開Lua標準庫,經常使用的標準庫有l(wèi)uaopen_base、luaopen_package、luaopen_table、luaopen_io、//luaopen_os、luaopen_string、luaopen_math、luaopen_debugluaL_openlibs(L);//壓入一個數(shù)字20lua_pushnumber(L,20);//壓入一個數(shù)字15lua_pushnumber(L,15);//壓入一個字符串Lualua_pushstring(L,"Lua");//壓入一個字符串Clua_pushstring(L,"C");//獲取棧元素個數(shù)int n=lua_gettop(L);//遍歷棧中每一個元素for(int i=1;i<=n;i++){cout << lua_tostring(L ,i) << endl;}return 0; }
在上面的這段代碼中,我們能夠能夠看到我們首先創(chuàng)建了一個lua_State類型的變量L,我們能夠將它理解成一個Lua運行環(huán)境的上下文(Context),這里我們在Lua堆棧中壓入了四個元素:20、15、"Lua"、"C"然后將其輸出,假設大家理解了Lua堆棧中的索引,那么終于輸出的結果應該是:20、15、"Lua"、"C"。由于索引1始終指向棧底,最先入棧的元素會處于棧底。
因此當我們依照遞增的索引順序來輸出棧中的元素的話,實際上是自下而上輸出,這樣我們就能得到這種結果了。
? ? ? ?好了。假設這段代碼沒有什么問題的話。接下來我們來解說Lua為C/C++提供的接口,它們均被定義在lua.h文件里。Lua提供的C/C++接口大部分與棧操作有關,因此深入理解Lua堆棧是學習Lua語言的重點和難點。通過數(shù)據(jù)結構的知識,我們能夠知道棧有出棧和入棧兩種基本操作,Lua提供的C API中入棧能夠通過push系列的方法來實現(xiàn)。例如以下圖所看到的:
而出棧或者說查詢的方法則能夠通過to系列的方法來實現(xiàn),例如以下圖:
這兩部分是學習Lua語言一定要去了解的內容,由于以后假設須要我們將Lua整合到其他項目中這些內容,這些東西能夠說是原理性、核心性的東西。
好了,以下我們利用這里的API對一個演示樣例代碼進行改造,這里添加了對棧中元素類型的推斷:
#include <iostream>extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" }using namespace std;int main() {//創(chuàng)建Lua環(huán)境lua_State* L=lua_open();//打開Lua標準庫,經常使用的標準庫有l(wèi)uaopen_base、luaopen_package、luaopen_table、luaopen_io、//luaopen_os、luaopen_string、luaopen_math、luaopen_debugluaL_openlibs(L);//壓入一個數(shù)字20lua_pushnumber(L,20);//壓入一個字符串15lua_pushnumber(L,15);//壓入一個字符串Lualua_pushstring(L,"Lua");//壓入一個字符串Clua_pushstring(L,"C");//獲取棧中元素個數(shù)int n=lua_gettop(L);//遍歷棧中每一個元素for(int i=1;i<=n;i++){//類型推斷switch(lua_type(L,i)){case LUA_TSTRING:cout << "This value's type is string" << endl;break;case LUA_TNUMBER:cout << "This value's type is number" << endl;break;}//輸出值cout << lua_tostring(L ,i) << endl;}//釋放Lualua_close(L); }
? ? 二、Lua與C++交互
? ?Lua與C++的交互從宿主語言的選擇劃分上能夠分為C++調用Lua和Lua調用C++兩中類型:
? ?1、C++調用Lua
? ? 使用C++調用Lua時我們能夠直接利用C++中的Lua環(huán)境來直接Lua腳本,比如我們在外部定義了一個lua腳本文件。我們如今須要使用C++來訪問這個腳本該怎么做呢?在這里我們能夠使用luaL_loadfile()、luaL_dofile()這兩個方法個方法來實現(xiàn)。其差別是前者僅載入腳本文件而后者會在載入的同一時候調用腳本文件。我們一起來看以下的代碼:
#include <iostream>using namespace std;#include <iostream>extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" }using namespace std;int main() {//創(chuàng)建Lua環(huán)境lua_State* L=luaL_newstate();//打開Lua標準庫,經常使用的標準庫有l(wèi)uaopen_base、luaopen_package、luaopen_table、luaopen_io、//luaopen_os、luaopen_string、luaopen_math、luaopen_debugluaL_openlibs(L);//以下的代碼能夠用luaL_dofile()來取代//載入Lua腳本luaL_loadfile(L,"script.lua");//運行Lua腳本lua_pcall(L,0,0,0);//將變量arg1壓入棧頂lua_getglobal(L,"arg1");//將變量arg2壓入棧頂lua_getglobal(L,"arg2");//讀取arg1、arg2的值int arg1=lua_tonumber(L,-1);int arg2=lua_tonumber(L,-2);//輸出Lua腳本中的兩個變量cout <<"arg1="<<arg1<<endl;cout <<"arg2="<<arg2<<endl;//將函數(shù)printf壓入棧頂lua_getglobal(L,"printf");//調用printf()方法lua_pcall(L,0,0,0);//將函數(shù)sum壓入棧頂lua_getglobal(L,"sum");//傳入?yún)?shù)lua_pushinteger(L,15);lua_pushinteger(L,25);//調用printf()方法lua_pcall(L,2,1,0);//這里有2個參數(shù)、1個返回值//輸出求和結果cout <<"sum="<<lua_tonumber(L,-1)<<endl;//將表table壓入棧頂lua_getglobal(L,"table");//獲取表lua_gettable(L,-1);//輸出表中第一個元素cout <<"table.a="<<lua_tonumber(L,-2)<<endl;}在這段代碼中我們調用了一個外部的文件script.lua。這是一個Lua腳本文件,在調試階段,我們須要將其放置在和C++項目源文件同級的文件夾下,而在正式運行階段,我們僅僅須要將其和終于的可運行文件放在同一個文件夾下就好了。以下是腳本代碼:
--在Lua中定義兩個變量 arg1=15 arg2=20--在Lua中定義一個表 table= {a=25,b=30 }--在Lua中定義一個求和的方法 function sum(a,b)return a+b end--在Lua中定義一個輸出的方法 function printf()print("This is a function declared in Lua") end 我們注意到在腳本文件里我們定義了一些變量和方法,在C++代碼中我們首先用lua_getglobal()方法來講Lua腳本中的變量或函數(shù)壓入棧頂,這樣我們就能夠使用相關的to系列方法去獲取它們,由于每次運行lua_getglobal()都是在棧頂。由于我們使用索引值-1來獲取棧頂?shù)脑亍++能夠調用Lua中的方法,第一步和普通的變量同樣。是將Lua中定義的方法壓入棧頂。由于僅僅有壓入棧中。我們才干夠使用這種方法,接下來,我們須要通過push系列的方法為棧中的方法傳入?yún)?shù),在完畢參數(shù)傳入后,我們能夠使用一個lua_pcall()的方法來運行棧中的方法,它有四個參數(shù)。第一個參數(shù)是Lua環(huán)境狀態(tài)Lua_State。第二個參數(shù)是要傳入的參數(shù)個數(shù),第三個參數(shù)是要返回的值的數(shù)目。第四個參數(shù)一般默認為0。
由于Lua支持返回多個結果。因此,我們能夠充分利用Lua的這一特點來返回多個值。
運行該方法后。其結果會被壓入棧頂,所以我們能夠索引值-1來獲取函數(shù)的結果。假設函數(shù)有多個返回值。則依照函數(shù)中定義的return 順序,依次入棧,索引值-1代表最后一個返回值。好了。這就是C++調用Lua的詳細實現(xiàn)了。
? ? ?2、Lua調用C++
? ? ?首先我們在C++中定義一個方法,該方法必須以Lua_State作為參數(shù),返回值類型為int,表示要返回的值的數(shù)目。
static int AverageAndSum(lua_State *L) {//返回棧中元素的個數(shù)int n = lua_gettop(L);//存儲各元素之和double sum = 0;for (int i = 1; i <= n; i++){//參數(shù)類型處理if (!lua_isnumber(L, i)){//傳入錯誤信息lua_pushstring(L, "Incorrect argument to 'average'");lua_error(L);}sum += lua_tonumber(L, i);}//傳入平均值lua_pushnumber(L, sum / n);//傳入和lua_pushnumber(L, sum);//返回值的個數(shù),這里為2return 2; } 接下來我們在C++中使用lua_register()方法完畢對該方法的注冊
lua_register(L, "AverageAndSum", AverageAndSum);這樣我們就能夠在Lua環(huán)境中使用這種方法啦,前提是定義必須在運行代碼之前完畢。我們在Lua腳本文件下添加對該方法的調用:
--在Lua中調用C++中定義并且注冊的方法 average,sum=AverageAndSum(20,52,75,14) print("Average=".average) print("Sum=".sum) 假設我們須要在C++中查看該方法調用的結果。那么這個在C++中調用Lua是一樣的。
好了,C++和Lua的交互終于講完了。被這塊的代碼糾結了好幾天,這下總算是搞明確了。當然這僅僅是對原理的一種學習和理解啦。假設希望更好的使用Lua調用C++,建議了解這幾個項目:
LuaPlus、LuaBind。這樣相信大家對于C++中的方法如何在Lua中綁定會有更好的認識吧!
? ? 三、Lua與C#交互
? ?既然我們已經知道了C++是如何和Lua完畢交互的,理論上我們能夠通過編寫dll的方式將前面完畢的工作繼續(xù)在C#中運行,但是這樣做我們須要花費大量時間在三種語言之間糾結,由于這樣會添加調試的難度。之前有個做coco2dx的朋友抱怨要在C++、Javascript、Lua之間來回跑,我當時沒認為有什么,由于我最困難的時候就是C#和Java項目混合的情形,如今我算是深有體會了啊。這算是報應嗎?哈哈。好了。不說這個了,好在C#與Lua的交互目方面前已經有了較好的解決方式。在開源社區(qū)我們能夠找到非常多的支持在C#中調用Lua的工具庫,博主這里向大家推薦的是LuaInterface這個開源項目。這個開源項目我找到了兩個地址:
1、https://github.com/Jakosa/LuaInterface
2、http://code.google.com/p/luainterface
博主個人感覺這應該是同一個項目,由于兩個項目的源碼是一樣的,只是從Github上下載的項目在使用的時候會報錯。預計是我電腦里的Lua版本號和它項目里所用的Lua的版本號不一致造成的吧。以下的這個項目是能夠使用的,博主這里寫了一個簡單的演示樣例:
//------------------------------------------------------------------------------ // <summary> // 這是一個用以演示LuaInterface的簡單程序,通過LuaInterface我們能夠實如今C#與Lua的 // 的相互通信。Lua是一個輕巧而高效的語言。它能夠和不論什么語言混合使用。Lua語言最初并非 // 為游戲開發(fā)而誕生,卻是由于游戲開發(fā)而成名。
眼下。在世界上有大量的游戲使用了Lua作為它 // 的腳本語言。如圖Unity使用了C#作為它的語言。Lua在游戲開發(fā)領域發(fā)揮著不可忽視的重要作 // 用。
使用LuaInterface的方法例如以下: // 1.C# // 注冊Lua中可調用方法: // mLua.RegisterFunction(Lua調用方法名, 類, 類.GetMethod(C#方法名)); // 注:C#不要用法級泛型。即 void Fun<T>(string str);,假設使用,系統(tǒng)自己主動判定T為第一個參數(shù)的類型。
// 載入Lua代碼 // mLua.DoString(Lua代碼); // mLua.DoFile(Lua文件絕對路徑); // 調用Lua方法 // mLua.GetFunction(Lua方法).Call(參數(shù)); 注:此處參數(shù)不要傳遞dynamic類型的類。否則Lua中無法獲取屬性值 // 2.Lua // 調用C#方法時須要先注冊注冊后依照Lua方法處理 // </summary> //------------------------------------------------------------------------------ using System; using LuaInterface; namespace LuaExample { public class LuaScript { //定義LuaFile屬性以便于從外部調用一個Lua腳本 private string mLuaFile; public string LuaFile { get { return mLuaFile; } set { mLuaFile = value; } } //Lua虛擬機 private Lua mLua; //構造函數(shù) public LuaScript () { //初始化Lua虛擬機 mLua=new Lua(); //注冊Printf方法 mLua.RegisterFunction("Printf",this,this.GetType().GetMethod("Printf")); } //定義一個C#方法供Lua使用 public void Printf(string str) { Console.WriteLine("This Method is Invoked by Lua:" + str); } //在C#中調用Lua方法 public void DoFile() { if(mLuaFile!="") //運行Lua腳本中的代碼 mLua.DoFile(mLuaFile); } //在C#中調用Lau方法 public void DoString() { //以字符串形式定義的Lua腳本 string mFuncString="function Add(a,b) io.write(a+b) end"; //在Lua中定義該方法 mLua.DoString(mFuncString); //調用該方法 mLua.GetFunction("Add").Call(4,8); } //在Lua中調用C#腳本 public void Invoke() { //調用注冊的Printf方法 mLua.GetFunction("Printf").Call("Hello Lua"); } } }
接下來我們編寫一個主類來調用這個類:using System; using LuaInterface;namespace LuaExample {class MainClass{public static void Main (string[] args){//實例化LuaSxriptLuaScript mLua=new LuaScript();//設置LuaFilemLua.LuaFile="D:\\test.lua";//調用字符串中定義的Lua方法mLua.DoString();//為美觀考慮添加一個空行Console.WriteLine();//運行Lua文件里定義的腳本mLua.DoFile();//調用C#中定義的方法mLua.Invoke();}} } 好了。C#與Lua的交互攻克了,很多其他的內容期待著大家自行到該項目源碼中去尋找。好了,先這樣吧!
? ? 四、Lua與Java交互
? ? 和C#相似的一點是在Java中我們能夠使用JNI來調用C++代碼。因此理論上Lua和Java應該是能夠通過JNI來交互的。這塊博主眼下沒有展開研究。這里僅僅給大家推薦以下工具庫:
1、LuaJava
2、luaj
? ? 五、結語
? ? 好吧。好了。好幾天的時間來研究Lua語言的API,總算感覺是收獲多一點吧。由于C++方面研究的東西不是非常多,所以像編譯C++項目、配置C++環(huán)境、引用C++庫和頭文件這些問題曾經都不大會,這次居然一下子都學會了,博主推薦大家使用CodeBlocks這個C/C++開發(fā)環(huán)境,它內置的gcc編譯器我認為還不錯啦,并且它跨平臺啊,以后工作了說不定會在Linux和Mac下做開發(fā),選擇一個跨平臺的編輯器或者是IDE,對于我們來說未嘗不是一件好事啊。由于學習新東西總是要花一定成本的。好了,今天的內容就是這樣啦,希望大家喜歡啊,嘻嘻。突然認為這篇文章好長啊。
每日箴言:別總由于遷就別人就委屈自己。這個世界沒幾個人值得你總彎腰。彎腰的時間久了。僅僅會讓人習慣于你的低姿態(tài)。你的不重要。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
喜歡我的博客請記住我的名字:秦元培。我的博客地址是blog.csdn.net/qinyuanpei。
轉載請注明出處,本文作者:秦元培, 本文出處:http://blog.csdn.net/qinyuanpei/article/details/39910099
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
版權聲明:本文博主原創(chuàng)文章。博客,未經同意不得轉載。轉載請注明作者和出處,謝謝。
轉載于:https://www.cnblogs.com/yxwkf/p/4843763.html
總結
以上是生活随笔為你收集整理的[Unity3D]Unity3D游戏开发Lua随着游戏的债券(于)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 姓夏的网名80个
- 下一篇: epoll与fork