内容纲要

1.导入LuaFramework_UGUI_V2包后,首先需要根据平台在LuaFramwork中Build下,生成对应平台的Resource文件(会生成StreamingAssets目录)

2.然后启动Scenes下的main场景运行:

3.在main场景下只绑定了一个空的GameManager和一个Main的Scripts: 它调用了AppFacade中的StartUp用MVC发送了开始的消息:

    public class Main : MonoBehaviour {

        void Start() {
            AppFacade.Instance.StartUp();   //启动游戏
        }
    }

    public void StartUp() {
        SendMessageCommand(NotiConst.START_UP);
        RemoveMultiCommand(NotiConst.START_UP);
    }

4.这个消息被StartUpCommand类处理:

 RegisterCommand(NotiConst.START_UP, typeof(StartUpCommand));

5.其中重要的就是向AppFacade(继承自Facade,包含MVC控制器的一个综合管理类吧),注册Manager //除了加入一个Dictionary外,还在上面提到的空节点的对象上添加了所有的Manager类

public class StartUpCommand : ControllerCommand {

    public override void Execute(IMessage message) {
        if (!Util.CheckEnvironment()) return;

        GameObject gameMgr = GameObject.Find("GlobalGenerator");
        if (gameMgr != null) {
            AppView appView = gameMgr.AddComponent<AppView>();
        }
        //-----------------关联命令-----------------------
        AppFacade.Instance.RegisterCommand(NotiConst.DISPATCH_MESSAGE, typeof(SocketCommand));

        //-----------------初始化管理器-----------------------
        AppFacade.Instance.AddManager<LuaManager>(ManagerName.Lua);
        AppFacade.Instance.AddManager<PanelManager>(ManagerName.Panel);
        AppFacade.Instance.AddManager<SoundManager>(ManagerName.Sound);
        AppFacade.Instance.AddManager<TimerManager>(ManagerName.Timer);
        AppFacade.Instance.AddManager<NetworkManager>(ManagerName.Network);
        AppFacade.Instance.AddManager<ResourceManager>(ManagerName.Resource);
        AppFacade.Instance.AddManager<ThreadManager>(ManagerName.Thread);
        AppFacade.Instance.AddManager<ObjectPoolManager>(ManagerName.ObjectPool);
        AppFacade.Instance.AddManager<GameManager>(ManagerName.Game);
    }
}

6.其中大部分管理类可以通用,特别的就是GameManager这个类,负责游戏相关的核心特性逻辑:

        void Awake() {
            Init();
        }

        void Init() {
            DontDestroyOnLoad(gameObject);  //防止销毁自己

            CheckExtractResource(); //释放资源
            Screen.sleepTimeout = SleepTimeout.NeverSleep;
            Application.targetFrameRate = AppConst.GameFrameRate;
        }

        public void CheckExtractResource() {
            bool isExists = Directory.Exists(Util.DataPath) &&
              Directory.Exists(Util.DataPath + "lua/") && File.Exists(Util.DataPath + "files.txt");
            if (isExists || AppConst.DebugMode) {
                StartCoroutine(OnUpdateResource());
                return;   //文件已经解压过了,自己可添加检查文件列表逻辑
            }
            StartCoroutine(OnExtractResource());    //启动释放协成 
        }

        IEnumerator OnUpdateResource() {   //下载资源
            if (!AppConst.UpdateMode) {
                OnResourceInited();
                yield break;
            }
            string dataPath = Util.DataPath;  //数据目录
            string url = AppConst.WebUrl;
            string message = string.Empty;
            string random = DateTime.Now.ToString("yyyymmddhhmmss");
            string listUrl = url + "files.txt?v=" + random;
            Debug.LogWarning("LoadUpdate---->>>" + listUrl);

            WWW www = new WWW(listUrl); yield return www;
            if (www.error != null) {
                OnUpdateFailed(string.Empty);
                yield break;
            }
            if (!Directory.Exists(dataPath)) {
                Directory.CreateDirectory(dataPath);
            }
            File.WriteAllBytes(dataPath + "files.txt", www.bytes);
            string filesText = www.text;
            string[] files = filesText.Split('\n');

            for (int i = 0; i < files.Length; i++) {
                if (string.IsNullOrEmpty(files[i])) continue;
                string[] keyValue = files[i].Split('|');
                string f = keyValue[0];
                string localfile = (dataPath + f).Trim();
                string path = Path.GetDirectoryName(localfile);
                if (!Directory.Exists(path)) {
                    Directory.CreateDirectory(path);
                }
                string fileUrl = url + f + "?v=" + random;
                bool canUpdate = !File.Exists(localfile);
                if (!canUpdate) {
                    string remoteMd5 = keyValue[1].Trim();
                    string localMd5 = Util.md5file(localfile);
                    canUpdate = !remoteMd5.Equals(localMd5);
                    if (canUpdate) File.Delete(localfile);
                }
                if (canUpdate) {   //本地缺少文件
                    Debug.Log(fileUrl);
                    message = "downloading>>" + fileUrl;
                    facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);
                    /*
                    www = new WWW(fileUrl); yield return www;
                    if (www.error != null) {
                        OnUpdateFailed(path);   //
                        yield break;
                    }
                    File.WriteAllBytes(localfile, www.bytes);
                     */
                    //这里都是资源文件,用线程下载
                    BeginDownload(fileUrl, localfile);
                    while (!(IsDownOK(localfile))) { yield return new WaitForEndOfFrame(); }
                }
            }
            yield return new WaitForEndOfFrame();

            message = "更新完成!!";
            facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);

            OnResourceInited();
        }

        public void OnResourceInited() {
#if ASYNC_MODE
            ResManager.Initialize(AppConst.AssetDir, delegate() {
                Debug.Log("Initialize OK!!!");
                this.OnInitialize();
            });
#else
            ResManager.Initialize();
            this.OnInitialize();
#endif
        }

        void OnInitialize() {    //这个就是比较重要的初始化函数了
            LuaManager.InitStart();
            LuaManager.DoFile("Logic/Game");         //加载游戏
            LuaManager.DoFile("Logic/Network");      //加载网络
            NetManager.OnInit();                     //初始化网络
            Util.CallMethod("Game", "OnInitOK");     //初始化完成    //这里加载完相应的Lua脚本后,调用了Game脚本中的OnInitOK开始真正创建场景

            initialize = true;

            //类对象池测试
            var classObjPool = ObjPoolManager.CreatePool<TestObjectClass>(OnPoolGetElement, OnPoolPushElement);
            //方法1
            //objPool.Release(new TestObjectClass("abcd", 100, 200f));
            //var testObj1 = objPool.Get();

            //方法2
            ObjPoolManager.Release<TestObjectClass>(new TestObjectClass("abcd", 100, 200f));
            var testObj1 = ObjPoolManager.Get<TestObjectClass>();

            Debugger.Log("TestObjectClass--->>>" + testObj1.ToString());

            //游戏对象池测试
            var prefab = Resources.Load("TestGameObjectPrefab", typeof(GameObject)) as GameObject;
            var gameObjPool = ObjPoolManager.CreatePool("TestGameObject", 5, 10, prefab);

            var gameObj = Instantiate(prefab) as GameObject;
            gameObj.name = "TestGameObject_01";
            gameObj.transform.localScale = Vector3.one;
            gameObj.transform.localPosition = Vector3.zero;

            ObjPoolManager.Release("TestGameObject", gameObj);
            var backObj = ObjPoolManager.Get("TestGameObject");
            backObj.transform.SetParent(null);

            Debug.Log("TestGameObject--->>>" + backObj);
        }

7.Game lua脚本的一部分:

function Game.InitViewPanels()
    for i = 1, #PanelNames do
        require ("View/"..tostring(PanelNames[i]))
    end
end
--初始化完成,发送链接服务器信息--
function Game.OnInitOK()
    AppConst.SocketPort = 2012;
    AppConst.SocketAddress = "127.0.0.1";
    networkMgr:SendConnect();

    --注册LuaView--
    this.InitViewPanels();

    this.test_class_func();
    this.test_pblua_func();
    this.test_cjson_func();
    this.test_pbc_func();
    this.test_lpeg_func();
    this.test_sproto_func();
    coroutine.start(this.test_coroutine);

    CtrlManager.Init();
    local ctrl = CtrlManager.GetCtrl(CtrlNames.Prompt);
    if ctrl ~= nil and AppConst.ExampleMode == 1 then
        ctrl:Awake();
    end 
    logWarn('LuaFramework InitOK--->>>');
end
  • 在上面Lua脚本的InitViewPanels中的PanelNames 来自于Lua -> Comm中的define lua脚本: //在这里可以自定义我们要创建的Panel和控制脚本名称

    CtrlNames = {
    Prompt = "PromptCtrl",
    Message = "MessageCtrl"
    }
    PanelNames = {
    "PromptPanel",    
    "MessagePanel",
    }
  • 然后需要在Lua -> View下有同名的Panel的lua脚本,在lua -> Controller下有个同名的控制脚本

    local transform;
    local gameObject;
    PromptPanel = {};
    local this = PromptPanel;
    --启动事件--
    function PromptPanel.Awake(obj)
    gameObject = obj;
    transform = obj.transform;
    
    this.InitPanel();
    logWarn("Awake lua--->>"..gameObject.name);
    end
    --初始化面板--
    function PromptPanel.InitPanel()
    this.btnOpen = transform:Find("Open").gameObject;
    this.gridParent = transform:Find('ScrollView/Grid');
    end
    --单击事件--
    function PromptPanel.OnDestroy()
    logWarn("OnDestroy---->>>");
    end

8.在上面Game lua脚本执行CtrlManager.Init(),时创建了PromptCtrl和MessageCtrl,

function CtrlManager.Init()
    logWarn("CtrlManager.Init----->>>");
    ctrlList[CtrlNames.Prompt] = PromptCtrl.New();
    ctrlList[CtrlNames.Message] = MessageCtrl.New();
    return this;
end
  • 在PromptCtrl.New()只是return了this,但是lua脚本的特效加载时就向这个table注册了很多函数:

    local this = PromptCtrl;
    function PromptCtrl.New()
    logWarn("PromptCtrl.New--->>");
    return this;
    end
  • 在Game lua脚本执行CtrlManager.GetCtrl(CtrlNames.Prompt);和 ctrl:Awake();实际上是在PromptCtrl和MessageCtrl中执行Awake:

    function PromptCtrl.Awake()
    logWarn("PromptCtrl.Awake--->>");
    panelMgr:CreatePanel('Prompt', this.OnCreate);
    end
    function MessageCtrl.Awake()
    logWarn("MessageCtrl.Awake--->>");
    panelMgr:CreatePanel('Message', this.OnCreate);
    end

9.panelMgr:CreatePanel是调用了C#中我们前面注册的PanelManager中的CreatePanel:AppFacade.Instance.AddManager(ManagerName.Panel);

        public void CreatePanel(string name, LuaFunction func = null) {
            string assetName = name + "Panel";
            string abName = name.ToLower() + AppConst.ExtName;
            if (Parent.FindChild(name) != null) return;

#if ASYNC_MODE
            ResManager.LoadPrefab(abName, assetName, delegate(UnityEngine.Object[] objs) {   //核心就是创建了对应的Prefab,并设置一些参数,(这些参数可以通过Lua传入),然后把创建的GameObject(在这里是一个Panel(带有一个Image和Lua Behaviour组件)),然后把该gameObject又传回到Lua提供的回调中
                if (objs.Length == 0) return;
                GameObject prefab = objs[0] as GameObject;
                if (prefab == null) return;

                GameObject go = Instantiate(prefab) as GameObject;
                go.name = assetName;
                go.layer = LayerMask.NameToLayer("Default");
                go.transform.SetParent(Parent);
                go.transform.localScale = Vector3.one;
                go.transform.localPosition = Vector3.zero;
                go.AddComponent<LuaBehaviour>();

                if (func != null) func.Call(go);
                Debug.LogWarning("CreatePanel::>> " + name + " " + prefab);
            });
#else
            GameObject prefab = ResManager.LoadAsset<GameObject>(name, assetName);
            if (prefab == null) return;

            GameObject go = Instantiate(prefab) as GameObject;
            go.name = assetName;
            go.layer = LayerMask.NameToLayer("Default");
            go.transform.SetParent(Parent);
            go.transform.localScale = Vector3.one;
            go.transform.localPosition = Vector3.zero;
            go.AddComponent<LuaBehaviour>();

            if (func != null) func.Call(go);
            Debug.LogWarning("CreatePanel::>> " + name + " " + prefab);
#endif
        }

10.回调的主要目的注册UI事件:

function MessageCtrl.OnCreate(obj)
    gameObject = obj;

    message = gameObject:GetComponent('LuaBehaviour');
    message:AddClick(MessagePanel.btnClose, this.OnClick);   //这里得到我们之前创建的LuaBehaviour脚本,并调用里面的AddClick函数添加按键回调

    logWarn("Start lua--->>"..gameObject.name);
end

LuaBehaviour中的AddClick:
        public void AddClick(GameObject go, LuaFunction luafunc) {
            if (go == null || luafunc == null) return;
            buttons.Add(go.name, luafunc); 
     go.GetComponent<Button>().onClick.AddListener(   //注册onClick回调    //有个问题, 在上面调用message:AddClick(MessagePanel.btnClose, this.OnClick); 时传入MessagePanel.btnClose,这个MessagePanel.btnClose是在什么时候指定到对应的Button上的???   //是在go.name = assetName; go.AddComponent<LuaBehaviour>();时,在LuaBehaviour中的awake等函数中,会调用响应Lua脚本中的函数,来做初始化
                delegate() {
                    luafunc.Call(go);
                }
            );
        }

发表评论