using System; using System.Collections; using System.Collections.Generic; using Cysharp.Threading.Tasks; using PhxhSDK.AOT; using PhxhSDK.AOT.SimpleLoader; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.SceneManagement; public class GameLauncher : MonoBehaviour { [Serializable] private class DownloadContent { public List catalogs = new(); } const string START_SCENE_NAME = "Assets/Scenes/StartScene.unity"; //记录在playerPres里的需要下载的catalogs的ID const string DOWNLOAD_CATALOGS_ID = "DownloadCatalogs"; //最大能容忍的下载卡住时长,一旦超过则重新开始下载 // private const float DOWNLOAD_STUCK_TIME_MAX = 10; #if USE_HCLR private HybridCLRHelper _hybridCLRHelper; #endif //version update private AsyncOperationHandle _downloadOP; private UI_VersionUpdate _versionUpdateUI; private Type _versionUpdaterType; // private float _downloadProgress; // private float _downloadStuckTime; private List _KeysNeedToDownload = new(); //此对象里保存了需要下载的catalog,每次获取新的catalog会将此对象保存到手机上,如果在下载的过程中关闭了游戏,下次打开还能拿到catalog继续下载 private DownloadContent _downloadContent = new(); private bool HasContentToDownload => _downloadContent != null && _downloadContent.catalogs != null && _downloadContent.catalogs.Count > 0; public bool enableHotUpdate = true; private void Start() { Launch(); } private async UniTask Launch() { #if UNITY_EDITOR enableHotUpdate = false; #endif #if USE_YOO await YooAssetHelper.Init(); #endif Debug.Log($"Launch Game! enableHotUpdate:{enableHotUpdate}"); await VersionCheck(); if (HasContentToDownload) await VersionUpdate(); if (enableHotUpdate) { #if USE_HCLR Debug.Log("需要热更新,开始加载程序集"); _hybridCLRHelper = new HybridCLRHelper(); _hybridCLRHelper.SetDllList(AOTGenericReferences.PatchedAOTAssemblyList); #if USE_YOO _hybridCLRHelper.RegistLoader(new SimpleLoader_YooAsset()); #else _hybridCLRHelper.RegistLoader(new SimpleLoader_Addressables()); #endif await _hybridCLRHelper.LoadAssemblies(); Debug.Log("加载程序集结束!"); #endif } await EnterGame(); } private IEnumerator VersionCheck() { //todo 如果包体版本不一致则提示用户去app store重新下载 var checkUpdateOP = Addressables.CheckForCatalogUpdates(false); Debug.Log("start checkUpdateOP"); yield return checkUpdateOP; if (checkUpdateOP.Status == AsyncOperationStatus.Succeeded) { _downloadContent.catalogs = checkUpdateOP.Result; if (HasContentToDownload) { Debug.Log("new version on server"); //如果服务器上的catalog和本地不一致,则覆盖上一次需要下载的被内容 var jsonStr = JsonUtility.ToJson(_downloadContent); PlayerPrefs.SetString(DOWNLOAD_CATALOGS_ID, jsonStr); PlayerPrefs.Save(); } else { if (PlayerPrefs.HasKey(DOWNLOAD_CATALOGS_ID)) { Debug.Log("there are some contents remains from last downloading"); var jsonStr = PlayerPrefs.GetString(DOWNLOAD_CATALOGS_ID); JsonUtility.FromJsonOverwrite(jsonStr, _downloadContent); } } if (HasContentToDownload) { var updateCatalogOP = Addressables.UpdateCatalogs(_downloadContent.catalogs, false); yield return updateCatalogOP; if (updateCatalogOP.Status == AsyncOperationStatus.Succeeded) { _KeysNeedToDownload.Clear(); foreach (var resourceLocator in updateCatalogOP.Result) { _KeysNeedToDownload.AddRange(resourceLocator.Keys); } } else { Debug.LogError($"Update catalog failed!exception:{updateCatalogOP.OperationException.Message}"); } Addressables.Release(updateCatalogOP); } } else { Debug.LogError($"CheckUpdate failed!exception:{checkUpdateOP.OperationException.Message}"); } Addressables.Release(checkUpdateOP); yield return SimpleLoader_Addressables.ReloadAddressableCatalog(); Debug.Log($"版本检查结束,是否有需要下载的内容:{HasContentToDownload}"); } private IEnumerator VersionUpdate() { yield return OpenVersionUpdateUI(); yield return Download(); Debug.Log($"版本更新结束!"); } //通过加载场景打开版本更新界面 private IEnumerator OpenVersionUpdateUI() { _versionUpdateUI = FindObjectOfType(true); if (_versionUpdateUI == null) { Debug.LogError("cant find UI_VersionUpdate"); } _versionUpdateUI.gameObject.SetActive(true); return null; } //下载资源 private IEnumerator Download() { var downloadSizeOp = Addressables.GetDownloadSizeAsync((IEnumerable)_KeysNeedToDownload); yield return downloadSizeOp; Debug.Log($"download size:{downloadSizeOp.Result / (1024f * 1024f)}MB"); if (downloadSizeOp.Result > 0) { //_downloadStuckTime = 0; Addressables.Release(downloadSizeOp); // //下载经常在100%的时候卡住,参考:https://forum.unity.com/threads/addressables-1-14-2-downloaddependenciesasync-does-not-complete-randomly.966671/ // //倒数第二个回答 // var asyncLoadResources = Addressables.LoadResourceLocationsAsync((IEnumerable)_KeysNeedToDownload, // Addressables.MergeMode.Union, null); // IList dependencyLoadList = new List(); // yield return asyncLoadResources; // foreach (var item in asyncLoadResources.Result) // { // dependencyLoadList.Add(item); // } // // Addressables.Release(asyncLoadResources); // // _downloadOP = // Addressables.DownloadDependenciesAsync(dependencyLoadList); _downloadOP = Addressables.DownloadDependenciesAsync((IEnumerable)_KeysNeedToDownload, Addressables.MergeMode.Union, false); _versionUpdateUI.SetParams(_downloadOP); // while (!_downloadOP.IsDone) // { // RefreshDownLoadStatus(); // if (!CheckIfNeedReDownload()) // yield return 0; // else // { // //下载会经常卡在99%,此时还剩下很少的资源要下载,可以直接放弃下载,进入游戏 // //经过尝试,上述方法行不通,_downloadOp不能被终止,如果直接release的话会导致后续逻辑报错 // Debug.LogError($"下载卡住超过时间上限,放弃下载!"); // yield return _downloadOP; // } // } yield return _downloadOP; if (_downloadOP.Status == AsyncOperationStatus.Succeeded) Debug.Log($"下载完毕!"); else Debug.LogError($"Download Update Content Failed! exception:{_downloadOP.OperationException.Message} \r\n {_downloadOP.OperationException.StackTrace}"); Addressables.Release(_downloadOP); } //清除需要下载的内容 Debug.Log($"delete key:{DOWNLOAD_CATALOGS_ID}"); PlayerPrefs.DeleteKey(DOWNLOAD_CATALOGS_ID); } // private void RefreshDownLoadStatus() // { // var currDownloadProgress = _downloadOP.GetDownloadStatus().Percent; // if (Math.Abs(currDownloadProgress - _downloadProgress) < float.Epsilon) // _downloadStuckTime += Time.deltaTime; // else // _downloadStuckTime = 0; // _downloadProgress = currDownloadProgress; // //Debug.Log($"下载进度:{_downloadProgress} 下载卡顿时间:{_downloadStuckTime}"); // } // // private bool CheckIfNeedReDownload() => _downloadStuckTime >= DOWNLOAD_STUCK_TIME_MAX; private IEnumerator EnterGame() { var op = Addressables.LoadSceneAsync(START_SCENE_NAME, LoadSceneMode.Single); yield return op; if (op.Status != AsyncOperationStatus.Succeeded) { Debug.LogError( $"load scene failed,exception:{op.OperationException.Message} \r\n {op.OperationException.StackTrace}"); } } }