Unity 項目中怎樣正確的使用 Lua?
在 Unity 中, 我們通常寫的 C# 已經就算是 script 了, 所以我覺得使用 Lua 的唯二理由就是一為了可以熱更新, 二是一些 Lua 寫的邏輯可能伺服器端也要用到. 不知道我理解的是否正確?
那麼如果決定使用 Lua 的話, 是不是意味著是將原來所有要用 C# 寫的邏輯全改成 Lua 的? 如果是的話豈不是需要將所有的 C# API 全做一份 Lua 綁定的? 這樣工程量似乎很大啊. 而且我搜索到的常用的 Lua 實現(比如 UniLua) 只是做了 Lua 的 C# 實現. (也可能是作者沒有將那部分開源出來?)所以現在我不知道大家都是怎樣在 Unity 中使用 Lua. 請哥哥教我
來使用哥開發的slua吧, http://www.slua.net
開源, 和ulua相比快的沒有朋友, 沒有反射,沒有額外gc alloc,採用靜態代碼生成, 可以用於遊戲核心邏輯, 完整支持4.6+ UI系統.
同ulua/tolua的性能對比,請看這裡 slua benchmark
20萬次 測試用例 數據如下(slua0.6 vs cstolua1.7, slua0.7再次飆升速度)
slua 0.6 對比 slua 0.7主要功能如下:
- 速度就是快, 這是slua的核心目標
- 避免額外gc alloc, 去掉性能殺手
- 90%以上UnityEngine介面導出(主要去掉了flash,平台相關的介面);
- 100% UnityEngine.UI介面導出 (4.6+ 版本)
- 支持UnityEvent/UnityAction, 使用lua function
- 支持delegation,使用lua function (支持iOS)
- 支持coroutine
- 支持導出自定義類
- 所有enum導出為number
- 所有數組返回值導出為lua table
- 使用luajit 64bit(完整支持armv7,armv7s,arm64), 可用lua5.3替換
- 支持il2cpp/il2cpp 64
因為項目需要,比較了下uLua和sLua
測試工程見 GitHub - qiankanglai/unity_lua_benchmark: sLua/uLua Performance Test
放幾張結果圖
ps. 下面的圖我為了美觀都是對數坐標...pss. uLua的工程目錄我無力吐槽………………過去半年時間,我們團隊完成了一個運營七年的端游項目的口袋版移植工作,還原了mmo的主要元素和玩法。前幾天剛剛上線。我們立項的時候就決定絕大多數代碼都使用lua完成。這個主要是為了配合端游的更新,我們做的是互通版,pc端和手機端共用同一個伺服器。而結果證明lua是可以承載一個mmo的運行效率需求的。我們做的時候有這麼一個劃分,凡是效率相關的(比如地圖排序,尋路),平滑顯示相關的(比如人物移動,頭頂血條位置)都用c#寫。凡是涉及到伺服器通信,邏輯狀態的,都在lua寫。後面我們為了趕進度,戰鬥部分並沒有重新設計,也是直接lua移植,這個其實會影響運行效率,不過還在可以接受的範圍之內,反正我們追求的也並不是最頂級的效率,很多東西都可以用設計而不是代碼來優化。我們使用的是tolua(原ulua)。全局一個luastate。由一個管理類維護。整個遊戲的狀態由lua來維護。每個可顯示對象對應一個actor,這個是一個c#對象,由lua中的邏輯對象(如player monster)持有並維護。
從現在開發使用的情況來看,c#的開發效率其實是要比lua高的,藉助於編譯檢查和代碼補全,寫起來非常爽(其實就是vs用著爽)。不過作為一個重度遊戲(或者說包體積大於300兆的遊戲)更新整包成本其實是非常大的,你可以一個月更新一個版本,但是不能一個月讓玩家下一個版本。因為一些bug引發了內存泄露,崩潰,嚴重邏輯問題這些都是非常蛋疼的,而且絕不是簡單的一句之前多測試就可以解決的。從這點來說lua的優勢就非常大了。我之前認為遊戲不能屈從於熱更新,但是現在看來熱更新就是大於天的需求,越是依賴於用戶粘性的遊戲其需求就越大。
Unity3D中使用Lua之UniLua
方便動態更新遊戲用。
開源項目地址:
xebecnan/UniLua最新支持到Lua5.2,C#版的Lua
基礎用法:
大部分的使用是可以參考標準的 Lua 官方文檔和 Lua 教程的。 Lua 本身的語法是一樣的。C API 和 C# API 之間有個對應關係。例如 lua_pushnumber() 這個 C API 對應到 UniLua 里就是 lua.PushNumber()
所有標準 lua 中 lua.h 和 lauxlib.h 里定義的介面,都對應 LuaAPI.cs 里定義的 ILuaAPI 和
LuaAuxLib.cs 里定義的 ILuaAuxLib 介面。xebecnan/UniLua從 C# 調用 Lua最樸素的從 C# 調用 lua 的一個全局函數的寫法:
Lua.GetGlobal( "foo" ); // 載入 lua 中定義的一個名叫 foo 的全局函數到堆棧
Debug.Assert( Lua.IsFunction(-1) ); // 確保載入成功了, 此時棧頂是函數 foo
Lua.PushString( "test" ); // 將第一個參數(字元串 "test")入棧
Lua.PushInteger( 42 ); //將第二個參數(整數 42)入棧
Lua.Call(2, 0); // 調用函數 foo, 指明有2個參數,沒有返回值
// 上面的代碼相當於 lua 里一個這樣的調用 foo("test", 42)
稍微複雜一點的例子可以參考實常式序里的一些簡單寫法:參考這個文件 Assets/Behaviour/LuaScriptController.cs:
Assets/Behaviour/LuaScriptController.csframework/main.lua// 創建 Lua 虛擬機
var Lua = LuaAPI.NewState();
// 載入基本庫
Lua.L_OpenLibs();
// 載入 Lua 腳本文件
var LuaScriptFile = "framework/main.lua";
var status = Lua.L_DoFile( LuaScriptFile );
// 捕獲錯誤
if( status != ThreadStatus.LUA_OK )
{
throw new Exception( Lua.ToString(-1) );
}
// 確保 framework/main.lua 執行結果是一個 Lua Table
if( ! Lua.IsTable(-1) )
{
throw new Exception(
"framework main"s return value is not a table" );
}
// 從 framework/main.lua 返回的 table 中讀取 awake 欄位指向的函數
// 並保存到 AwakeRef 中 (可以將 AwakeRef 視為這個函數的句柄)
var AwakeRef = StoreMethod( "awake" );
// 不再需要 framework/main.lua 返回的 table 了,將其從棧上彈出
Lua.Pop(1);
//----------------------------------------------------
// 在需要的時候可以這樣調用 AwakeRef 指向的 lua 函數
CallMethod( AwakeRef );
//----------------------------------------------------
// StoreMethod 和 CallMethod 的實現
private int StoreMethod( string name )
{
Lua.GetField( -1, name );
if( !Lua.IsFunction( -1 ) )
{
throw new Exception( string.Format(
"method {0} not found!", name ) );
}
return Lua.L_Ref( LuaDef.LUA_REGISTRYINDEX );
}
private void CallMethod( int funcRef )
{
Lua.RawGetI( LuaDef.LUA_REGISTRYINDEX, funcRef );
var status = Lua.PCall( 0, 0, 0 );
if( status != ThreadStatus.LUA_OK )
{
Debug.LogError( Lua.ToString(-1) );
}
}
xebecnan/UniLua從 Lua 調用 C# 函數 ( 使用 C# 來擴展 Lua 功能 )
目前的示常式序是使用 FFI 庫來實現的 從 Lua 調用 C# 函數。 FFI 因為用到了反射機制來調用 C# 函數,性能會比較低。應該盡量避免使用,如果沒有找到更好的辦法,準備之後把這個FFI實現廢棄掉。其實直接用 C# 實現一個庫的形式,來讓 lua 調用這種傳統的做法效率會比較高,也是推薦採用的方式。而且也並不會麻煩太多。
比如我現在要實現一個叫 libfoo 的庫, 裡面提供兩個方法: add(a, b) 和 sub(a, b)
庫的實現
using UniLua;
public static class LibFoo
{
public const string LIB_NAME = "libfoo.cs"; // 庫的名稱, 可以是任意字元串
public static int OpenLib(ILuaState lua) // 庫的初始化函數
{
var define = new NameFuncPair[]
{
new NameFuncPair("add", Add),
new NameFuncPair("sub", Sub),
};
lua.L_NewLib(define);
return 1;
}
public static int Add(ILuaState lua)
{
var a = lua.L_CheckNumber( 1 ); // 第一個參數
var b = lua.L_CheckNumber( 2 ); // 第二個參數
var c = a + b; // 執行加法操作
lua.PushNumber( c ); // 將返回值入棧
return 1; // 有一個返回值
}
public static int Sub(ILuaState lua)
{
var a = lua.L_CheckNumber( 1 ); // 第一個參數
var b = lua.L_CheckNumber( 2 ); // 第二個參數
var c = a - b; // 執行減法操作
lua.PushNumber( c ); // 將返回值入棧
return 1; // 有一個返回值
}
}
庫的初始化
// 創建 Lua 虛擬機
var Lua = LuaAPI.NewState();
// 載入基本庫
Lua.L_OpenLibs();
Lua.L_RequireF( LibFoo.LIB_NAME // 庫的名字
, LibFoo.OpenLib // 庫的初始化函數
, false // 不默認放到全局命名空間 (在需要的地方用require獲取)
);
庫的使用 (在 lua 代碼中)
// 獲取庫
local libfoo = require "libfoo.cs"
// 調用庫的方法
print(libfoo.add(42, 1))
print(libfoo.sub(42, 22))
xebecnan/UniLuaUTF-8 support
C# 採用 UTF-16 作為字元串的內部編碼,而 Lua 本身沒有實現比較完善的編碼支持。為了處理這個問題,我實現了一個簡單的編碼庫 enc lib。使用方法如下:
-- Assuming your source code is in utf-8.
-- convert from utf-8:
local utf8_str = "測試字元串"
local print_safe_str = enc.decode(utf8_str, "utf8")
print(print_safe_str)
-- convert to utf-8:
local original_str = enc.encode(print_safe_str, "utf8")
assert(utf8_str == original_str)
更新:頂樓上,早已轉入slua了(http://www.slua.net)
__________________________________________________________
參考:uLua最新的Unity+Lua熱更新解決方案!!!【技術綜合貼】呵呵,看了下來感覺untiy裡面根本沒有用lua的必要啊
最近剛開始使用Unity引擎,集成Lua的部分寫了一篇總結,供參考。Unity手游開發札記——Lua語言集成 - Funny David的文章 - 知乎專欄基本的結論是:1. 做商業遊戲在Unity中集成Lua語言是一件不太能避免的事情,只是Lua語言應用的範圍是怎樣的要仔細確定;2. 個人在安卓設備上測試的結論和前面錢康來的基本一致,ToLua#在介面調用方面比SLua的稍微好一點,猜測的原因之一是由於SLua在返回值中添加一個bool值來標識函數調用的成功與否;3. ToLua#和SLua之外都有對應的框架,個人感覺KSFramwork這套框架實現的部分功能不錯,單是感覺ToLua#性能稍好,因此在自己的項目中做了一個「」雜交「」。。。
這塊變化較大,目前(2016年)主要有兩個主要的備選方案:slua和tolua。
在Unity中使用Lua腳本:語言層和遊戲邏輯粘合層處理
Unity熱更新方案(uLua vs sLua)GitHub - qiankanglai/unity_lua_benchmark: Benchmark for Lua in Unity只用來做了配置解析…… C#下的Lua效率很低…大概是C的1/100…不適合做主邏輯
表示你們的工具不如鵝廠內部工具給力啊,還需要努力
c#light ,你值得擁有...不錯 就是為了熱更新 我在自己的項目里花幾天就移植了部分功能過去 關鍵是一有不妥 都是c#代碼也容易轉換回來 目前來看除了有些特性不支持,沒遇到什麼大坑 不足之處是效率不是很讓人滿意 與非腳本的溝通也有點生硬
推薦閱讀:
※如何零基礎系統學習3D遊戲開發?
※在Unity中製作動畫,如何兼顧動畫和物理效果將人物頭髮處理到最佳效果?
※為什麼知乎很多人對我國遊戲行業的前景都非常不看好?
※在當前網路環境下(2016年),手游使用長連接(TCP)好還是短連接(HTTP)好?