[Unity3D]Unity3D遊戲開發之Lua與遊戲的不解之緣(下),unity3dlua
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
喜歡我的部落格請記住我的名字:秦元培,我的部落格地址是blog.csdn.net/qinyuanpei。
轉載請註明出處,本文作者:秦元培, 本文出處:http://blog.csdn.net/qinyuanpei/article/details/40050225
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
各位朋友,大家好,我是秦元培,歡迎大家關注我的部落格,我的部落格地址是blog.csdn.net/qinyuanpei。在前兩篇文章中,博主和大家一起學習了Lua在遊戲開發領域中應用,今天我們繼續來學習Lua語言在遊戲開發中的應用,今天我們將視角轉換到我們熟悉的Unity平台上來,那麼我們為什麼要將Lua語言引入Unity平台呢?這是我們今天要思考的第一個問題。傳統的單機遊戲通常以遊戲光碟的形式出售給遊戲玩家,玩家在購買遊戲後無法獲得更多的遊戲內容,玩家只能在一張容量有限的遊戲光碟裡不斷地重複尋找著遊戲的樂趣。毋庸置疑,這種模式不利於遊戲開發方為遊戲增加新的內容。可是在互連網技術逐步成熟的今天,玩家在購買一款實體遊戲後,通常可以通過購買DLC來體驗更加豐富的遊戲內容,而遊戲製作方則可以通過DLC向玩家傳達遊戲正傳中沒有表達出來的內容。我們知道DLC是通常是指遊戲的資料片,它是對遊戲內容的一種補充。從技術層面上來講,如果我們採用編譯型的語言來做一款遊戲,那麼我們根本無法實現對遊戲內容的擴充,因為我們需要對整個項目進行重新編譯然後打包成遊戲光碟再出售給玩家。這樣無疑會增加遊戲製作方的製作成本,而更重要的是玩家不會為了新的遊戲內容而再次購買遊戲,顯然這種方式是不合理的。那麼,此時像Lua這樣的指令碼語言就可以發揮出巨大的作用,因為指令碼語言通常不會佔用太多資源,或許我們只需要一個遊戲指令碼就可以利用遊戲中現有的情境和人物開闢出新的遊戲劇情。所以,經過一番分析,大家可以總結出指令碼語言在遊戲開發中一個重要的作用就是更新。因為指令碼語言通常都是純文字檔案,我們只需要改變某些參數而不必重新編譯整個項目,這正是我們希望看到的。
好了,下面我們就來一起學習在Unity3D項目中如何使用Lua語言吧,Unity3D基於Mono虛擬機器,所以理論上.NET的類庫是可以直接在Unity3D中使用的。可是考慮到Unity3D跨平台的需要,我們選擇的工具必須在各個平台獲得良好的支援。在前文中提到的LuaInterface理論上是可以在Unity3D中使用的,可是由於IOS不支援反射機制,所以這個類庫我們無法直接在Unity3D中使用的。在開源社區中,博主發現了雲風團隊的阿楠開發的UniLua,雲風團隊的風雲是一個在國內遊戲開發領域比較著名的人物,那麼我們今天就來選擇UniLua來作為我們的一個工具庫吧,該項目是一個開源項目,參考了LuaInterface項目,不過在處理反射這個問題上使用了新的方法,所以目前可以完美地支援各個平台。相信大家有了前面兩篇文章的基礎,現在已經可以從容地面對Lua API了吧。好了,我們,現在來建立一個簡單地Unity項目:
第一步是下載UniLua:http://github.com/xebecnan/UniLua。將UniLua引用到項目中有兩種方法,一種是將該項目中的UniLua編譯成dll然後在Unity項目中使用,一種是將該項目中的UniLua直接複製到Unity 項目中,我們這裡使用第二種方法,因為博主比較懶,呵呵。將UniLua的命名空間添加到我們項目中,我們就可以開始動手寫程式了。不過這裡,博主想說的是Mono可能會導致的一個錯誤,估計是阿楠在寫這個項目的時候使用了.NET4.0以上的版本,而在.NET4.0以上的版本是支援預設參數的建構函式的。可是由於Mono預設使用的是.NET3.5,所以在編譯項目的時候就會報錯,我們可以通過Project->Assembly-CSharp->Build->General將.NET的目標框架設為4.0,這樣就可以解決這個問題了。好了,下面我們開始寫代碼啦,首先建立一個InvokeScript.cs的指令碼:
using UnityEngine;using System.Collections;using UniLua;public class InvokeScript : MonoBehaviour {//Lua指令檔,我們將在C#調用該指令碼public TextAsset LuaFile;//Lua虛擬機器private ILuaState mLua;void Start(){//初始化Lua虛擬機器mLua=LuaAPI.NewState();//載入Lua標準庫mLua.L_OpenLibs();//引用一個靜態地C#庫mLua.L_RequireF(CSharpLib.CLASSNAME,CSharpLib.InitLib,false); //執行Lua指令碼mLua.L_DoString(LuaFile.text);}void OnGUI(){if(GUILayout.Button("調用Lua指令碼",GUILayout.Height(30))){InvokeLua();}if(GUILayout.Button("調用C#指令碼",GUILayout.Height(30))){InvokeCSharp();}}#region 調用C#指令碼void InvokeCSharp(){//擷取方法並傳入參數mLua.GetGlobal("SumAndSub");mLua.PushInteger(12);mLua.PushInteger(8);mLua.PCall(2,4,0);}#endregion#region 調用Lua指令碼void InvokeLua(){//擷取Lua指令碼中的arg1參數mLua.GetGlobal("arg1");//輸出arg1Debug.Log("Lua指令碼中的變數arg1="+mLua.L_ToString(-1));//擷取Lua指令碼中的arg2參數mLua.GetGlobal("arg2");//輸出arg2Debug.Log("Lua指令碼中的變數arg2="+mLua.L_ToString(-1));//擷取Lua指令碼中的Printf方法mLua.GetGlobal("Printf");//調用Lua指令碼中的Printf方法mLua.PCall(0,0,0);//擷取Lua指令碼中的Sum方法mLua.GetGlobal("Sum");//傳入參數12和25mLua.PushInteger(12);mLua.PushInteger(25);//調用此方法mLua.PCall(2,3,0);//擷取傳入的兩個參數及求和結果int a=mLua.ToInteger(-3);int b=mLua.ToInteger(-2);int sum=mLua.ToInteger(-1);//輸出Debug.Log("調用Lua指令碼中的Sum方法:"+a+"+"+b+"="+sum);}#endregion}
在這段指令碼中,我們首先初始化了Lua環境,這一點和我們在C++中使用Lua是一樣的,因為UniLua在設計API的時候在命名上和LuaAPI保持了高度的一致,如果你對Lua API足夠熟悉的話,那麼現在這一切對你而言應該會很簡單的。接下來,我們通過Require的形式引入了我們編寫的一個C#庫,它是一個靜態庫,目的是封裝C#方法以便於Lua指令碼來調用,這一部分我們稍後會講到。接下來,我們通過Unity的AssetText載入了一個Lua指令檔,該指令碼的檔案的副檔名是.txt,因為我們只需要Lua指令碼的內容。在指令碼中我們定義了兩個方法InvokeLua和InvokeSharp來分別調用Lua指令碼和C#指令碼。好了,接下來,我們重點來講Lua調用C#指令碼的這部分,因為UniLua在調用函數這塊兒和LuaInterface不太一樣,所以我們不能再用原來的先註冊C#方法然後再像Lua指令碼方法一樣,不過博主覺得這裡的原理是一樣的,不過UniLua提供了更好的方法綁定機制,我們來看下面的指令碼:
using UnityEngine;using System.Collections;using UniLua;public static class CSharpLib{//當前類檔案名稱,我們將在Lua指令碼中使用這個名稱public const string CLASSNAME="CSharpLib.cs";//C#庫初始化public static int InitLib(ILuaState lua){NameFuncPair[] define=new NameFuncPair[]{new NameFuncPair("SumAndSub",SumAndSub),};lua.L_NewLib(define);return 1;}//我們在C#中定義一個求和差的方法public static int SumAndSub(ILuaState lua){//第一個參數int a=lua.L_CheckInteger(1);//第二個參數int b=lua.L_CheckInteger(2);//計算和int c=a+b;//計算差int d=a-b;//將參數及計算結果壓入棧lua.PushInteger(a);lua.PushInteger(b);lua.PushInteger(c);lua.PushInteger(d);//有四個傳回值, 儘管在C#中不支援返回多個值,可是在Lua中這樣是支援的return 4;}}
大家一定注意到這裡有個NameFuncPair類吧,這就是在UniLua中用來將一個C#方法和Lua方法進行綁定的方法,我們首先構造這樣一個NameFuncPair數組,然後將其加入到lua_L_NewLib()的參數中,這樣相當於是註冊了一個庫,我覺得應該就是註冊了一個方法集合吧.而CLASSNAME是一個表示當前類名稱的常量,可以取任一字元,這裡我們使用該類的檔案名稱我們將在Lua指令碼是用這個值來尋找當前類.接下來,我們可以看到博主構造了一個求和差的C#方法,這個方法和Lua API中定義的方法是一致的,即我們需要指定該方法會返回的值得數目.如果我們需要返回一個值,就要把它通過push系列的方法壓入棧中.這裡我們返回了四個值,大家一定會問好是C#還支援返回多個值啊,其實呢,這是Lua語言提供給我們的一個福利啊,比如我們需要返回一個物體在3D世界裡的座標,通常情況下,我們需要用三個指派陳述式才能擷取吧,可是你用Lua的話,一行代碼就可以搞定啦.好,現在我們回到InvokeScript指令碼的Start方法中,大家可以注意到這裡有一個L_RequireF()的方法,前面只是輕描淡寫地說它引入了一個庫,那麼現在我們看看它具體做了什麼吧,第一個參數表示這個類的名字,指向我們定義好的CLASSNAME,第二個參數是這個類的初始化方法指向InitLib()方法,第三個參數是是否要在全域空間中使用這個庫,這裡我們選在false.好了,這樣,我們就完成了C#指令碼的編寫.好了,下面我們在項目中建立一個純文字檔案,我們輸入如下代碼:
local csharplib=require"CSharpLib.cs"arg1="Unity3D"arg2="Unreal"arg3="Coco2dX"function Printf() print("This is the methods invoked in Lua")endfunction Sum(a,b) return a,b,a+bendfunction SumAndSub(a,b) print(csharplib.SumAndSub(a,b))end
第一行代碼同樣是一個require的方法,這是Lua指令碼中引用一個庫的方法,該方法可以引用Lua的標準庫,同樣可以引用我們定義的外部庫,大家注意到這裡的名字和我們之前定義的CLASSNAME是一樣的,因為我們就是通過這個名字來查詢這個庫的,我們在Lua環境中註冊了這個庫,所以現在才可以引用這個庫.在這段指令碼中我們定義了幾個字元型的變數,兩個Lua方法,一個用Lua封裝的C#方法.好了,現在我們將這個文字檔指定到InvokeScript的LuaFile欄位,我們通過LuaFille的text擷取指令碼內容,然後通過DoString()方法來執行指令碼中的內容,注意這裡要先對C#庫進行註冊,然後再執行指令碼中的內容,否則會出現錯誤.好了,最後,我們來一起看看運行效果吧:
大家可以看到C#調用的Lua指令碼中我們擷取了指令碼中的兩個變數arg1、arg2,調用了Lua中定義的兩個方法,而最後一個方法,如我們所願,它返回了四個值,這正是我們所希望的結果.這裡順便說一下啊,在Lua中的print方法和return在Call以後是可以直接在Debug中輸出結果的,無需我們再去做Log。好了,今天的內容就是這樣啦,希望大家喜歡啊,歡迎大家關注我的部落格,在下一篇文章中,博主將帶領大家繼續Lua到底,請關注博主的下一篇文章《Unity3D遊戲開發之Lua與遊戲的不解之緣終結篇:UniLua熱更新完全解讀》,謝謝大家.關於UniLua調用非靜態類和方法,大家可以參考這篇文章:http://www.cnblogs.com/cqgreen/p/3483026.html。
每日箴言:合適的人生位置,既不靠近錢,也不靠近權,而是靠近靈魂;真正的幸福,既不是富貴,也不是凡事都對,而是問心無愧。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
喜歡我的部落格請記住我的名字:秦元培,我的部落格地址是blog.csdn.net/qinyuanpei。
轉載請註明出處,本文作者:秦元培, 本文出處:http://blog.csdn.net/qinyuanpei/article/details/40050225
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Unity3D遊戲開發的執行個體,或原始碼 推薦書籍或則下載原始碼的地址
unity 3D遊戲開發這本書
[Unity3D]手機3D遊戲開發:怎使用Unity3D中內建的重力感應
汪 汪海的實驗室 海的實驗室- - 各種圖形學實驗和資料結構實驗以及其他一切瑣碎雜亂的小筆記們都相遇在此齊聚一堂共同守候 0 - - 各種圖形學實驗和資料結構實驗以及其他一切瑣碎雜亂的小筆記們都相遇在此齊聚一堂共同守候 0error(s), 0 warning(s) 這神奇時刻的到來 error(s), 0 warning(s) 這神奇時刻的到來學習Unity指令碼推薦:Unity3D官網索引重力感應在手機遊戲的開發中非常常見。Unity3D本身集合了重力感應的相關內容。一個簡單的JS指令碼示範一下重力感應的使用。CSDNGravity.js://物體的貼圖var round : Texture2D;//物體在螢幕中顯示的X Y座標var x = 0;var y = 0;//物體螢幕顯示的最大 X Y 範圍var cross_x = 0;var cross_y = 0;function Start(){//初始化賦值cross_x = Screen.width - round.width;cross_y = Screen.height - round.height;}function OnGUI () {//整體顯示 x y z 重力感應的重力分量GUI.Label(Rect(0,0,480,100),"position is " + Input.acceleration);//繪製物體GUI.DrawTexture(Rect(x,y,256,256),round);}function Update(){//根據重力分量修改物體的位置這裡乘以30的意思是讓物體移動的快一些x += Input.acceleration.x * 30;y += -Input.acceleration.y * 30;//避免物體超出螢幕if(x < 0){x = 0;}else if(x > cross_x){x = cross_x;}if(y < 0){y = 0;}else if(y > cross_y){y = cross_y;}}這裡的Input是指Unity中的輸入,acceleration便是其重力了,x和y分別代表其重力分量。建立完畢之後只需要添加紋理圖片即可:12