1. 程式人生 > >Cocos2d+Lua 遊戲開發中的奇技淫巧系列之一 Lua require的語法糖

Cocos2d+Lua 遊戲開發中的奇技淫巧系列之一 Lua require的語法糖

  媽蛋,感覺寫的部落格,又要爛尾了,我有罪,我的定力太差了,這就是我這麼聰明,這麼多年卻只能混成個窮逼的根本原因嗎?趕緊趁今天在公司暫時沒有事情做,先發一篇文章.....

---------------------------------------------------------------------------------------------------------------------------------

   作為一箇中級即將要成為高階的遊戲開發程式設計師,用Lua寫了一二年遊戲的你,require這個有什麼好提的,天天在用,使用中還有什麼技巧可言?

  這個require在使用過程中,真的還有一些技巧,當然,這個技巧並不是說在遊戲執行效率上有驚天的變化,而僅僅是一個語法糖,不用對遊戲效能也沒有半毛錢的影響,但是用了,可能對遊戲開發效率和閱讀效率都有些幫助。

    先分享一下require在Lua原始碼中的實現...嗯,算了,這一步跳過吧,require載入一個lua的過程很多部落格已經說了,我也說得並不比別人好,還是直接進入主題吧。

通常,很多遊戲的reuire是這樣子用的:比如,某遊戲的包背管理器的是在PackageMgr.lua裡面實現的,這個檔案在遊戲的Res\script\lua\mgr\PackageMgr.lua裡面。假如PackageMgr.lua裡面是這樣子寫的,即這個PackageMgr.lua是直接返回的一個單例。

local PackageMgr={}

--這裡面是遊戲邏輯

return PackageMgr

那麼,在其它地方引用這個單例時,最常見的一種程式碼的寫法是這樣子:

方法1

local packet = require("script.lua.mgr.PackageMgr")

將Res設定成lua的搜尋根路徑,在引用時,使用全路徑,這樣子的用法是比較經典的,它的問題唯一在:寫的程式碼太多了,你要引用別人寫的一個模組,還要知道它所在的路徑,假如這個路徑很長,碼起來很累的,copy也會出錯,而且加載出錯,要到執行時才能知道沒有這個路徑。

為了解決拼寫過長的問題,我們可以考慮另外一種解決方案:

方法2

我們將Res\script\lua\mgr\新增到遊戲的搜尋路徑,這樣,在使用時就可以

local packet = require("PackageMgr")

只需要一個PackageMgr模組名就能將它引用進來了,是不是省了很多字?嗯,這種寫法也有一個問題:就是假如遊戲的資料夾太多,這樣子就要新增很多的搜尋路徑,搜尋路徑太多的話,搜尋的效率自然會降低了,而且,太多的檔案路徑,怎麼新增?手工新增還是指令碼新增?新增的和刪除的資料夾怎麼維護也是個問題。

好了,我們既想獲得方法2的簡潔,又想獲得方法1 的高效率,自然的就出現了第三種方法

方法3

我們定義一個新的require,為了區別require,我們就叫做GameRequire吧。

function GameRequire(luaFileName)
    local fullLuaFilePath = FullPath[luaFileName]
    if fullLuaFilePath then
        return require(fullLuaFilePath)
    else
        return require(luaFileName)
    end
end

閱讀上面GameRequire的實現程式碼,首先,通過一個全域性的FullPath表,將luaFileName轉換成FullLuaFilePath,假如存在FullLuaFilePath,我們就用FullLuaFilePath 進行require,假如不存在,我們就用luaFileName進行require。這樣,我們在使用時只要輸入一個lua檔名,就能自動實現全路徑的搜尋載入啦。相信大家都已經看出這裡面實現檔名轉換成長路徑邏輯的關鍵是FullPath這個表了,這個表具體是長這樣子的:

FullPath ={

Package="script.lua.mgr.PackageMgr",
MainUI ="script.lua.ui.MainUI",
....


} 

可以看出這個FullPath是一個全域性的變量表,這個表裡面的內容非常簡單,就是將所有的lua檔案都轉換成了長路徑儲存,通常這個FullPath.lua是用指令碼來實現和維護的,哪個同學新增了一個lua檔案,就跑一下維護指令碼,更新一下這個FullPath.lua,這樣子大家都可以用GameRequire("檔名")就可以引用新增的模組了。

寫到這裡,相信很多寫了幾年lua的同學都想噴我了,什麼辣雞啊,這些東西也有什麼好寫的,不是很常見的嗎?沒有什麼好說的。好吧,好吧,很顯然,寫GameRequire還是太累了,雖然我們可以把GameRequire簡寫成GR之類的,但還要寫引號,括號。下面的方法就是解決這個懶人問題的奇淫技巧。這種方法是我自己原創出來的(雖然技術含量基本沒有,可能也有很多大佬不宵,不過我覺得還是很意思的)。

方法4

我們知道lua裡面有元表這個東西,元表裡面的__index方法相信很多同學都知道了,這裡面我們要用__index方法來實現lua檔案的載入,使得我們可以少寫點程式碼

local GameRequire= GameRequire
_GModel ={}
_GModelMeta={
__index =function(t,key)
    local modelFile = GameRequire(key)
    --t[key] = modelFile --這裡是開啟快取功能
    return modelFile

end

}

我們定義了一個全域性的變數_GModel,這個變數有一個元表_GModelMeta,裡面有一個元方法。使用時是這樣子使用的:

local packet = _GModel.PackageMgr

這樣子就實現require的呼叫了。原理聽我慢慢分析一下:

首先: _GModel.PackageMgr和 _GModel["PackageMgr"]是等價的對吧?(這是lua的語法,如果你不知道的,那麼這篇文章對你來說真的是白看了)。

其次:lua先在_GModel表裡面查詢“PackageMgr”成員,查不到,就在元表_GModelMeta裡面查詢,_GModelMeta裡面也找不到啊,沒關係,_GModelMeta有一個__index元方法,那就呼叫 __index方法來查詢吧,這樣子__index裡面的GameRequire函式就被呼叫 了,然後返回GameRequire的結果。上面__index函式被呼叫裡,裡面的引數t就是_GModel,key就是“PackageMgr”,為什麼這二個引數值是這樣子?你得回去查查元表的呼叫過程了。

上面的程式碼還有一句註釋掉了的,這個註釋掉的是一個快取程式碼,假如將它開啟,第一次呼叫時_GModel找不到成員,會觸發GameRequire,同時,將GameRequire的返回值存在了_GModel裡面了,第二次呼叫時,因為_GModel已經有模組的資料,將會直接返回,不再呼叫GameRequire,也就是也不會呼叫到require了,省了lua內部的查詢過程,在這個角度看,開了快取的_GModel的效率比require還要高一點。寫的程式碼比require少,效率還要比require高那麼一點點的方法,是不是個奇技淫巧?

好了,到這裡,我們就完成了require的奇技介紹了。如果你願意你可以將_GModel簡單寫成_M

local packet = _M.PackageMgr

這樣子,require是不是就可以碼很少字啦?哈哈,不過千萬不要將_GModel簡單寫成_G,因為_G是lua的保留變數。

相關推薦

Cocos2d+Lua 遊戲開發系列之一 Lua require語法

  媽蛋,感覺寫的部落格,又要爛尾了,我有罪,我的定力太差了,這就是我這麼聰明,這麼多年卻只能混成個窮逼的根本原因嗎?趕緊趁今天在公司暫時沒有事情做,先發一篇文章..... --------------------------------------------------

黑暗的生物:利用快活生存

not 正在 ever reading 自己的 which cat 世界 round 今日導讀 如果讓你在伸手不見五指的黑暗當中生存,你能熬過幾天呢?而大千世界,無奇不有。在很多你不知道的角落,有些生物在完全黑暗的世界裏不僅活得相當快活,還進化出了各種令人瞠目結舌的驚奇技能

C#開發一:調試windows系統服務

選項 nbsp 調試 方法 href info bubuko 系統 .html 原文:C#開發奇技淫巧一:調試windows系統服務windows系統服務不能直接運行,只能在安裝完服務之後啟動、暫停、繼續、停止服務,導致服務的調試不能使用一般的斷點調試。 要調試系統服務,

C#開發二:根據dll文件加載C++或者Delphi插件

man int cnblogs 需要 pat method msg initial 函數指針 原文:C#開發奇技淫巧二:根據dll文件加載C++或者Delphi插件 這兩天忙著把框架改為支持加載C++和Delphi的插件,來不及更新blog了。 原來的

——在textarea插入文字

本文首發我的簡書 今天在看自己的記錄時發現在幾個月前的一個專案中碰到一個這樣的需求:做一些簡單的計算按鈕,點選這些按鈕之後就在後面的輸入框中顯示對應的計算方法,對於一些函式類的計算方法需要將游標定位在括號裡面,效果如下圖: 當時也是第一次碰到這種需求,我搜集了網路

Java在進行網路傳輸的過程

今天,翻書的時候看到書上的網路傳輸資料的時候,頓時感覺之前的自己的操作太low了,這篇主要講,在網路傳輸的過程中,你傳送請求,但是你現在介面又需要根據返回的資料來更新介面,但資料什麼時候返回你又不知道,這時候該怎麼辦?之前我直接在主執行緒裡面一個死迴圈判斷接受資

攜程反爬的Eleven引數-反爬與反反爬的

http://www.shenjianshou.cn/blog/?p=302 今天我們要聊點什麼呢,之前說要聊去哪兒的,不過暫且咱們再放一放,先聊一聊去哪兒的乾爹攜程吧,上次我記得看了攜程工程師霸氣迴應說懂爬蟲的來去哪兒,懂反爬的來攜程。我覺得特別棒,這種開放的心態

C#開發三:把dll放在不同的目錄讓你的程式更整潔

http://www.tuicool.com/articles/mQVrUbJ 程式目錄的整理 想必C#的開發者都遇到過這個問題,引用的dll都放在根目錄下,隨著專案的日益增大,根目錄下充滿了各種各樣的dll,非常的不美觀。 如果能夠把dll按照想要的目錄來存放,

資料結構與演算法有那些

之前我也寫過一兩篇與演算法技巧相關的文章 一些常用的演算法技巧總結 【演算法技巧】位運算裝逼指南 今天的這篇文章,算是一種補充,同時會列舉一些常見的演算法題,如何用這些技巧來解決,通過使用這些方法,可以讓一些演算法題變的更加簡單。 1、用 n & (n - 1)消去 n 最後的一位 1 在 n 的二進

關於字符串和文件流的

童鞋 err pen spa 關於 文件大小 ood c_str 64bit 早早起來,發一篇來概括一下昨天晚上搞到深夜才學到的一些字符串和文件流的簡單操作。 (寫在前面:以下所有的文件流都以輸入文件流為例,另外這種文件讀寫方式與競賽時使用的有較大差別,NOIP黨裏C++剛

Mysql數據閃回的(binlog2sql)

mysql binlog2sql 一、概述binlog2sql是一個開源項目,應用於大眾點評線上環境。類似於ORACLE中的閃回功能,binlog2sql可以基於時間點或者位置偏移量進行數據恢復。從MySQL binlog解析出你要的SQL。根據不同選項,你可以得到原始SQL、回滾SQL、去除主鍵的I

CSS布局:各種居中

設置 ybds orm 必須 解決 html prim 分享 post CSS布局奇技淫巧:各種居中 2017-08-23 無雙 前端開發 作者:無雙 鏈接:www.cnblogs.com/2050/p/3392803.html 居中是我們使用css來布局時常

javascript 向下取整

mat asc floor math 轉化 java 目的 奇技淫巧 移位運算 let a=5/4|0 輸出a=1 利用了javascript在進行位運算前,先把浮點數轉化為整數的特性。 同理,也可以用移位運算,達到同樣目的 let a =5/4<

」博客園頁面美化(差不多是劃水

http 沒有 博客 -- erl text 頁面 ash spa 何為頁面美化?永遠沒有止境的亂搞也。 ——hzz 教材: 博客園美化

C的

如果 %s def 完全 逆序 函數 遞歸實現 -s r+ 直接上代碼: (1).交換兩參數的值 #include <stdio.h>int main(){    int a = 10,b = 5;    a^=b^=a^=b;  

CF1063A Oh Those Palindromes

傳送門(當然上洛咕能搜到) 求指定字串最多回文子串個數 Ssy太強了... 學到一個新東西 就是如果一個字元有x個 那麼它能產生的最多的迴文串個數為C(x,2) 可以理解為選擇兩個字母作為邊界 然後發現 如果所有一樣的字母全都拼在一起 那麼總個數就是C(x,2) 所以答案就是一樣的放一起就OK

java的--意外行為與特性(譯文)

Java是一種非常成熟的程式語言 - 事實上,它已經走過21年了,如果它是一個人,它可以在美國隨便混!隨著年齡的增長,智慧也在增長,而至少有時候,有些東西會變得很怪異。在本文中,我將介Java語言的一些奇技淫巧的行為和特徵。 在這裡,沒有特別的順序去介紹一系列Java的奇技淫巧,僅供娛樂,或者你向朋友們推介

Noip前的大抱佛腳----

STL函式 set set查詢前驅後繼 multiset<int>::iterator iter; S.insert(x); iter=S.find(x);//返回迭代器 iter--;//前驅 int ans=*iter; S.erase(find(x)); return ans; 或者

那些

1.交換兩個變數的值。 分析:常用的做法是新增第三個變數來作為中介完成,這裡分享一個不太常用的做法: 變數為字串時: $a = "hello"; $b = "php"; list($a,$b) = array($b,$a); 或者: $a = $a.$b; $b = strlen(

Puppet的一些

puppet這個工具真的很神奇,先不說商業版有哪些黑科技,單是開源版本就有很多可能讓你摸不著頭腦的地方,下面來列舉一下puppet是怎麼查詢puppet server的 其實很簡單,puppet agent只要知道puppet是什麼就可以了,你可以將“puppet”寫在