UI界面搭建 进入GameStart场景,在Hierarchy窗口确认GameStart这个Prefab已经放好。
在GameStart——UIRoot——WndRoot下新建子GameObject,命名为HotFixPanel
,并设为stretch窗口
在HotFixPanel
下新建Image命名为BG,Color设为黑色,改为Stretch模式。
在BG下新建Image命名为ProgressBar,改为Filled——Horizontal类型。
下载时进度条走一次、解压时进度条走一次、验证时再走一次(可选)
在BG下新建SpeedText,放在BG靠近右下角ProgressBar上方的位置,在其中输入“3m/s”。
在BG下新建ProcessText,放在SpeedText左侧,在其中输入“下载中……”
在HotFixPanel
下新建Image命名为Info
在Info下新建Title(Text),在其中输入“热更内容”,调整位置到Info中间上部
在Info下新建Scroll View,在它的Content子节点下新建Text,在其中输入“1.热更修复了XXX”
想要让Content自适应,在Text上添加ContentSizeFitter组件,并且选择Vertical Fit为“Preferred Size”。然后要记住将Text组件的RectTransform的Pivot设为X:0,Y:1
HotFixPanel 在Scripts——UGUI——Panel文件夹下新建HotFixPanel
文件
1 2 3 4 5 6 7 8 9 10 11 12 using UnityEngine;using UnityEngine.UI;public class HotFixPanel : MonoBehaviour { public Image m_Image; public Text m_Text; public Text m_ProcessText; [Header("热更信息界面" ) ] public GameObject m_InfoPanel; public Text m_HotContentText; }
把它挂载到HotFixPanel
上,并将指定的对象拖拽进来。关闭Info对象
将HotFixPanel拖拽到Resources文件夹 内。然后点击选中这个Prefab——右键——离线数据——生成UI离线数据。(不用热更的Prefab其实不需要离线数据,这里只为了演示流程)
HotFixWnd 在Scripts——UGUI——Window文件夹下新建HotFixWnd
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 using System.Collections;using UnityEngine;public class HotFixWnd : WindowBase { private HotFixPanel m_Panel; private float m_SumTime; public override void OnAwake (params object [] args ) { m_SumTime = 0 ; m_Panel = GameObject.GetComponent<HotFixPanel>(); m_Panel.m_Image.fillAmount = 0 ; m_Panel.m_Text.text = string .Format("{0:F}M/S" , 0 ); HotPatchManager.Instance.ServerInfoError += ServerInfoError; HotPatchManager.Instance.ItemDownloadError += ItemError; HotFix(); } public override void OnClose () { HotPatchManager.Instance.ServerInfoError -= ServerInfoError; HotPatchManager.Instance.ItemDownloadError -= ItemError; } void HotFix () { if (Application.internetReachability == NetworkReachability.NotReachable) { GameStart.Instance.OpenCommonWnd("网络链接失败" , "网络链接失败,请检查网络链接是否正常?" , () => { Application.Quit(); }, () => { Application.Quit(); }); } else { CheckVersion(); } } void CheckVersion () { HotPatchManager.Instance.CheckVersion(hot => { if (hot) { GameStart.Instance.OpenCommonWnd("热更确定" , string .Format("当前版本为{0},有{1:F}M大小热更包,是否确定下载?" , HotPatchManager.Instance.CurVersion, HotPatchManager.Instance.LoadSumSize/1024.0f ), OnClickConfirmDownload, OnClickCancelDownload); } else { StartOnFinish(); } }); } void OnClickConfirmDownload () { if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer) { if (Application.internetReachability == NetworkReachability.ReachableViaCarrierDataNetwork) { GameStart.Instance.OpenCommonWnd("下载确认" , "是否使用流量下载?" , StartDownload, OnClickCancelDownload); } } else { StartDownload(); } } void OnClickCancelDownload () { Application.Quit(); } void StartDownload () { m_Panel.m_ProcessText.text = "下载中......" ; m_Panel.m_InfoPanel.SetActive(true ); m_Panel.m_HotContentText.text = HotPatchManager.Instance.CurrentPatches.Description; GameStart.Instance.StartCoroutine(HotPatchManager.Instance.StartDownloadAB(StartOnFinish)); } void StartOnFinish () { GameStart.Instance.StartCoroutine(OnFinish()); } IEnumerator OnFinish () { yield return GameStart.Instance.StartCoroutine(GameStart.Instance.StartGame(m_Panel.m_Image, m_Panel.m_ProcessText)); UIManager.Instance.CloseWnd(this ); } public override void OnUpdate () { if (HotPatchManager.Instance.IsStartDownload) { m_SumTime += Time.deltaTime; m_Panel.m_Image.fillAmount = HotPatchManager.Instance.GetProgress(); float speed = (HotPatchManager.Instance.GetLoadSize() / 1024.0f ) / m_SumTime; m_Panel.m_Text.text = string .Format("{0:F}m/s" , speed); } } void ServerInfoError () { GameStart.Instance.OpenCommonWnd("服务器列表获取失败" , "服务器列表获取失败,请检查网络链接是否正常?尝试重新下载?" , CheckVersion, Application.Quit); } void ItemError (string error ) { GameStart.Instance.OpenCommonWnd("资源下载失败" , string .Format("{0}等资源下载失败,请重新尝试下载!" ,error), ANewDownload, Application.Quit); } void ANewDownload () { HotPatchManager.Instance.CheckVersion(hot => { if (hot) { StartDownload(); } else { StartOnFinish(); } }); } }
在GameStart
中注册HotFixWnd
1 2 3 4 5 6 void RegisterUI (){ UIManager.Instance.Register<MenuWnd>(ConstantStr.MENUPANEL); UIManager.Instance.Register<LoadingWnd>(ConstantStr.LOADINGPANEL); UIManager.Instance.Register<HotFixWnd>(ConstantStr.HOTFIXPANEL); }
在ConstantStr
中添加
1 public const string HOTFIXPANEL = "HotFixPanel.prefab" ;
共有界面搭建 在Hierarchy——GameStart——UIRoot——WndRoot中新建GameObject,命名为CommonConfirm。
在CommonConfirm下新建BG(Image),在BG下新建Title(Text),Content(Text),ConfirmButton(Button)、CancelButton(Button)
将此CommonConfirm拖拽到Resources 文件夹下。
在Scripts——UGUI——Item文件夹下新建CommonConfirm
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 using UnityEngine.UI;public class CommonConfirm : ItemBase { public Text m_Title; public Text m_Content; public Button m_ConfirmBtn; public Button m_CancelBtn; public void Show (string title,string content,UnityEngine.Events.UnityAction confirmAction,UnityEngine.Events.UnityAction cancelAction ) { m_Title.text = title; m_Content.text = content; AddButtonClickListener(m_ConfirmBtn, () => { confirmAction(); Destroy(gameObject); }); AddButtonClickListener(m_CancelBtn, () => { cancelAction(); Destroy(gameObject); }); } }
共有界面属于UI里面的Item组件,在上述代码可以看到这种组件不管按什么键都是直接销毁
将此脚本挂载在CommonConfirm下,拖拽好对应的组件后apply一下Prefab。
注意事项 我们的HotFixPanel和共有界面的prefab,都是放在Resources文件夹下的,这意味着除非大版本更新,这两个UI窗口不参与热更。
这是因为当玩家打开客户端时,一定要显示这两个UI,如果热更它们,客户端并不能第一时间应用这两个UI的更新,只能热更结束之后再重新加载,这是很矛盾的。因为这有可能导致我们需要加载两次assetbundle配置,老的配置需要加载完再丢弃。
加入热更之后,我们软件的启动顺序是:
检查服务器是否有热更,有热更先下载热更——加载assetbundle配置,如果热更文件有新的assetbundle配置就使用热更文件的assetbundle配置。
我们已经修改了AssetBundleManager
的LoadAssetBundle
方法,让它优先加载热更包,我们也需要修改LoadAssetBundleConfig
方法,优先加载热更包的assetbundle配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public bool LoadAssetBundleConfig () { #if UNITY_EDITOR if (!ResourceManager.Instance.m_LoadFromAssetBundle) return false ; #endif m_ResourceItemDic.Clear(); string configPath = ABLoadPath + m_ABConfigABName; string hotABPath = HotPatchManager.Instance.ComputeABPath(m_ABConfigABName); configPath = string .IsNullOrEmpty(hotABPath) ? configPath : hotABPath; AssetBundle configAB = AssetBundle.LoadFromFile(configPath); }
我们之前打热更包时,assetbundleconfig并没有出现在热更包里,这是因为我们并没有在Unity编辑器内增删需要热更的文件,也没有修改需要热更文件的名字,我们的资源管理框架只通过名字来确定资源(CRC码是通过资源名生成的)。
共有界面启用方法 共有界面在游戏各个模块都有可能用到,我们在程序入口,也就是GameStart
脚本内添加启用它的方法:
1 2 3 4 5 6 7 8 public void OpenCommonWnd (string title,string content,UnityEngine.Events.UnityAction confirmAction, UnityEngine.Events.UnityAction cancelAction ) { GameObject wndObj = Instantiate(Resources.Load<GameObject>("CommonConfirm" )); wndObj.transform.SetParent(windowRoot,false ); CommonConfirm commonConfirm = wndObj.GetComponent<CommonConfirm>(); commonConfirm.Show(title, content, confirmAction, cancelAction); }
修改GameStart HotFixPanel搭建好之后,进入游戏并初始化时我们可以给Panel提供初始化的信息。这需要我们修改GameStart
部分的逻辑
首先将GameStart
的Start
方法内部的UIManager.Instance.Init(uiRoot, windowRoot, uiCamera, eventSystem);
移动到Awake
方法体中
然后将GameStart
的Start
方法内部的//GameSceneManager.Instance.LoadScene(ConstantStr.MENUSCENE);
移动到HotFixWnd
——OnClose
方法体中
最后在GameStart
中新建public IEnumerator StartGame(Image image,Text text)
方法,将GameStart
的Start
方法内部的代码全都转移到这里来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 protected override void Awake (){ base .Awake(); DontDestroyOnLoad(gameObject); ResourceManager.Instance.Init(this ); ObjectPoolManager.Instance.Init(recyclePoolTrans,sceneTrans); HotPatchManager.Instance.Init(this ); UIManager.Instance.Init(uiRoot, windowRoot, uiCamera, eventSystem); RegisterUI(); } private void Start (){ UIManager.Instance.CreateWindow(ConstantStr.HOTFIXPANEL,isFromResFolder:true ); } public IEnumerator StartGame (Image image,Text text ){ image.fillAmount = 0f ; yield return null ; text.text = "加载本地数据... ..." ; AssetBundleManager.Instance.LoadAssetBundleConfig(); image.fillAmount = 0.2f ; yield return null ; text.text = "加载配置数据... ..." ; LoadConfig(); image.fillAmount = 0.7f ; yield return null ; text.text = "注册组件... ..." ; image.fillAmount = 0.9f ; yield return null ; text.text = "初始化入口... ..." ; GameSceneManager.Instance.Init(this ); image.fillAmount = 1f ; yield return null ; }
最后,在HotFixWnd
的StartOnFinish
方法中开启这个协程
1 2 3 4 5 6 7 8 9 10 11 12 13 void StartOnFinish (){ GameStart.Instance.StartCoroutine(OnFinish()); } IEnumerator OnFinish () { yield return GameStart.Instance.StartCoroutine(GameStart.Instance.StartGame(m_Panel.m_Image, m_Panel.m_ProcessText)); UIManager.Instance.CloseWnd(this ); }