using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using BITKit.IO; using BITKit.UX; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UIElements; using YooAsset; using Debug = UnityEngine.Debug; namespace BITKit { public class BITFramework : MonoBehaviour { public static BITFramework Singleton { get; private set; } private static System.Diagnostics.Stopwatch Stopwatch; [RuntimeInitializeOnLoadMethod] private static void Reload() { Stopwatch = new Stopwatch(); #if UNITY_EDITOR #else SplashScreen.Stop(SplashScreen.StopBehavior.StopImmediate); #endif } [SerializeReference, SubclassSelector] private IReference addressableName; [SerializeReference, SubclassSelector] private IReference packageName = new Reference("DefaultPackage"); [SerializeReference, SubclassSelector] private IRemoteServices remoteServices; [SerializeReference, SubclassSelector] private IBuildinQueryServices buildinQueryServices; [SerializeField] private Optional loadEntryScene; [SerializeField] private UIDocument document; [SerializeField] private bool isOffline; [SerializeField] private bool IsEditorSimulateMode; [SerializeField] private UnityEvent,Action> afterInitialize=new(); [SerializeField] private UnityEvent onGetCurrentVersion; [UXBindPath("progress-bar",true)] private ProgressBar _progressBar; [UXBindPath("progress-fill",true)] private VisualElement _progressFill; [UXBindPath("progress-label")] private Label _progressLabel; private Action _setProgressValue; private Action _setProgressLabel; private async void Start() { Singleton = this; try { #if UNITY_EDITOR #else IsEditorSimulateMode = false; #endif UXUtils.Inject(this); if (_progressBar is not null) { _setProgressValue = value => _progressBar.value = value; }else if (_progressFill is not null) { _setProgressValue = value => _progressFill.style.width = new Length() { value = value*100, unit = LengthUnit.Percent }; } _setProgressLabel = value => _progressLabel.text = value; _setProgressValue?.Invoke(0f); _setProgressLabel?.Invoke("正在初始化资源系统..."); var stopwatch = new Stopwatch(); stopwatch.Start(); DontDestroyOnLoad(gameObject); // 初始化资源系统 if (YooAssets.Initialized is false) YooAssets.Initialize(); // 创建默认的资源包 var package = YooAssets.TryGetPackage(packageName.Value) ?? YooAssets.CreatePackage(packageName.Value); // 设置该资源包为默认的资源包,可以使用YooAssets相关加载接口加载该资源包内容。 YooAssets.SetDefaultPackage(package); if (package.InitializeStatus is EOperationStatus.Succeed) { YooAssets.DestroyPackage(package.PackageName); } InitializeParameters initParameters = new HostPlayModeParameters { BuildinQueryServices = buildinQueryServices, RemoteServices = remoteServices }; if (Application.platform is RuntimePlatform.WebGLPlayer) { initParameters = new WebPlayModeParameters() { BuildinQueryServices = buildinQueryServices, RemoteServices = remoteServices }; } #if UNITY_EDITOR if (IsEditorSimulateMode) { var editorParameters = new EditorSimulateModeParameters { SimulateManifestFilePath = EditorSimulateModeHelper.SimulateBuild("ScriptableBuildPipeline", packageName.Value) }; initParameters = editorParameters; } else { switch (UnityEditor.EditorUserBuildSettings.activeBuildTarget) { case UnityEditor.BuildTarget.WebGL: initParameters = new WebPlayModeParameters() { BuildinQueryServices = buildinQueryServices, RemoteServices = remoteServices }; break; } } #endif if (isOffline&& IsEditorSimulateMode is false) { initParameters = new OfflinePlayModeParameters(); } InitializationOperation initOperation = null; try { initOperation = package.InitializeAsync(initParameters); var text = $"正在初始化资源系统...,使用{initParameters.GetType().Name}"; _progressLabel.text =text; Debug.Log(text); } catch (Exception e) { _progressLabel.text =e.Message; initParameters = new HostPlayModeParameters { BuildinQueryServices = buildinQueryServices, RemoteServices = remoteServices }; initOperation = package.InitializeAsync(initParameters); } while (initOperation.IsDone is false) { await UniTask.NextFrame(destroyCancellationToken); //_progressBar.value =initOperation.Progress; _setProgressValue?.Invoke(initOperation.Progress); } try { if (isOffline is false) { _progressLabel.text = "正在更新资源包版本..."; var operation = package.UpdatePackageVersionAsync(); try { onGetCurrentVersion.Invoke(package.GetPackageVersion()); } catch (Exception e) { BIT4Log.LogException(e); } while (operation.IsDone is false) { await UniTask.NextFrame(destroyCancellationToken); //_progressBar.value = operation.Progress; _setProgressValue?.Invoke(operation.Progress); } if (operation.Status == EOperationStatus.Succeed) { //更新成功 string packageVersion = operation.PackageVersion; Debug.Log($"Updated package Version : {packageVersion}"); _progressLabel.text = "正在更新资源包清单..."; var manifestOperation = package.UpdatePackageManifestAsync(packageVersion); await manifestOperation.ToUniTask(); if (manifestOperation.Status == EOperationStatus.Succeed) { //更新成功 int downloadingMaxNum = 10; int failedTryAgain = 3; var downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain); //没有需要下载的资源 if (downloader.TotalDownloadCount != 0) { //需要下载的文件总数和总大小 int totalDownloadCount = downloader.TotalDownloadCount; long totalDownloadBytes = downloader.TotalDownloadBytes; downloader.OnDownloadProgressCallback = (totalDownloadCount, currentDownloadCount, totalDownloadBytes, currentDownloadBytes) => { BIT4Log.Log( $"{totalDownloadCount},{currentDownloadCount},{totalDownloadBytes},{currentDownloadBytes}"); //下载进度 float progress = (float)currentDownloadBytes / totalDownloadBytes; _setProgressValue?.Invoke(progress); _setProgressLabel?.Invoke($"{currentDownloadBytes}/{totalDownloadBytes}"); }; downloader.OnDownloadErrorCallback = (fileName, error) => { _setProgressLabel?.Invoke(error); }; //注册回调方法 // downloader.OnDownloadErrorCallback = OnDownloadErrorFunction; // downloader.OnDownloadProgressCallback = OnDownloadProgressUpdateFunction; // downloader.OnDownloadOverCallback = OnDownloadOverFunction; // downloader.OnStartDownloadFileCallback = OnStartDownloadFileFunction; //开启下载 downloader.BeginDownload(); await downloader; //检测下载结果 if (downloader.Status != EOperationStatus.Succeed) { //更新失败 Debug.LogError(operation.Error); _progressLabel.text = operation.Error; } } } else { //更新失败 Debug.LogError(operation.Error); _progressLabel.text = operation.Error; } } else { //更新失败 Debug.LogError(operation.Error); _progressLabel.text = operation.Error; } } } catch (Exception e) { BIT4Log.LogException(e); _progressLabel.text = e.Message; } _setProgressLabel?.Invoke("资源系统初始化完成,后处理中..."); afterInitialize.Invoke(_setProgressValue,_setProgressLabel); _setProgressLabel?.Invoke("资源系统初始化完成,后处理完成"); if (addressableName is not null) { _progressLabel.text ="正在初始化Framework"; var frameworkHandle = YooAssets.LoadAssetAsync(addressableName.Value); while (frameworkHandle.IsDone is false) { await UniTask.NextFrame(destroyCancellationToken); //_progressBar.value=frameworkHandle.Progress; _setProgressValue?.Invoke(frameworkHandle.Progress); } var framework = Instantiate(frameworkHandle.AssetObject); DontDestroyOnLoad(framework); _progressLabel.text="已加载完成"; BIT4Log.Log("BITFramework加载完成,耗时:" + stopwatch.ElapsedMilliseconds + "ms"); } stopwatch.Stop(); YooAssetUtils.RegisterPackage(packageName.Value); YooAssetUtils.RegisterResourcePackage(package); await AfterInit(package); } catch (Exception e) { await UniTask.SwitchToMainThread(); _progressBar.value =0; _progressLabel.text = e.Message; } } private async UniTask AfterInit(ResourcePackage package) { if (loadEntryScene.Allow) { _progressLabel.text="正在加载场景..."; try { await package.LoadSceneAsync(loadEntryScene.Value); _progressLabel.text="场景加载完成..."; } catch (Exception e) { Debug.LogException(e); await UniTask.NextFrame(); _progressLabel.text = e.Message; } } else { _progressLabel.text="未找到默认场景,正在加载场景[1]"; SceneManager.LoadScene(1); } if (document) Destroy(document); } private void OnDestroy() { #if UNITY_EDITOR YooAssets.Destroy(); #endif } } [Serializable] public sealed class GameQueryServices : IBuildinQueryServices { public bool Query(string packageName, string fileName) { // 注意:fileName包含文件格式 return StreamingAssetsHelper.FileExists(packageName, fileName); } public bool Query(string packageName, string fileName, string fileCRC)=>Query(packageName,fileName); } [Serializable] public sealed class LocalQueryServices : IBuildinQueryServices { public readonly string Path; public LocalQueryServices(string path) { Path = path; } public bool Query(string packageName, string fileName, string fileCRC) { return File.Exists(System.IO.Path.Combine(Path, fileName)); } } /// /// 远端资源地址查询服务类 /// [Serializable] public sealed class RemoteServices : IRemoteServices { [SerializeField] private string _defaultHostServer; [SerializeField] private string _fallbackHostServer; public RemoteServices(){} public RemoteServices(string defaultHostServer, string fallbackHostServer) { _defaultHostServer = defaultHostServer; _fallbackHostServer = fallbackHostServer; } string IRemoteServices.GetRemoteMainURL(string fileName) { return $"{_defaultHostServer}/{fileName}"; } string IRemoteServices.GetRemoteFallbackURL(string fileName) { return $"{_fallbackHostServer}/{fileName}"; } } }