杂项功能——场景的加载与切换

本文介绍了如何在Unity中使用ScriptableObject和AssetReferences封装场景数据,创建可切换的场景类型,实现场景之间的无缝过渡,包括传送点触发、屏幕淡入淡出效果,以及利用JsonUtility进行场景数据的保存和加载。
摘要由CSDN通过智能技术生成

        简单完成一些场景相关的功能。

        一:封装场景数据

        其中SceneType 包含两种:主菜单场景  和 游戏场景,以实现不同逻辑。

[CreateAssetMenu(menuName = "Game Scene/GameSceneSO")]
public class GameSceneSO : ScriptableObject
{
    public AssetReference sceneReference;

    public SceneType sceneType;

}

        二:传送点

        记录了要传送的场景以及位置。在被触碰时即触发传送。

public class TransitionPoint : MonoBehaviour
{
    [SerializeField] private GameSceneSO sceneToGo;

    [SerializeField] private Vector3 positionToGo;

    [SerializeField] private bool hideAfterTransition;

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("Player"))
            TransitionAction();
    }

    private void TransitionAction()
    {
        SceneLoader.Instance.SceneTransition(sceneToGo, positionToGo, true);
        if(hideAfterTransition) gameObject.SetActive(false);
    }
}

        三:场景管理类

        1:记录了游戏刚开始时加载的场景,同时保留了将要加载场景的数据。

        2:场景加载前和加载后要做的事情使用全局事件实现。

        3:具体的加载和卸载使用AssetReference下的LoadSceneAsync 和 UnLoadScene(一定要配套使用,不能一个用 Addressables.LoadSceneAsync(),另一个用 AssetReference的UnLoadScene() )。

        4:利用协程完成场景的异步加载过程:(1)等待屏幕fadein (2)等待卸载当前的场景 (3)屏幕FadeOut + 新场景的加载。

[DefaultExecutionOrder(-50)]
public class SceneLoader : Singleton<SceneLoader>,ISavable
{
    public GameSceneSO firstScene;
    
    public float fadeDuration;

    [SerializeField] private Transform player;

    [SerializeField] private GameSceneSO restScene;

    [SerializeField] private GameSceneSO currentScene;

    private GameSceneSO sceneToGo;

    private Vector3 posToGo;

    private bool needFade;

    public bool IsLoading { get; private set; }

    protected override void Awake()
    {
        base.Awake();
        sceneToGo = firstScene;
        currentScene = firstScene;
        LoadNewScene();
    }

    private void Start()
    {
        AudioManager.Instance.PlayBgmBySceneType(currentScene.sceneType);
        MouseManager.Instance.SetMouseCursorBySceneType(currentScene.sceneType);
        GlobalEvent.CallEnterMenuSceneEvent();
    }
    
    private void OnEnable()
    {
        (this as ISavable).RegisterSaveData();
        GlobalEvent.newGameEvent += TransitionToRestScene;
    }

    private void OnDisable()
    {
        (this as ISavable).UnRegisterSaveData();
        GlobalEvent.newGameEvent -= TransitionToRestScene;
    }

    public SceneType GetCurrentSceneType()
    {
        if (currentScene != null) return currentScene.sceneType;
        return SceneType.Menu;
    }
    
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sceneToGo">前往的场景</param>
    /// <param name="posToGo">新场景的初始的位置</param>
    /// <param name="needFade">是否需要淡入淡出</param>
    public void SceneTransition(GameSceneSO sceneToGo, Vector3 posToGo, bool needFade)
    {
        if (IsLoading) return;
        //设定加载的状态和信息
        IsLoading = true;
        this.sceneToGo = sceneToGo;
        this.posToGo = posToGo;
        this.needFade = needFade;
        GlobalEvent.CallBeforeSceneLoadEvent();
        StartCoroutine(SceneTransition());
    }

    private IEnumerator SceneTransition()
    {
        if (needFade)
        {
            UIManager.Instance.fadeCanvas.Fadein(fadeDuration);
        }
        yield return new WaitForSeconds(fadeDuration);
        
        //等待当前场景卸载完
        yield return currentScene.sceneReference.UnLoadScene();
        
        //加载新的场景
        LoadNewScene();
    }

    private void LoadNewScene()
    {
        var loadingOperation=sceneToGo.sceneReference.LoadSceneAsync(LoadSceneMode.Additive, true);
        loadingOperation.Completed += OnLoadCompleted;
    }

    private void OnLoadCompleted(AsyncOperationHandle<SceneInstance> obj)
    {
        if (obj.Status == AsyncOperationStatus.Succeeded)
        {
            currentScene = sceneToGo;
            //player.gameObject.SetActive(currentScene.sceneType != SceneType.Persistent);
            IsLoading = false;
            GlobalEvent.CallAfterSceneLoadEvent(posToGo);
            if (needFade)
            {
                UIManager.Instance.fadeCanvas.FadeOut(fadeDuration);
            }
            SceneManager.SetActiveScene(obj.Result.Scene);
            if(currentScene.sceneType!=SceneType.Persistent)AudioManager.Instance.PlayBgmBySceneType(currentScene.sceneType);
            MouseManager.Instance.SetMouseCursorBySceneType(currentScene.sceneType);
            UIManager.Instance.SetPanelAfterLoad(currentScene.sceneType);
            
            if (currentScene.sceneType == SceneType.Menu) GlobalEvent.CallEnterMenuSceneEvent();
            else GlobalEvent.CallExitMenuSceneEvent();
        }
        else
        {
            Debug.LogError("Failed to load scene: " + obj.OperationException.Message);
        }
        
    }

    private void TransitionToRestScene()
    {
        SceneTransition(restScene, Vector3.zero, true);
    }


    #region Save And Load
    public string GetDataID()
    {
        return "Scene";
    }

    public void SaveData(Data data)
    {
        data.SaveScene(currentScene);
        data.playerPos = new SerializeVector3(player.position);
    }

    public void LoadData(Data data)
    {
        IsLoading = false;
        SceneTransition(data.LoadScene(), data.playerPos.ToVector3(), true);
    }
    

    #endregion


}

四:屏幕淡入淡出的实现

        新建一个单独的Fade Canvas,里面放置有一张Image(黑色),利用DoTween下的DOBlendableColor函数调整Image的透明度即可。(也可以直接写个协程就行)

public class FadeCanvas : MonoBehaviour
{
    public Image fadeImage;

    //实现画面的渐入:即将透明度从0变成1
    public void Fadein(float duration)
    {
        Color targetColor = fadeImage.color;
        targetColor.a = 1;
        fadeImage.DOBlendableColor(targetColor, duration);
    }
    
    //实现画面的渐出:即将透明度从1变成0
    public void FadeOut(float duration)
    {
        Color targetColor = fadeImage.color;
        targetColor.a = 0;
        fadeImage.DOBlendableColor(targetColor, duration);
    }
    
    
}

补充:如何保存场景?   保存系统基于 数据的保存与加载

        在Data中额外添加以下数据:一个是场景,一个是人物的坐标位置。

 public string sceneToSave;

    public SerializeVector3 playerPos;

        在Data中还单独实现了 保存 和 加载的方法。

注意:整个DataManager采用的是 Newtonsoft.Json ,而这里采用的是Unity自带的JsonUtility,原因在于Newtonsoft.Json在序列化GameSceneSO 后,无法正确还原(可能Newtonsoft.Json不支持Unity中的AssetReference,而JsonUtility支持)。

public void SaveScene(GameSceneSO gameScene)
    {
        sceneToSave = JsonUtility.ToJson(gameScene);
    }

    public GameSceneSO LoadScene()
    {
         var gameScene = ScriptableObject.CreateInstance<GameSceneSO>();
         JsonUtility.FromJsonOverwrite(sceneToSave, gameScene);
         return gameScene;
    }
    

        在SceneLoader完成Save 和 Load 的接口

 public void SaveData(Data data)
    {
        data.SaveScene(currentScene);
        data.playerPos = new SerializeVector3(player.position);
    }

    public void LoadData(Data data)
    {
        isLoading = false;
        SceneTransition(data.LoadScene(), data.playerPos.ToVector3(), true);

    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值