很久以前的设计…这是单例的设计,需要挂载在游戏对象上。
/***
* Title:
* 场景管理器
* Description:
* 实现场景的各种加载
* 公开异步操作数给Loading界面使用
*/
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelManager : SingletonMono<LevelManager>
{
int SceneIndex = 0;
public AsyncOperation LoadingProcess;
// Use this for initialization
void Start()
{
GameObject.DontDestroyOnLoad(this);
}
public static void InitLoad()
{
new GameObject("LevelMgr").AddComponent<LevelManager>();
}
public void LoadNext()
{
SceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneIndex++;
SceneManager.LoadScene(SceneIndex);
}
public void LoadBack()
{
SceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneIndex--;
if (SceneIndex >= 0)
SceneManager.LoadScene(SceneIndex);
}
public void LoadAgain()
{
SceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(SceneIndex);
}
public void LoadScene(int index)
{
SceneManager.LoadScene(index);
}
public void LoadScene(string sceneName)
{
SceneManager.LoadScene(sceneName);
}
public IEnumerator LoadSceneAsync(string sceneName)
{
LoadingProcess = SceneManager.LoadSceneAsync(sceneName);
yield return LoadingProcess;
}
/// <summary>
/// 输入一个场景的名称,
/// 先跳转到Loading场景在跳转进目标场景中
/// </summary>
/// <param name="sceneName"></param>
public void LoadSceneWithLoading(string sceneName)
{
Data.NextSceneName = sceneName;
LoadScene(Data.LoadingScene);
}
public IEnumerator LoadNextAsync()
{
SceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneIndex++;
LoadingProcess = SceneManager.LoadSceneAsync(SceneIndex);
yield return LoadingProcess;
}
public IEnumerator LoadBackAsync()
{
SceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneIndex--;
LoadingProcess = SceneManager.LoadSceneAsync(SceneIndex);
yield return LoadingProcess;
}
public IEnumerator LoadAgainAsync()
{
LoadingProcess = SceneManager.LoadSceneAsync(SceneIndex);
yield return LoadingProcess;
}
private void OnDisable()
{
StopAllCoroutines();
}
public void QuitGame()
{
Application.Quit();
}
}
这是我现在的设计,状态机设计,我现在的设计是能不挂载游戏物体的脚本死都不挂。
SceneMgr
using System;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Scene
{
/// <summary>
/// 场景管理器
/// 状态设计模式,因为很多子系统都有可能会设计到场景跳转,例如UI的按钮点击
/// </summary>
public class SceneMgr
{
public SceneMgr()
{
_LoadingScene = CreateSceneState<LoadingSceneState>("LoadingScene",false,false) as LoadingSceneState;
_CurScene = CreateSceneState<InitSceneState>("InitScene",false,false);
_CurScene.SceneBegin();
_CurScene.IsBegin = true;
}
/// <summary>
/// 特殊Loading场景
/// </summary>
private LoadingSceneState _LoadingScene;
/// <summary>
/// 当前场景状态
/// </summary>
private ISceneState _CurScene;
/// <summary>
/// 异步操作数
/// 查看当前场景是否已经加载完成
/// </summary>
public AsyncOperation LoadingProcess;
/// <summary>
/// 输入一个场景的名称,
/// 先跳转到Loading场景在跳转进目标场景中
/// </summary>
/// <param name="sceneName"></param>
private void LoadSceneWithLoading(ISceneState scene)
{
_LoadingScene.SetNextScene(scene);
//同步加载到Loading场景
LoadScene(_LoadingScene);
}
/// <summary>
/// 不通过LoadingScene简单同步加载
/// </summary>
/// <param name="sceneName"></param>
private void LoadScene(ISceneState scene)
{
//旧状态离开
if (_CurScene != null)
_CurScene.SceneExit();
//赋值新状态
_CurScene = scene;
//先加载对应的AB包
if (scene.IsABLoad)
{
GameManager.Instance.LoadSceneAB(scene.SceneName);
}
SceneManager.LoadScene(scene.SceneName);
}
/// <summary>
/// 异步加载,返回异步操作数,协程操作
/// </summary>
/// <param name="scene"></param>
/// <returns></returns>
private void LoadSceneAsync(ISceneState scene)
{
_CurScene = scene;
//先加载对应的AB包
if (scene.IsABLoad)
{
GameManager.Instance.LoadSceneAB(scene.SceneName);
}
//使用异步操作数直接赋值监控就行了,好像不用迭代
Debug.Log($"异步加载场景+{scene.SceneName}");
LoadingProcess = SceneManager.LoadSceneAsync(scene.SceneName);
LoadingProcess.allowSceneActivation = false; //异步加载
}
/// <summary>
/// 创建一个场景状态
/// </summary>
/// <returns></returns>
public ISceneState CreateSceneState<T>(string sceneName,bool isABLoad,bool isWithLoading) where T:ISceneState,new()
{
T t = new T();
t.Init(isWithLoading, isABLoad, sceneName, this);
return t;
}
/// <summary>
/// 加载场景
/// </summary>
/// <param name="scene">场景状态类</param>
/// <param name="isLoading">是否已经通过Loading界面加载</param>
public void SetSceneState(ISceneState scene,bool isLoading)
{
if (scene.IsWithLoading)
{
if (!isLoading)
{
LoadSceneWithLoading(scene);
}
else
{
LoadSceneAsync(scene);
}
}
else
{
//加载新场景
LoadScene(scene);
}
}
/// <summary>
/// 加载场景
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sceneName">场景名称</param>
/// <param name="isABLoad">场景是否在AB包中</param>
/// <param name="isWithLoading">是否属于异步Loading界面加载</param>
/// <param name="isLoading">是否已经通过Loading界面加载</param>
public void SetSceneState<T>(string sceneName, bool isABLoad, bool isWithLoading, bool isLoading) where T:ISceneState,new()
{
ISceneState scene = CreateSceneState<T>(sceneName,isABLoad,isWithLoading);
SetSceneState(scene,isLoading);
}
public void Update()
{
//等待场景加载完成
if (LoadingProcess != null)
{
if(!LoadingProcess.allowSceneActivation)
{
//Loading界面更新
_LoadingScene.SceneUpdate();
return;
}
else
{
//LoadingScene的离开
_LoadingScene.SceneExit();
LoadingProcess = null;
}
}
//新状态初始化
if (!_CurScene.IsBegin)
{
_CurScene.IsBegin = true;
_CurScene.SceneBegin();
}
//场景状态更新
_CurScene.SceneUpdate();
}
}
}
ISceneState
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Scene
{
public abstract class ISceneState
{
/// <summary>
/// 场景名称
/// </summary>
public string SceneName;
/// <summary>
/// 是否通过AB加载场景
/// </summary>
public bool IsABLoad;
/// <summary>
/// 是否初始化完成
/// </summary>
public bool IsBegin;
/// <summary>
/// 是否要通过Loading方式加载到新场景中
/// </summary>
public bool IsWithLoading;
/// <summary>
/// 状态上下文
/// </summary>
protected SceneMgr _Context;
public void Init(bool isWithLoading,bool isABLoad,string sceneName,SceneMgr context)
{
this.IsBegin = false;
this.IsWithLoading = isWithLoading;
this.IsABLoad = isABLoad;
this.SceneName = sceneName;
this._Context = context;
}
/// <summary>
/// 场景开始
/// </summary>
public virtual void SceneBegin()
{
}
/// <summary>
/// 场景进入
/// </summary>
public virtual void SceneUpdate()
{
GameManager.Instance.Update();
}
/// <summary>
/// 场景离开时
/// </summary>
public virtual void SceneExit()
{
//场景离开释放内存资源
GameManager.Instance.ReleaseAll();
}
}
}
比较特殊的场景Loading异步加载演示
using UIFW;
namespace Scene
{
public class LoadingSceneState : ISceneState
{
/// <summary>
/// Loading之后的一个场景
/// </summary>
private ISceneState _NextScene;
/// <summary>
/// 改变Loading场景接下来要跳转的场景
/// </summary>
/// <param name="scene"></param>
public void SetNextScene(ISceneState scene)
{
this._NextScene = scene;
}
public override void SceneBegin()
{
base.SceneBegin();
//打开LoadingUIFrm,下一个关卡需要异步进入
GameManager.Instance.UIManager.OpenUIFrm("LoadingUIFrm");
//加载到下一个关卡,只能使用set
GameManager.Instance.SetSceneState(_NextScene, true);
}
public override void SceneExit()
{
_NextScene = null;
base.SceneExit();
}
}
}
需要配合指定的UI
using UnityEngine;
using UnityEngine.UI;
namespace UIFW
{
public class LoadingUIFrm : INormalUserInterface
{
/// <summary>
/// 滑动条
/// </summary>
Slider slider_Loading;
/// <sumary>
/// 加载进度值文本
/// </summary>
Text txt_LoadingValue;
/// <summary>
/// 进度值 不要随便修改由unity控制的值
/// </summary>
float _Progress;
public override void SetObject()
{
//生成父类
base.TraParent = base._UIManager.TraNormal;
}
public void Awake()
{
//获得UI组件
slider_Loading = UITool.GetComponentFromChildNode<Slider>(transform, "sliderLoading");
txt_LoadingValue = UITool.GetComponentFromChildNode<Text>(transform, "txtLoadingValue");
}
public void Update()
{
_Progress = GameManager.Instance.SceneMgr.LoadingProcess.progress;
if (Mathf.Approximately(_Progress, .9f))
{
_Progress = 1;
}
//更新UI
UpdateUI(_Progress);
}
private void UpdateUI(float value)
{
if (slider_Loading.value < value && !Mathf.Approximately(slider_Loading.value, value))
{
slider_Loading.value += .01f;
}
if (txt_LoadingValue != null)
txt_LoadingValue.text = Mathf.Round(slider_Loading.value * 100) + "%";
if (Mathf.Approximately(slider_Loading.value, 1f))
{
GameManager.Instance.SceneMgr.LoadingProcess.allowSceneActivation = true;
}
}
public override void Release()
{
base.Release();
//引用置空
txt_LoadingValue = null;
slider_Loading = null;
}
}
}