using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; namespace A2W { public class SceneLoader : Singleton { ITransition currentTransition; // 当前的过场实例 Canvas canvas; public bool isPreLoading { get; set; } = false; // 预加载中 public Action OnFinished; // 转场结束 bool isLoading = false;// 现在是否可以转场 WaitForSeconds leastTime; public void Init() { GameObject go = new GameObject(); go.name = "LoadingCanvas"; go.transform.SetParent(transform, false); canvas = go.AddComponent(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvas.sortingOrder = 1000; CanvasScaler canvasScaler = go.AddComponent(); canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; canvasScaler.referenceResolution = new Vector2(Screen.width, Screen.height); } /// /// 使用转场加载场景 /// /// 场景名称 /// 转场方式 /// 是否有预加载 /// 至少等待一定时间 public void LoadScene(string SceneName, ITransition transition = null, bool hasPreload = false, float leastSecond = 0.2f) { // 如果现在不能转换场景直接返回 if (isLoading) return; if (canvas is null) { Init(); } isPreLoading = hasPreload; leastTime = new WaitForSeconds(leastSecond); currentTransition = transition; // 如果有设置过场就设置过场 if (transition is not null) { currentTransition.Init(canvas.transform); } // 开始转换场景 StartCoroutine(LoadLevel(SceneName)); } /// /// 转换场景并且使用过场动画 /// /// 场景名称 /// private IEnumerator LoadLevel(string levelName) { // 异步加载场景 AsyncOperation loading = SceneManager.LoadSceneAsync(levelName); // 不允许场景加载完后直接转换 loading.allowSceneActivation = false; // 现在不再能转换场景 isLoading = true; if (currentTransition is not null) { // 开始过场 currentTransition.Begin(); // 等待一帧 // 理由再下面有解释,但其实这里本来不需要,因为检查动画前还夹着一个检查加载的过程。基本不会在一帧内就加载完 // 但是保险起见还是在播放动画后延迟一帧 yield return null; // 至少等待一定时间 yield return leastTime; // 等待动画播放完成 while (!currentTransition.IsDone()) yield return null; } // 等待场景加载几乎完成 while (loading.progress < 0.899) yield return null; // 允许场景加载完成 loading.allowSceneActivation = true; // 等待场景加载彻底完成 while (loading.progress != 1) yield return null; //等待预加载完毕 while (isPreLoading) yield return null; if (currentTransition is not null) { // 结束过场 currentTransition.Finish(); // 等待一帧 // 因为我发现如果在开始动画后不等待一帧的话,第二个动画其实还没开始播放, // 后面检测动画完成检测的就是第一个动画,就起不到检测第二个动画的作用。 yield return null; // 至少等待一定时间 yield return leastTime; // 等待动画播放完成 while (!currentTransition.IsDone()) yield return null; } // 可以继续转换场景 isLoading = false; OnFinished?.Invoke(); } } }