前言
类的介绍:
SceneControllerManager:
用于进行Scene开启关闭以及是否使SceneActive,从而达到不同Scene之间的转换
SceneSave:
用于存储每个Scene应该存储和读取的的SceneData
所有应该存储/读取的数据都可以放入其中
GameObjectSave
一个Key为SceneName,Value为SceneSave的Dictionary,用于快速读取每个场景的SceneData
ISaveable:
一个接口,内部涵盖了GUID(Globally Unique Identifier),全局唯一标识符,便于对所有继承于ISaveable的类所构造的唯一对象的管理。
GUID();
GameObjectSave();
存储系统,包含SceneData的数据集合,此处使用Dictionary数据结构存取。
IsaveableRegister();
IsaveableDerigister();
二者用于向SaveLoadManger的List进行注册和注销,使此ISaveable接入SaveLoadManager的SaveList之中,使其Store和Restore函数 有效/无效。
IsaveableStore();//将该场景的数据存储入本身的GameObjectSave中。
IsaveableRestore();//将上次存储的场景恢复,并且覆写该场景现有的数据。
代码:
SceneControllerManager
Unity的SceneLoad具备两种形式,一种是Single,即LoadScene时只打开选中的Scene,另外一种是Additive,可以同时加载多个Scene,但只能有一个ActiveScene,ActiveScene就是Engine用来添加新gameobject的Scene,所以在loadScene进行替换是,还得将其设置为Active。
下方有许多AsyncOperation 类的方法,需要使用协程方法来调用,不然编译器会发出警告。
总结:
在场景加载完毕前不会进行Fade0的协程,这样就确保了fade结束后场景已经完全加载,这就是采用大量异步方法来编写SceneControllerManager的原因。
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SceneControllerManager : SingletonMonoBehaviour<SceneControllerManager>
{
private bool isFading;
[SerializeField] private Image fadeImage;
[SerializeField] private CanvasGroup fadeImageCanvasGroup;
[SerializeField] private float fadeDuration;
[SerializeField] private SceneName startSceneName;
private IEnumerator Fade(float fadeFinalAlpha)
{
isFading=true;
//场景加载时禁止角色输入的点击
fadeImageCanvasGroup.blocksRaycasts=true;
float fadeSpeed=Mathf.Abs(fadeImageCanvasGroup.alpha-fadeFinalAlpha)/fadeDuration;
while(!Mathf.Approximately(fadeImageCanvasGroup.alpha,fadeFinalAlpha))
{
fadeImageCanvasGroup.alpha=Mathf.MoveTowards(fadeImageCanvasGroup.alpha,fadeFinalAlpha,Time.deltaTime*fadeSpeed);
yield return null;
}
fadeImageCanvasGroup.blocksRaycasts=false;
isFading=false;
}
private IEnumerator FadeAndSwitchScene(string SceneName)
{
EventHandler.CallBeforeSceneLoadEvent();
yield return StartCoroutine(Fade(1f));
// SaveLoadManager.Instance.StoreCurrentScene(SceneName);
//Switch Scene
//1.Unload the current scene 此处需要使用使用yield return 应为 UnloadSceneAsync 是一个 AsyncOperation,属于异步方法,需要使用yield return,不然编译器会发出警告
yield return SceneManager.UnloadSceneAsync(SceneManager.GetActiveScene().buildIndex);
//2.Begin the LoadingNextScene IEnumator
StartCoroutine(LoadSceneAndSetActive(SceneName));
// SaveLoadManager.Instance.RestoreCurrentScene(SceneName);
yield return StartCoroutine(Fade(0f));
EventHandler.CallAfterSceneLoadEvent();
}
private IEnumerator LoadSceneAndSetActive(string SceneName)
{
//Additive/Single?
yield return SceneManager.LoadSceneAsync(SceneName,LoadSceneMode.Additive);
//How the SceneLoad in the SceneList?
Scene newlyLoadedScene=SceneManager.GetSceneAt(SceneManager.sceneCount-1);
//Why we should set active scene here?
yield return SceneManager.SetActiveScene(newlyLoadedScene);
}
private IEnumerator Start()
{
fadeImage.color=new Color(0,0,0,1f);
fadeImageCanvasGroup.alpha=1f;
yield return LoadSceneAndSetActive(startSceneName.ToString());
//使用委托
EventHandler.CallAfterSceneLoadEvent();
//覆写场景Data
// SaveLoadManager.Instance.RestoreCurrentScene();
//提供画面
StartCoroutine(Fade(0f));
}
public void FadeAndLoadScene(string SceneName)
{
if(!isFading)
StartCoroutine(FadeAndSwitchScene(SceneName));
}
}
SaveLoadManager:
提供四个函数接口,和两个固有访问器
public interface ISaveable
{
string ISavableUniqueID{get; set;}
GameObjectSave ISavableGameObjectSave{get;set;}
void ISaveableRegister();
void ISaveableDerigister();
void ISaveableRestoreScene(string sceneName);
void ISaveableStoreScene(string sceneName);
}
SceneSave:
SceneSave中可以为任何东西,可以是地图的数据,也可以是游戏物件的数据,用数据存取StandardPrefab,也就是数据需要挂载的GameObject,就可以完成对该数据类型物件的存取与覆写。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// This class is used to contain the details that Scene should save and read
/// </summary>
public class SceneSave : MonoBehaviour
{
public List<Item> itemList;
}
GenerateGUID
newGUID属于System的固有函数
using UnityEngine;
//ExecuteAlways使此代码在编译器运行时也可以同时运行
[ExecuteAlways]
public class GenerateGUID : MonoBehaviour
{
[SerializeField]
private string _guid;
public string GUID{
get{
return _guid;
}
set{
_guid=value;
}
}
private void Awake()
{
//Only populate in the editor,make sure the gameobject is not playing in the scene
if(!Application.IsPlaying(gameObject))
{
//Ensure the object has a guaranted unique id
if(_guid=="")
{
//Asign GUID 此处调用System自带的NewGuid函数
_guid=System.Guid.NewGuid().ToString();
}
}
}
}
通过Isaveable接口构造一个SceneItemManager范例
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//使GenerateGUID成为这个Class的绝对附加类,即在挂载SceneItemManger时,会同时附加GenerateGUID
[RequireComponent(typeof(GenerateGUID))]
public class SceneItemManger : SingletonMonoBehaviour<SceneItemManger>,ISaveable
{
[SerializeField] private Transform ItemParentTransform;
private GameObjectSave itemGameObjectSave;
public GameObjectSave ISavableGameObjectSave
{
get{
return itemGameObjectSave;
}
set{
itemGameObjectSave=value;
}
}
[SerializeField]
private string _guid;
public string ISavableUniqueID
{
get{
return _guid;
}
set{
_guid=value;
}
}
public void AfterSceneLoadEvent()
{
ItemParentTransform=GameObject.FindGameObjectWithTag(Tags.ItemParentTransform).transform;
}
protected override void Awake()
{
base.Awake();
_guid=this.GetComponent<GenerateGUID>().GUID;
ISavableGameObjectSave=new GameObjectSave();
}
private void OnEnable()
{
EventHandler.AfterSceneLoadEvent+=AfterSceneLoadEvent;
ISaveableRegister();
}
private void OnDisable()
{
EventHandler.AfterSceneLoadEvent-=AfterSceneLoadEvent;
ISaveableDerigister();
}
public void ISaveableRegister()
{
SaveLoadManager.Instance.ISaveableObjectList.Add(this);
}
public void ISaveableDerigister()
{
SaveLoadManager.Instance.ISaveableObjectList.Remove(this);
}
public void ISaveableStoreScene(string sceneName)
{
SceneSave currentSceneSave;
if(ISavableGameObjectSave.sceneData.TryGetValue(sceneName,out currentSceneSave))
{
//do sth.
}
}
public void ISaveableRestoreScene(string sceneName)
{
SceneSave currentSceneSave;
if(ISavableGameObjectSave.sceneData.TryGetValue(sceneName,out currentSceneSave))
{
//do sth.
}
}
public void DestoryAllTheItemInCurrentScene()
{
Item[] itemsInScene=GameObject.FindObjectsOfType<Item>();
foreach(Item item in itemsInScene)
{
Destroy(item.gameObject);
}
}
}
最后,使用PlayerTest调用SceneChange
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerTest : MonoBehaviour
{
// [SerializeField] private GameObject ReUsePoolPrefab=null;
private void Update()
{
// if(Input.GetMouseButtonDown(1))
// {
// Vector3 mouseScreenPosition=Input.mousePosition;
// Vector3 mouseWorldPosition=Camera.main.ScreenToWorldPoint(new Vector3(mouseScreenPosition.x,mouseScreenPosition.y,-Camera.main.transform.position.z));
// // PoolManger.Instance.ReUsePoolGameObject(ReUsePoolPrefab,mouseWorldPosition,Quaternion.identity);
// EventHandler.CallParticleUse(ReUsePoolPrefab,mouseWorldPosition,Quaternion.identity);
// }
if(Input.GetKeyDown(KeyCode.Q))
{
SceneControllerManager.Instance.FadeAndLoadScene(SceneName.Scene2.ToString());
}
else if(Input.GetKeyDown(KeyCode.E))
{
SceneControllerManager.Instance.FadeAndLoadScene(SceneName.Scene1.ToString());
}
}
}
SceneChange效果
注:可以使用IEnumator重写Start函数