笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。
CSDN视频网址:http://edu.csdn.net/lecturer/144
架构设计也是优化的一种,一款游戏如果没有一个好的架构,程序出现问题很难做到及时的响应,读者可以试想一下,如果编程的时候只是为了实现功能而实现功能,到头来就是代码越写越乱,各种功能交织在一起。出现问题后到处找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函数。后面继续代码的编写。。。。