1. 程式人生 > >Unity3D優化技巧系列五

Unity3D優化技巧系列五

筆者介紹:姜雪偉,IT公司技術合夥人,IT高階講師,CSDN社群專家,特邀編輯,暢銷書作者,國家專利發明人;已出版書籍:《手把手教你架構3D遊戲引擎》電子工業出版社和《Unity3D實戰核心技術詳解》電子工業出版社等。

架構設計也是優化的一種,一款遊戲如果沒有一個好的架構,程式出現問題很難做到及時的響應,讀者可以試想一下,如果程式設計的時候只是為了實現功能而實現功能,到頭來就是程式碼越寫越亂,各種功能交織在一起。出現問題後到處找BUG修復,優化就更無從談起了,誰也不希望把即將上線的專案程式碼,重新重構一遍,那還不如重新再做一遍,所以架構設計在遊戲開發中是非常重要的,而且是必須要掌握的技術。下面重點給讀者介紹MVC和FSM架構模式,效果如下所示:


MVC架構模式在前面的優化系列中給讀者提過,下面就詳細的告訴讀者如何編寫程式碼?給讀者詳細的分析一下,拿視窗的切換進行距離,遊戲執行時,首先是創立介面,我們把每個介面作為一個視窗,比如LoginWindow,HeroWindow等。它們也是作為View介面,這樣遊戲中就會出現很多介面的設計,效果如下所示:


下面分析介面的編寫,上文提到的每個UI就是一個Window,也是一個View,這麼多View肯定有自己的共性,我們可以將其定義為基類,基類稱為BaseWindow,程式碼編寫如下所示:

namespace Game.View
{
    public abstract class BaseWindow
    {
        protected Transform mRoot;

        protected EScenesType mScenesType; //場景型別
        protected string mResName;         //資源名
        protected bool mResident;          //是否常駐 
        protected bool mVisible = false;   //是否可見
      

        //類物件初始化
        public abstract void Init();

        //類物件釋放
        public abstract void Realse();

        //視窗控制初始化
        protected abstract void InitWidget();

        //視窗控制元件釋放
        protected abstract void RealseWidget();

        //遊戲事件註冊
        protected abstract void OnAddListener();

        //遊戲事件注消
        protected abstract void OnRemoveListener();

        //顯示初始化
        public abstract void OnEnable();

        //隱藏處理
        public abstract void OnDisable();

        //每幀更新
        public virtual void Update(float deltaTime) { }

        //取得所以場景型別
        public EScenesType GetScenseType()
        {
            return mScenesType;
        }

        //是否已開啟
        public bool IsVisible() { return mVisible;  }

        //是否常駐
        public bool IsResident() { return mResident; }

        //顯示
        public void Show()
        {
            if (mRoot == null)
            {
                if (Create())
                {
                    InitWidget();
                }
            }

            if (mRoot && mRoot.gameObject.activeSelf == false)
            {
                mRoot.gameObject.SetActive(true);

                mVisible = true;

                 OnEnable();

                OnAddListener();
            }
        }

        //隱藏
        public void Hide()
        {
            if (mRoot && mRoot.gameObject.activeSelf == true)
            {
                OnRemoveListener();
                OnDisable();

                if (mResident)
                {
                    mRoot.gameObject.SetActive(false);
                }
                else
                {
                    RealseWidget();
                    Destroy();
                }
            }

            mVisible = false;
        }

        //預載入
        public void PreLoad()
        {
            if (mRoot == null)
            {
                if (Create())
                {
                    InitWidget();
                }
            }
        }

        //延時刪除
        public void DelayDestory()
        {
            if (mRoot)
            {
                RealseWidget();
                Destroy();
            }
        }

        //建立窗體
        private bool Create()
        {
            if (mRoot)
            {
                Debug.LogError("Window Create Error Exist!");
                return false;
            }

            if (mResName == null || mResName == "")
            {
                Debug.LogError("Window Create Error ResName is empty!");
                return false;
            }

            if (GameMethod.GetUiCamera.transform== null)
            {
                Debug.LogError("Window Create Error GetUiCamera is empty! WindowName = " + mResName);
                return false;
            }

            GameObject obj = LoadUiResource.LoadRes(GameMethod.GetUiCamera.transform, mResName);

            if (obj == null)
            {
                Debug.LogError("Window Create Error LoadRes WindowName = " + mResName);
                return false;
            }

            mRoot = obj.transform;

            mRoot.gameObject.SetActive(false);

            return true;
        }

        //銷燬窗體
        protected void Destroy()
        {
            if (mRoot)
            {
                LoadUiResource.DestroyLoad(mRoot.gameObject);
                mRoot = null;
            }
        }

        //取得根節點
        public Transform GetRoot()
        {
            return mRoot;
        }
    }
}

以上是關於窗體的基類,實現了所有窗體需要建立的方法,建立窗體,銷燬窗體,預載入,掩藏 等操作。實行按的具體操作類需要繼承該窗體類,比如LoginWindow的寫法如下所示:
namespace Game.View
{
    public class LobbyWindow : BaseWindow
    {
        public LobbyWindow()
        {
            mScenesType = EScenesType.EST_Login; //場景型別
            mResName = GameConstDefine.LoadGameLobbyUI; //資源名字
            mResident = false;  //是否常駐記憶體
        }

        ////////////////////////////繼承介面/////////////////////////
        //類物件初始化
        public override void Init()
        {
            EventCenter.AddListener(EGameEvent.eGameEvent_LobbyEnter, Show);
            EventCenter.AddListener(EGameEvent.eGameEvent_LobbyExit, Hide); 
        }

        //類物件釋放
        public override void Realse()
        {
            EventCenter.RemoveListener(EGameEvent.eGameEvent_LobbyEnter, Show);
            EventCenter.RemoveListener(EGameEvent.eGameEvent_LobbyExit, Hide); 
        }

        //視窗控制元件初始化
        protected override void InitWidget()
        {           
            mHomepage = mRoot.FindChild("StartMenuManager/StartMenuBtn/Homepage").GetComponent<UIToggle>();
            UIGuideCtrl.Instance.AddUiGuideEventBtn(mHomepage.gameObject);
            mBattle = mRoot.FindChild("StartMenuManager/StartMenuBtn/Battle").GetComponent<UIToggle>();
            mMarket = mRoot.FindChild("StartMenuManager/StartMenuBtn/Market").GetComponent<UIToggle>();
            mInteraction = mRoot.FindChild("StartMenuManager/StartMenuBtn/Interaction").GetComponent<UIToggle>();

            UIGuideCtrl.Instance.AddUiGuideEventBtn(mMarket.gameObject);
            mDiamondText = mRoot.FindChild("Status/Diamond/Label").GetComponent<UILabel>();
            mGoldText = mRoot.FindChild("Status/Gold/Label").GetComponent<UILabel>();
            mSettingBtn = mRoot.FindChild("Status/Setting").GetComponent<UIButton>();

            UIGuideCtrl.Instance.AddUiGuideEventBtn(mBattle.gameObject);
            EventDelegate.Add(mHomepage.onChange, OnHomePageChange);
            EventDelegate.Add(mBattle.onChange, OnBattleChange);
            EventDelegate.Add(mMarket.onChange, OnMarketChange);
            EventDelegate.Add(mInteraction.onChange, OnInteractionChange);
            UIEventListener.Get(mHeadIcon.transform.parent.gameObject).onClick += InfoPersonPress;
        }
        private void InfoPersonPress(GameObject go)
        {
            GameLog.Instance.AddUIEvent(GameLog.UIEventType.UIEventType_PersonalInfo);

            LobbyCtrl.Instance.AskPersonInfo();
            //PresonInfoCtrl.Instance.Enter();
        }
      
        //視窗控制元件釋放
        protected override void RealseWidget()
        {
        }


        //遊戲事件註冊
        protected override void OnAddListener()
        {
            EventCenter.AddListener(EGameEvent.eGameEent_ChangeMoney, RefreshMoney);
            EventCenter.AddListener<bool>(EGameEvent.eGameEvent_ReceiveLobbyMsg, NewChat);
            EventCenter.AddListener(EGameEvent.eGameEvent_AddNewMailReq, NoticeNewMail);
            EventCenter.AddListener(EGameEvent.eGameEvent_ChangeNickName,ChangeNickName);
            EventCenter.AddListener(EGameEvent.eGameEvent_ChangeHeadID, ChangeHeadID);

            EventCenter.AddListener(EGameEvent.eGameEvent_ChangeUserLevel, ChangeLevel);
        }
        private void ChangeHeadID()
        {
            mHeadIcon.spriteName = GameUserModel.Instance.GameUserHead.ToString();
        }

        //遊戲事件注消
        protected override void OnRemoveListener()
        {
            EventCenter.RemoveListener(EGameEvent.eGameEent_ChangeMoney, RefreshMoney);
            EventCenter.RemoveListener<bool>(EGameEvent.eGameEvent_ReceiveLobbyMsg, NewChat);
            EventCenter.RemoveListener(EGameEvent.eGameEvent_AddNewMailReq, NoticeNewMail);
            EventCenter.RemoveListener(EGameEvent.eGameEvent_ChangeNickName, ChangeNickName);
            EventCenter.RemoveListener(EGameEvent.eGameEvent_ChangeHeadID, ChangeHeadID);
            EventCenter.RemoveListener(EGameEvent.eGameEvent_ChangeUserLevel, ChangeLevel);
        }

        //顯示
        public override void OnEnable()
        {

        }

        //隱藏
        public override void OnDisable()
        {

        }

        public void ChangeUserLevel()
        {
            mLevel.text = GameUserModel.Instance.UserLevel.ToString();
        }
//回撥事件
        public void OnHomePageChange()
        {
            //todo
            mLevel.text = GameUserModel.Instance.UserLevel.ToString();
            mNickName.text = GameUserModel.Instance.GameUserNick;
            mHeadIcon.spriteName = GameUserModel.Instance.GameUserHead.ToString();
            mGold.text = GameUserModel.Instance.mGameUserGold.ToString();
            mDiamond.text = GameUserModel.Instance.mGameUserDmd.ToString();
            VipSignLevel.text = "VIP "+GameUserModel.Instance.GameUserVipLevel.ToString();
            int level = GameUserModel.Instance.UserLevel;
            mLevel.text = level.ToString();
            
            LevelConfigInfo leveinfo = ConfigReader.GetLevelInfo(level);
            if (leveinfo != null)
            {
                mExp.text = GameUserModel.Instance.GameUserExp + "/" + leveinfo.LevelUpExp;
                mExp.transform.parent.GetComponent<UISprite>().fillAmount = GameUserModel.Instance.GameUserExp / leveinfo.LevelUpExp;
                if (level >= 29 && GameUserModel.Instance.GameUserExp >= leveinfo.LevelUpExp)
                {
                    level = 30;
                    mLevel.text = level.ToString();
                    mExp.gameObject.SetActive(false);
                    mExp.transform.parent.GetComponent<UISprite>().fillAmount = 1f;
                }
            }
            
            if (mHomepage.value)
            {
                GameLog.Instance.AddUIEvent(GameLog.UIEventType.UIEventType_HomePage);
                HomePageCtrl.Instance.Enter();
            }
            else
            {
                HomePageCtrl.Instance.Exit();
            }
        }

        public void OnBattleChange()
        {
            if (mBattle.value)
            {
                GameLog.Instance.AddUIEvent(GameLog.UIEventType.UIEventType_Battle);
                BattleCtrl.Instance.Enter();
            }
            else
            {
                BattleCtrl.Instance.Exit();
            }
        }

        public void OnMarketChange()
        {
            if (mMarket.value)
            {
                GameLog.Instance.AddUIEvent(GameLog.UIEventType.UIEventType_Market);
                MarketCtrl.Instance.Enter();
            }
            else
            {
                MarketCtrl.Instance.Exit();
            }
        }

        public void OnInteractionChange()
        {
            if (mInteraction.value)
            {
                GameLog.Instance.AddUIEvent(GameLog.UIEventType.UIEventType_Friend);
                SocialCtrl.Instance.Enter();
            }
            else
            {
                SocialCtrl.Instance.Exit();
            }
        }

        public void MailListBtn(GameObject obj)
        {
            mMailBtnBg.SetActive(false);

            MailCtrl.Instance.Enter();
        }
    }


}
該類主要是實現整個UI的邏輯編寫,其他的視窗編寫類似。下面編寫Control控制類程式碼的編寫,示例程式碼如下:
    public class LoginCtrl : Singleton<LoginCtrl>
    {
        public void Enter()
        {
            EventCenter.Broadcast(EGameEvent.eGameEvent_LoginEnter);
        }

        public void Exit()
        {
            EventCenter.Broadcast(EGameEvent.eGameEvent_LoginExit);
        }

        //登陸
        public void Login(string account, string pass)
        {

        }

        //登陸錯誤反饋
        public void LoginError(int code)
        {

        }

        //接收GateServer資訊
        public void RecvGateServerInfo(Stream stream)
        {

        }

        //登陸失敗
        public void LoginFail()
        {

        }

        //選擇LoginServer
        public void SelectLoginServer(int i)
        {

        }
        //開始遊戲
        public void GamePlay()
        {

        }
   }
控制類的主要作用是進行一些訊息的傳遞,這也是為了解除模組之間的耦合性,該模組的主要核心是Enter函式和Exit函式。後面繼續程式碼的編寫。。。。