Unity3D熱更新之LuaFramework篇[02]--用Lua創建自己的面板


在上篇文章 Unity3D熱更新之LuaFramework篇[01]--從零開始 中,我們了解了怎么獲得一個可用的LuaFramework框架。

本篇將我會先介紹一下如何配置Lua開發環境,然后分析在此框架中加載面板的流程,以及如何創建自己的面板。

1、配置Lua開發環境

有一點要說明的是,使用此種方式(ToLua+LuaFramework)做熱更新,則意味着你的大部分邏輯都需要改用Lua語言來編寫。

因此,開發前得先得配置好Lua開發環境。畢竟,工欲善其事,必先利其器。

環境配置大概分以下三個步驟:

1.安裝IntelliJ IDEA Community Edition 2018.2.4 x64

官網地址 http://www.jetbrains.com/idea/download/#section=windows

直接下載即可,下載 Community 版本,也就是社區版,免費的

2.下載Lua For Windows

https://github.com/rjpcomputing/luaforwindows/releases

下載最新的就行,然后安裝。

3.安裝emmylua插件

安裝插件有2種方法,可以直接搜插件庫安裝,或者下載好插件后本地加載。

 

以上安裝步驟均來自:三頁菌 的文章 最好用的lua編輯器--------emmylua使用匯總

其文章極其詳細的介紹了如何搭建並配置一個好用的Lua開發環境,請自行參考。

 

2、Lua中是怎么加載一個面板的

在上一篇文章最后,我們運行框架,最終顯示了一個Lua腳本動態創建的面板,即PromptPanel,如圖2-1所示。

圖2-1

翻看框架的目錄結構,會在Assets/LuaFrame/Examples/Builds/Prompt目錄找到兩個預制體,PromptPanel和PromptItem,也就是這個面板的主體和獸人頭像,如圖2-2所示。

圖2-2

用上一節中安裝的IntelliJ IDEA打開工程目錄,在Controller目錄和View目錄會找到與PromptPanel密切相關的兩個文件PromptCtrl.lua、PromptPanel.lua,如圖2-3所示

圖2-3

由目錄名稱可知,此框架采用了一種MVC結構,用以對代碼功能做區分。XxxPanel負責頁面顯示邏輯,XxxCtrl負責事件處理,示例沒有給出明顯的Model層,讀者可以根據自身項目酌情添加。

繼續查看框架代碼,會在Logic/Game.lua中找到游戲的入口:Game.OnInitOK函數,見圖2-4。

圖2-4

 在這個函數中,有3個重要邏輯:

1、初始化View

2、初始化Ctrl

3、啟動Ctrl

 1、初始化View

初始化View就是調用InitViewPanels這個函數,InitViewPanels函數用於加載View目錄下定義的XxxPanel,在Game.lua的17行中可以看到定義。

function Game.InitViewPanels()
   for i = 1, #PanelNames do
      require ("View/"..tostring(PanelNames[i]))
   end
end

PanelName則是在LuaFramwwork/Lua/Common/define.lua的第7行中定義的,對應面板的名稱。

PanelNames = {
    "PromptPanel",    
    "MessagePanel",
}

 

2、初始化Ctrl

初始化Ctrl是指CtrlManager.Init();這句,可以在LuaFramwwork/Lua/Logic/CtrlManager.lua第9行中看到相關定義。這個函數中通過調用New函數創建了Ctrl的實例。

function CtrlManager.Init()
    logWarn("CtrlManager.Init----->>>");
    ctrlList[CtrlNames.Prompt] = PromptCtrl.New();
    ctrlList[CtrlNames.Message] = MessageCtrl.New();
    return this;
end

 

 3、啟動Ctrl

啟動就是根據CtrlNames找到對應的Ctrl的實例,然后調用其Awake方法,見代碼:

    local ctrl = CtrlManager.GetCtrl(CtrlNames.Prompt);
    if ctrl ~= nil and AppConst.ExampleMode == 1 then
        ctrl:Awake();
    end

 

以上都是推測,

為了驗證猜測的對不對,我把CtrlManager.GetCtrl(CtrlNames.Prompt)這一句改為CtrlManager.GetCtrl(CtrlNames.Message),如果這次加載出來的是MessagePanel,則說明上述過程推斷正確。

....

改完后運行,發現加載的還是PromptPanel,難道確實是找錯地方了?

別急,這里還涉及另一個概念。

 在熱更框架中,程序運行的並不是我們在LuaFramework/lua目錄下編寫的代碼,而是在Assets/StreamingAssets目錄下的打包后的代碼,見圖2-5。

圖2-5 

那么有什么辦法讓我們剛剛改的代碼生效呢?

思路有兩個:

  1. 將們的寫的代碼打包到StreamAssets中;
  2. 讓程序直接運行打包前的代碼;

思路1的操作方法是:執行LuaFramework菜單下的Build XXX Resources菜單(見圖2-6),因為我現在的程序是運行在Windows平台,所以選擇Build Windows Resource

 

圖2-6

點擊菜單,等待重新打包完成。打包結束后,能看到整個StreamingAssets目錄中的內容都更新了,在里邊可以找到message和prompt相關的資源,見圖2-7。

圖2-7

重新運行后,得到了想的結果,程序直接加載了MessagePanel面板,見圖2-8。

圖2-8

 

由此印證我們對整個面板流程的加載的推測分析

 

關於思路2讓程序直接運行打包前的代碼,只需要關閉Lua的AssetBundle模式就好了。

找到LuaFramework/Scripts/ConstDefine/AppConst.cs文件,將LuaBundleMode = true;改為

LuaBundleMode = false;即可,見圖2-8,圖中是改過之后的。

圖2-8

 LuaBundleMode 改為false之后,Lua代碼修改后就無需重新Build xxx Resources就能直接看到效果。

盡管思路1和思路2是二選一即可的,但為方便后邊的示例,這里要統一修改為false。

 

3、如何創建自己的面板

在上一步的分析中,我們得知創建一個面板需要先初始化View,再實例化Ctrl,然后調用Ctrl的Awake。這些都是代碼層面的,前提還有一個,我們需要一個XxxPanel預制體。

總結一下,如果要創建一個我們自己的面板,則需要如下步驟:

1、創建一個XxxPanel預制體

2、創建對應的XxxPanel腳本

3、創建對應的XxxCtrl腳本

4、添加CtrlNames及PanelNames

5、加載XxxCtrl

 

下面我將以FirstPanel為例進行演示。

1、創建FirstPanel預制體。

在Hierarchy面板中創建一個FirstPanel,並在LuaFramework目錄下新建CustomPrj/FirstTest目錄,將FirstPanel拖到此做成預制體,見圖3-1。

圖3-1

然后刪掉Hierarchy面板中的FirstPanel,因為后面我們會動態加載它。

2、創建FirstPanel.lua腳本。

在Lua/View目錄下創建一個FirstPanel的lua腳本,腳本結構參照MessageView編寫,如下:

local transform;
local gameObject;

FirstPanel = {};
local this = FirstPanel;

--啟動事件--
function FirstPanel.Awake(obj)
    gameObject = obj;
    transform = obj.transform;

    this.InitPanel();
    logWarn("Awake lua--->>"..gameObject.name);
end

--初始化面板--
function FirstPanel.InitPanel()
    --這句要注釋掉,因為我們的FirstPanel中沒有按鈕
    --this.btnClose = transform:FindChild("Button").gameObject;
end

--單擊事件--
function FirstPanel.OnDestroy()
    logWarn("OnDestroy---->>>");
end
View Code

 

注:lua腳本的創建方法是在IDEA中,選中目錄,右鍵->New->Lua File。

3、創建FirstCtrl.lua腳本。

在Lua/Controller目錄下創建一個FirsCtrl的lua腳本,腳本結構參照MessagCtrl編寫,如下:

 1 FirstCtrl = {};
 2 local this = FirstCtrl;
 3 
 4 local message;
 5 local transform;
 6 local gameObject;
 7 
 8 --構建函數--
 9 function FirstCtrl.New()
10     logWarn("FirstCtrl.New--->>");
11     return this;
12 end
13 
14 function FirstCtrl.Awake()
15     logWarn("FirstCtrl.Awake--->>");
16     panelMgr:CreatePanel('First', this.OnCreate);
17 end
18 
19 --啟動事件--
20 function FirstCtrl.OnCreate(obj)
21     gameObject = obj;
22 
23     message = gameObject:GetComponent('LuaBehaviour');
24 
25     --這句要注釋掉,因為我們的FirstPanel中沒有按鈕
26     --message:AddClick(MessagePanel.btnClose, this.OnClick);
27 
28     logWarn("Start lua--->>"..gameObject.name);
29 end
30 
31 --單擊事件--
32 function FirstCtrl.OnClick(go)
33     destroy(gameObject);
34 end
35 
36 --關閉事件--
37 function FirstCtrl.Close()
38     panelMgr:ClosePanel(CtrlNames.Message);
39 end
View Code

 

4、添加CtrlNames及PanelNames

在Lua/Common找到define.lua,在CtrlNames中添加 First = "FirstCtrl",在PanelNames中添加"FirstPanel",如下:

CtrlNames = {
    Prompt = "PromptCtrl",
    Message = "MessageCtrl",
    First = "FirstCtrl"
}

PanelNames = {
    "PromptPanel",    
    "MessagePanel",
    "FirstPanel"
}

 

5、加載FirstCtrl

在Lua/Logic/Game.lua文件的Game.OnInitOK函數中,將CtrlManager.GetCtrl()的參數修改為我們剛剛添加的CtrlNames.First,如下所示:

    CtrlManager.Init();
    local ctrl = CtrlManager.GetCtrl(CtrlNames.First);
    if ctrl ~= nil and AppConst.ExampleMode == 1 then
        ctrl:Awake();
    end

  

保存代碼並運行

..............

嗯,什么都沒加載出來。

好吧,我得承認,在學習這個框架的過程中,每走一步都是坑。

我就是在艱難的趟過這些坑來之后,才覺得有必要將這個過程記錄下來,才有了這一系列文章,希望對后來人有所幫助。

.............

為什么我們自己的創建的面板沒有加載呢?

查看日志發現,在"LuaFramework InitOK--->>>"日志輸出之前,PromptCtrl.New和MessageCtrl.New都被調用了一次,而我們新加的FirstCtrl卻沒有,見圖3-2。

圖3-2

 應該是我們某些地方少加了調用。

查找后發現,確實有這樣一個地方。在Lua/Logic/CtrlManager.lua腳本的Init方法,對所有Ctrl的New方法進行了調用。

我們添加對FirstCtrl.New的調用,如下:

function CtrlManager.Init()
    logWarn("CtrlManager.Init----->>>");
    ctrlList[CtrlNames.Prompt] = PromptCtrl.New();
    ctrlList[CtrlNames.Message] = MessageCtrl.New();
    ctrlList[CtrlNames.First] = FirstCtrl.New(); return this;
end

 (其實第二節中我們發現了這個地方,本節中忘了將自己的代碼加進去)

 

 然后再運行

.....

報錯了,說我們的FirstCtrl是一個nil value, 見圖3-3

圖3-3

經查,是在CtrlManager中,我們沒有加載對應的腳本,見圖3-4(圖中是已添加之后的)

圖3-4

再次運行

出現了更多的錯誤,見圖3-5

圖3-5

 ......

有沒有想崩潰的感覺,唉,我當初就是這么一步步過來的。

這次的錯誤是缺少first.unity3d.

這里的原因是,我們之前剛把Lua代碼AssetBundle模式關掉(設置為false),lua代碼不用AssetBundle模式了,但我們的資源(FirstPanel預制體)還 是使用的AssetBundle模式。

並且資源的AssetBundle模式好像無法關閉,因此需要對FirstPanel預制體進行打包操作。

操作如下:

1、找到LuaFramework/Editor/Packager.cs文件中的HandleExampleBundle方法(約160行左右),添加對FirstPanel預制體打包的代碼,包名為"first",如下所示:

 

    /// <summary>
    /// 處理框架實例包
    /// </summary>
    static void HandleExampleBundle() {
        string resPath = AppDataPath + "/" + AppConst.AssetDir + "/";
        if (!Directory.Exists(resPath)) Directory.CreateDirectory(resPath);

        AddBuildMap("prompt" + AppConst.ExtName, "*.prefab", "Assets/LuaFramework/Examples/Builds/Prompt");
        AddBuildMap("message" + AppConst.ExtName, "*.prefab", "Assets/LuaFramework/Examples/Builds/Message");

        //打包我們新加的FirstPanel預制體
        AddBuildMap("first" + AppConst.ExtName, "*.prefab", "Assets/LuaFramework/CustomPrj/FirstTest");

        AddBuildMap("prompt_asset" + AppConst.ExtName, "*.png", "Assets/LuaFramework/Examples/Textures/Prompt");
        AddBuildMap("shared_asset" + AppConst.ExtName, "*.png", "Assets/LuaFramework/Examples/Textures/Shared");
    }

 

2、執行unity編輯器上方LuaFramework菜單中的Build Windows Resources菜單項,進行打包操作。打包完成后,可以在StreamingAssets目錄中看到first.unity3d文件。見圖3-6

圖3-6

 

再次運行,

這次終於得到了我們想要的結果,我們自己創建的面板FirstPanel,就這么加載出來了。

見圖3-7

圖3-7

 

真是太不容易了!

 

現在,將我們改錯的經過都加入到完整的步驟中,那么,加載一個我們自己創建的面板的完整步驟如下:

1、創建一個XxxPanel預制體

2、創建對應的XxxPanel腳本

3、創建對應的XxxCtrl腳本

4、添加CtrlNames及PanelNames

5、在CtrlManager中加入對XxxCtrl.New的調用,並在頭部require "XxxCtrl"

6、在Packager.cs文件中對XxxPanel預制體進行打包

7、在Game.lua加載XxxCtrl

 

 后續寫模塊的時候都會按這個流程來。

 

后記 

在本篇文章的第二節的寫作過程中,為什么我會用推測並驗證的寫法,而不是直接給出一個正確結論?第三節中,我為什么沒有直接給出正確的操作步驟,而是邊走邊改錯?

因為我希望本文能如實還原我學LuaFramework的過程,記錄每一個問題的發生條件,以及我解決問題的思路。

 下一篇文章將會介紹如何加載非XXXPanel的預制體以及按鈕事件處理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM