"Lost Island 2" Game Development Framework Development Diary: Scene Switching and Fade Effects

M_Studio's tutorial: ["Lost Island 2" Game Framework Development 01: Realizing Scene Conversion|Unity Tutorial]

Build the scene

Use the downloaded materials to build scenes H1-H4 and H2A, and the permanent scene Persistent, drag and drop all the scenes to the Hierachy panel, only load the H1 and Persistent scenes, use H1 as the active scene, and switch the scenes in each scene Add an empty object with 2DCollider to the icon, keep only the camera in the Persistent scene, delete the cameras in the rest of the scene, set the background color of the camera to black, set the Size to 5.4, and the display size of the Game panel to 1920*1080, In this way, the picture can be displayed exactly on the screen.

insert image description here
insert image description here

insert image description here

screenplay

Implementation logic: When the corresponding icon is clicked, the corresponding collision body is detected, and if the label of the game object of the collision body is the specified type label, the scene conversion method is called. Since there is a scene conversion in each scene, the class of the scene conversion is a singleton, which is easy to call
insert image description here

Create scripts: SingleTon.cs, CursorManager.cs, Teleport.cs, TransitionManager.cs.

// SingleTon.cs 
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : Singleton<T> // 类型约束,类T必须继承Singleton
{
    
    
    // 单例
    protected static T instance;

    // 设置只读属性
    public static T Instance
    {
    
    
        get {
    
     return instance; }
    }

    private void Awake()
    {
    
    
        if (instance == null)
        {
    
    
            instance = (T)this;
        }
        else
        {
    
    
            Destroy(this);
        }

        DontDestroyOnLoad(this);
    }

    // 是否创建实例
    private bool IsInitial()
    {
    
    
        return instance != null;
    }

    // 销毁单例
    protected void OnDestroy()
    {
    
    
        if (instance == this)
        {
    
    
            instance = null;
        }
    }
}
CursorManager.cs脚本编辑:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CursorManager : MonoBehaviour
{
    
    
    // 将鼠标点击的屏幕坐标转换成世界坐标
    private Vector2 mouseWorldPoint => Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
    // 如果鼠标点击了
    private bool isClick;
    private Collider2D coll;

    // Update is called once per frame
    void Update()
    {
    
    
        isClick = ObjectAtMouseClick();
        if (isClick && Input.GetMouseButton(0))
        {
    
    
            // 根据游戏对象的不同标签来判断执行什么操作
            coll = ObjectAtMouseClick();
            ClickAction(coll.gameObject);
        }

    }

    public void ClickAction(GameObject clickObject)
    {
    
    
        switch (coll.gameObject.tag)
        {
    
    
            case "Teleport":

                // 获取对象上的Teleport组件,执行场景切换方法
                Teleport t = clickObject.GetComponent<Teleport>();
                t?.TransitionScene();
                break;
            default:
                break;
        }
    }

    public Collider2D ObjectAtMouseClick()
    {
    
    
        return Physics2D.OverlapPoint(mouseWorldPoint);
    }
}

Add the script Teleport.cs to the switching icon object in each scene, click "Add Open Scenes" in the FIle->Build Setting panel to add all the scenes

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum SceneName
{
    
    
    H1,
    H2,
    H3,
    H4,
    H2A,
}
public class Teleport : MonoBehaviour
{
    
    
    // 现在处于的场景
    public SceneName from;
    // 要切换到的场景
    public SceneName to;

    public void TransitionScene()
    {
    
    
        // 场景切换
        TransitionManager.Instance.TransitionScene(from, to);
    }
}

scene switch

Edit the script TransitionManager.cs to achieve scene switching without adding fade in and fade out, and the code when the fade in and fade out effect is not realized:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class TransitionManager : Singleton
{

// 启动协程
public void TransitionScene(SceneName from, SceneName to)
{

    // 如果实现切换场景淡入淡出效果则不能进行场景切换
    StartCoroutine(SwitchScene(from, to));

}
/// <summary>
/// 切换场景
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
/// <returns></returns>
public IEnumerator SwitchScene(SceneName from, SceneName to)
{

    // 屏蔽鼠标操作
    anim.gameObject.GetComponent<Image>().raycastTarget = true;


    // 卸载当前场景
    yield return AsyncOperation unloadScene = SceneManager.UnloadSceneAsync(from.ToString());
   
    // 加载要加载的场景
     yield return AsyncOperation loadScene = SceneManager.LoadSceneAsync(to.ToString(), LoadSceneMode.Additive);
    
    // 激活场景
    Scene newScene = SceneManager.GetSceneByName(to.ToString()); // 通过场景名称来获取场景
    SceneManager.SetActiveScene(newScene);
}

}

Scene switching is normal
, but after adding the code of fade-in and fade-out related functions (below),



public IEnumerator SwitchScene(SceneName from, SceneName to)
    {
    
    
        // 当设置好场景后就加入Fade,1是全黑,0是透明
        yield return Fade(1);

        // 卸载当前场景
        yield return SceneManager.UnloadSceneAsync(from.ToString());
        // 加载要加载的场景
        yield return SceneManager.LoadSceneAsync(to.ToString(), LoadSceneMode.Additive);

        // 激活场景
        // Scene newScene = SceneManager.GetSceneByName(to.ToString()); // 通过场景名称来获取场景
        Scene newScene = SceneManager.GetSceneByBuildIndex((int)to); // 通过场景在build setting中的序号来获取场景,使用这个也会经常报错

        if (newScene == null)
        {
    
    
            SceneManager.SetActiveScene(newScene);
        }
        // 恢复场景可见
        yield return Fade(0);

        // 鼠标点击有效
        cp.blocksRaycasts = false;
    }
    
    /// <summary>
    /// 设置转场时的遮罩颜色渐变
    /// </summary>
    /// <param name="alpha"></param>
    private IEnumerator Fade(float alpha)
    {
    
    
        // 正在开始淡入特效
        isFade = true;
        // 屏蔽所有的鼠标点击
        cp.blocksRaycasts = true;

        // 计算淡入速度, 现在的alpha与目标alpha的差值再除以持续时间得到Fade速度
        float speed = Mathf.Abs(cp.alpha - alpha) / fadeDuaration;
        while (Mathf.Approximately(cp.alpha, alpha))
        {
    
    
            // 线性差值来实现Fade
            cp.alpha = Mathf.Lerp(cp.alpha, alpha, speed * Time.deltaTime);
            yield return null;
        }
        // 淡出后就重新设置isFade
        isFade = false;
    }
   

An error occurs: ArgumentException: Scene to unload is invalid
Specifically, the obtained Scene in this code "yield return SceneManager.UnloadSceneAsync(from.ToString());" is invalid

Consult information found online:

When using SceneManager.LoadSceneAsync, set allowSceneActivation to pay attention to the characteristics after using it: after using it,
Yield Return
AsyncOperation will never return, which means that the program will be stuck on this sentence, and if there are multiple AsyncOperations in Queuing, note that if the ongoing one is not finished, the next one will not start. When using UnloadSceneAsync, if there is only one active scene and the other scene has not been loaded because allowSceneActivation is set to false, then UnloadSceneAsync will not execute normally. And it will return empty.
In addition, if the progress of an AsyncOperation is 0, there is usually an AsyncOperation that has not been executed before it. Pay special attention to the characteristics when using it. For details, see Unity Scripting API search allowSceneActivation, LoadSceneAsync
and UnloadSceneAsync
carefully look at them Instructions and usage examples. ——————————————— Copyright statement: This article is the original article of CSDN blogger "Peter_Gao_", following the CC
4.0 BY-SA copyright agreement, please attach the original source link and this statement for reprinting . Original link: https://blog.csdn.net/qq_42672770/article/details/116023292

I thought it was because AsyncOperation was not executed, so I set the following code to detect the execution and then run it


    public void TransitionScene(SceneName from, SceneName to)
    {
    
    
       
        // 如果实现切换场景淡入淡出效果则不能进行场景切换
        if (!isFade)
            StartCoroutine(SwitchScene2(from, to));
    }
    /// <summary>
    /// 切换场景
    /// </summary>
    /// <param name="from"></param>
    /// <param name="to"></param>
    /// <returns></returns>
    public IEnumerator SwitchScene2(SceneName from, SceneName to)
    {
    
    
         yield return Fade(1);
        // 卸载当前场景
        AsyncOperation unloadScene = SceneManager.UnloadSceneAsync(from.ToString());
        while (!unloadScene.isDone)
        {
    
    
            yield return null;
        }

        // 加载要加载的场景
        AsyncOperation loadScene = SceneManager.LoadSceneAsync(to.ToString(), LoadSceneMode.Additive);
        loadScene.allowSceneActivation = false;
        while (!loadScene.isDone)
        {
    
    
            yield return null;
            loadScene.allowSceneActivation = true;

        }

        // 激活场景
        Scene newScene = SceneManager.GetSceneByName(to.ToString()); // 通过场景名称来获取场景
        // Scene newScene = SceneManager.GetSceneByBuildIndex((int)to); // 通过场景在build setting中的序号来获取场景

        bool isActive = SceneManager.SetActiveScene(newScene);
        while (isActive)
        {
    
    
            // 恢复场景可见
            yield return Fade(0);

            // 鼠标点击有效
            cp.blocksRaycasts = false;
        }
    }
   

Click to switch scenes and an error occurs:

ArgumentException: Scene to unload is invalid
UnityEngine.SceneManagement.SceneManagerAPI.UnloadSceneAsyncByNameOrIndex
(System.String sceneName, System.Int32 sceneBuildIndex, System.Boolean
immediately, UnityEngine.SceneManagement.UnloadSceneOptions options,
System.Boolean& outSuccess) (at <9c2babab467c4d89beebe2f694dc090f>:0)
UnityEngine.SceneManagement.SceneManager.UnloadSceneNameIndexInternal
(System.String sceneName, System.Int32 sceneBuildIndex, System.Boolean
immediately, UnityEngine.SceneManagement.UnloadSceneOptions options,
System.Boolean& outSuccess) (at <9c2babab467c4d89beebe2f694dc090f>:0)
UnityEngine.SceneManagement.SceneManager.UnloadSceneAsync
(System.String sceneName) (at <9c2babab467c4d89beebe2f694dc090f>:0)
TransitionManager+d__5.MoveNext () (at
Assets/Scripts/Transition/TransitionManager.cs:82)

In the end, I can't find the reason, as long as "yield return Fade(1);" and "Scene newScene = SceneManager.GetSceneByBuildIndex((int)to); // Get the scene by the serial number of the scene in the build setting, using this will often Report an error, another suggestion in the video tutorial is to use StatCrountine to start Fade, after the modification, the scene switching is normal, no error is reported but there is no fade in and fade out effect

   public void TransitionScene(SceneName from, SceneName to)
    {
    
    
        // 进行淡入淡出
        StartCoroutine(Fade(1));

        // 如果实现切换场景淡入淡出效果则不能进行场景切换
        if (!isFade)
            StartCoroutine(SwitchScene(from, to));
    }

    /// <summary>
    /// 切换场景
    /// </summary>
    /// <param name="from"></param>
    /// <param name="to"></param>
    /// <returns></returns>
    public IEnumerator SwitchScene(SceneName from, SceneName to)
    {
    
    
        // 卸载当前场景
        AsyncOperation unloadScene = SceneManager.UnloadSceneAsync(from.ToString());
        while (!unloadScene.isDone)
        {
    
    
            yield return null;
        }
        // 加载要加载的场景
        AsyncOperation loadScene = SceneManager.LoadSceneAsync(to.ToString(), LoadSceneMode.Additive);
        loadScene.allowSceneActivation = false;
        while (!loadScene.isDone)
        {
    
    
            yield return null;
            loadScene.allowSceneActivation = true;
        }

        // 激活场景
        Scene newScene = SceneManager.GetSceneByName(to.ToString()); // 通过场景名称来获取场景

        bool isActive = SceneManager.SetActiveScene(newScene);
        while (isActive)
        {
    
    
            // 恢复场景可见
            yield return Fade(0);

            // 鼠标点击有效
            cp.blocksRaycasts = false;
        }
    }

Fade in and fade out transition implementation

In the end, this problem could not be solved, so the fade-in and fade-out effect was realized in other ways-playing fade-in and fade-out animations. Reference tutorial: [Unity Tutorial: Making Scene Switching Transition Effects]
Making panel fade-in and fade-out animations is two animations that change the color attribute alpha of the panel's image component - fadeIn and fadeOut, and create an empty animation in the animator , and then make the Entry point to the empty animation. When the empty animation setting parameter fadeIn is true, the fade-in animation is played, and when fadeOut=true, the fade-out animation is played. The animation duration can be set to 0.1s.

Re-edit the code. For the code setting of the animation, directly obtain the state machine of the panel directly in the TransitionManager, and use the code setting to play the fade-in and fade-out animation



using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;// 可以访问到panel的UI

// TransitionManager.cs
public class TransitionManager : Singleton<TransitionManager>
{
    
    
    // panel上的淡入淡出的动画状态机,在面板中拖拽添加
    public Animator anim;
    // 等待时长
    public float waitTime = 0.1f;
    WaitForSeconds wait;

    private void Start()
    {
    
    
        // 较频繁使用的引用类型作
        wait = new WaitForSeconds(waitTime);
    }

    // 启动协程
    public void TransitionScene(SceneName from, SceneName to)
    {
    
    

        // 如果实现切换场景淡入淡出效果则不能进行场景切换
        StartCoroutine(SwitchScene(from, to));

    }

    // private IEnumerator Fade()
    // {
    
    
    //     anim.SetBool("fadeIn", true);
    //     anim.SetBool("fadeOut", false);

    //     anim.gameObject.GetComponent<Image>().raycastTarget = true;

    //     yield return wait;
    // }


    /// <summary>
    /// 切换场景
    /// </summary>
    /// <param name="from"></param>
    /// <param name="to"></param>
    /// <returns></returns>
    public IEnumerator SwitchScene(SceneName from, SceneName to)
    {
    
    
        // 播放淡入动画
        anim.SetBool("fadeIn", true);
        anim.SetBool("fadeOut", false);

        // 屏蔽鼠标操作
        anim.gameObject.GetComponent<Image>().raycastTarget = true;


        // 卸载当前场景
        AsyncOperation unloadScene = SceneManager.UnloadSceneAsync(from.ToString());
        while (!unloadScene.isDone)
        {
    
    
            yield return wait;
        }

        // 加载要加载的场景
        AsyncOperation loadScene = SceneManager.LoadSceneAsync(to.ToString(), LoadSceneMode.Additive);
        loadScene.allowSceneActivation = false;
        while (!loadScene.isDone)
        {
    
    
            yield return null;
            loadScene.allowSceneActivation = true;

        }
        // 播放淡出动画
        loadScene.completed += OnLoadedScene;

        // 激活场景
        Scene newScene = SceneManager.GetSceneByName(to.ToString()); // 通过场景名称来获取场景
        SceneManager.SetActiveScene(newScene);
    }

    private void OnLoadedScene(AsyncOperation async)
    {
    
    
        anim.SetBool("fadeOut", true);
        anim.SetBool("fadeIn", false);
        anim.gameObject.GetComponent<Image>().raycastTarget = false;
    }
}

Later, I learned about AsyncOperation.completed in the case, and I feel that I can try new methods.
In addition, I found one thing in the second episode: the collider that clicks the button to switch scenes does not check IsTrigger. For what other bloggers know about IsTrigger: link .

Guess you like

Origin blog.csdn.net/weixin_44848852/article/details/127127519