Unity开发框架整理

ToLua

此Unity框架支持Lua,所使用lua为ToLua,这里介绍了有关uLua以及toLua的相关内容。当然除了uLua和toLua意外,还有腾讯开发的XLua,xLua更加灵活,支持直接给C#进行打补丁的操作。
使用toLua开发的游戏很多,如下图所示



这里面包括了最火的王者荣耀
https://github.com/topameng/CsToLua

Unity


一般成熟项目的Unity工程至少包含
Editor
Plugins
Resources
StreamingAssets
这几个目录
然后框架中Game作为C#核心库

开发框架

  • 主入口Main
  • GameMain作为单例一直存在与整个游戏的生命周期内,主要完成初始化工作。

  • 基本类关系
  • Facade
    作为模块管理器存在,主要作用完成模块的初始化添加、获取与删除。
    主要接口有AddModule、GetModule、RemoveModule,而我们的框架使用的是继承于Facade的AppFacade。
    在Main中除了初始化AppFacade还有就是初始化状态机。

  • GameStateMachine
  • GameStateMachine stateMachine = AppFacade.Instance.AddModule ();
    

    状态机通过调用StateTransition方法来完成游戏状态的转变以及执行相应的代码

    public T StateTransition () where T : GameStateBase {
        GameStateBase oldState = currentState;
        currentState = gameObject.AddComponent ();
        if (oldState != null)
            oldState.PrepareExit ();
    
        currentState.PrepareEnter ();
        if (oldState != null)
            oldState.Exit ();
    
        currentState.Enter ();
        if (oldState != null)
            oldState.DoneExit ();
    
        currentState.DoneEnter ();
        Object.Destroy (oldState);      // 销毁旧的状态(组件)
        return currentState as T;
    }
    

    大致的流程分
    1 旧状态->PrepareExit
    2 旧状态->Exit
    3 新状态->Enter
    4 旧状态->DoneExit
    5 新状态->DoneExit

    状态机初始化完成操作:
    <状态:Initializing>
    1 添加FileHelper模块,主要用于文件操作。同时配置文件搜索路径。
    2 添加AssetBundleLoader模块,主要用于AssetBundle的相关操作。
    3 状态转变为Upgrading。
    <状态:Upgrading>
    1 添加ResourceDownloader模块,用于资源和代码下载。
    2 完成Upgrade工作。
    3 初始化AssetBundleLoader。
    4 添加并初始化ResourceManager,主要用于资源管理。
    5 添加并初始化LuaManager,主要用于Lua管理。
    Lua初始化调用

    protected virtual void OnLoadFinished()
    {
        luaState.Start();
        StartLooper();
        StartMain();
    }
    
    protected virtual void StartMain()
    {
        luaState.DoFile("Main.lua");
        levelLoaded = luaState.GetFunction("OnLevelWasLoaded");
        CallMain();
    }
    

    此时已经运行到Main.lua,前提一定完成Lua Binding。
    6 状态转变为Ready。
    此时完成了框架初始化的工作。这里需要特别注意的是在执行MainLua之前,我们需要加载完成需要使用的Lua,通过接口:DoLoadAssetBundleAsync。

    IEnumerator DoLoadAssetBundleAsync (Action onFinish) {
        // Get lua manifest
        string path = GameSettings.AppContentPath + GameSettings.GetOS () + "/" + GameConsts.luaBundleFile;
        string json = File.ReadAllText (path);
    
        LuaBundleList manifest = JsonUtility.FromJson (json);
    
        for (int i = 0; i < manifest.items.Count; i++) {
            string abName = manifest.items [i];
    
            yield return StartCoroutine (DoLoadAssetBundleAsync (abName, delegate (AssetBundle ab) {
                if (!ab) {
                    Debug.Log ("[LuaManager] DoLoadAssetBundleAsync : Can't find the assetbundle " + abName);
                    return;
                }
    
                // Add the bundle to searched bundles.
                abName = abName.Replace ("lua/", "");
                abName = abName.Replace (GameSettings.ExtName, "");
                LuaFileUtils.Instance.AddSearchBundle (abName.ToLower (), ab);
            }));
        }
            
        onFinish.Invoke ();
    }
    
    IEnumerator DoLoadAssetBundleAsync (string abName, Action onFinish) {
        string abPath = FileHelper.Instance.FindFile (abName);
        if (abPath == null) {
            onFinish (null);
            yield break;
        }
    
        AssetBundle ab = null;
        if (GameSettings.LuaEncryptMode) {
            abPath = "file://" + abPath;
    
            WWW www = new WWW (abPath);
            yield return www;
    
            byte[] encData = www.bytes;
            byte[] decData = CryptoUtil.Decrypt (encData, GameSettings.Secret);
    
            var loadRequest = AssetBundle.LoadFromMemoryAsync (decData);
            yield return loadRequest;
    
            ab = loadRequest.assetBundle;
        } else {
            var loadRequest = AssetBundle.LoadFromFileAsync (abPath);
            yield return loadRequest;
    
            ab = loadRequest.assetBundle;
        }
    
        onFinish (ab);
    }
    

    故此我们知道LuaBundles.json是我们需要初始化的luaBundle。

  • Lua
  • CustomSettings是我们需要绑定C#接口给Lua调用定义的地方。
    Main.Lua是LuaManager会初始化调用的文件,而Main函数是最开始调用的。

    require "init"
    ...
    local app = nil
    function Main()
        // 这里GameMain是游戏整个生命周期的都存在的
        local gameMain = UGameObject.Find("GameMain")
        app = App:instance(gameMain)
        app:registerScene(MainScene())
        app:registerScene(DinningScene())
        app:registerScene(LoadScene())
        app:registerTransition(MaskTransition())
        app:runScene(SceneConsts.Main)
    end
    

    init主要完成了lua脚本以及运行框架的所有初始化,大致完成了如下功能定义和初始化:
    1 async(Lua异步接口)
    async.iterator = function(fn_list)
    async.waterfall = function(fn_list, cb)
    async.forEach = function(arr, it, callback)
    async.forEachSeries = function(arr, it, callback)
    参考:Github Async

    2 class(类定义)

    function clone(object)
        local lookup_table = { }
        local function _copy(object)
            if type(object) ~= "table" then
                return object
            elseif lookup_table[object] then
                return lookup_table[object]
            end
            local new_table = { }
            lookup_table[object] = new_table
            for key, value in pairs(object) do
                new_table[_copy(key)] = _copy(value)
            end
            return setmetatable(new_table, getmetatable(object))
        end
        return _copy(object)
    end
    function class(base)
        local c = { }
        if type(base) == 'table' then
            -- our new class is a shallow copy of the base class!
            for i, v in pairs(base) do
                c[i] = v
            end
            c._base = base
        end
        -- the class will be the metatable for all its objects,
        -- and they will look up their methods in it.
        c.__index = c
    
        -- expose a constructor which can be called by ()
        local mt = { }
        mt.__call = function(class_tbl, ...)
            local obj = { }
            setmetatable(obj, c)
            if class_tbl.init then
                class_tbl.init(obj, ...)
            else
                -- make sure that any stuff from the base class is initialized!
                if base and base.init then
                    base.init(obj, ...)
                end
            end
            return obj
        end
        c.is_a = function(self, klass)
            local m = getmetatable(self)
            while m do
                if m == klass then return true end
                m = m._base
            end
            return false
        end
        setmetatable(c, mt)
        return c
    end
    -- A = class()
    -- function A:init(x)
    --     self.x = x
    -- end
    -- function A:test()
    --     print(self.x)
    -- end
    
    -- B = class(A)
    -- function B:init(x, y)
    --     A.init(self, x)
    --     self.y = y
    -- end
    
    -- b = B(1, 2)
    -- b:test()
    
    -- print(b:is_a(A))
    

    3 function(公用函数合集)
    4 const常量合集,用于MVC分层的常量定义。
    5 Utility
    6 Core主要包括App、Controller、Scene、View以及消息机制和管理器
    7 game中主要是逻辑代码和逻辑的MVC分层

    欢迎留言

    avatar
      Subscribe  
    Notify of