当前位置: 首页 > article >正文

Unity3D仿星露谷物语开发26之创建场景控制管理器

1、目标

创建场景控制管理器,来加载和卸载场景,以实现场景之间的切换。

2、思路

Fade To Back是黑色的过渡场景,透明度逐渐变为1。

Fade To Transparent To Show Scene:黑色消失的过渡场景,透明度逐渐变为0.

事件触发机制:

3、代码编写

(1)优化Enum.cs脚本

创建Scene的枚举类

public enum SceneName
{
    Scene1_Farm,
    Scene2_Field,
    Scene3_Cabin
}

(2)优化EventHandler.cs脚本

添加4个阶段的事件:

// Scene Load Events - in the order they happen
// Before Scene Unload Fade Out Event
public static event Action BeforeSceneUnloadFadeOutEvent;

public static void CallBeforeSceneUnloadFadeOutEvent()
{
    if(BeforeSceneUnloadFadeOutEvent != null)
    {
        BeforeSceneUnloadFadeOutEvent();
    }
}

// Before Scene Unload Event
public static event Action BeforeSceneUnloadEvent;

public static void CallBeforeSceneUnloadEvent()
{
    if( BeforeSceneUnloadEvent != null)
    {
        BeforeSceneUnloadEvent();
    }
}

// After Scene Loaded Event
public static event Action AfterSceneLoadEvent;

public static void CallAfterSceneLoadEvent()
{
    if(AfterSceneLoadEvent != null)
    {
        AfterSceneLoadEvent();
    }
}

// After Scene Load Fade In Event
public static event Action AfterSceneLoadFadeInEvent;

public static void CallAfterSceneLoadFadeInEvent()
{
    if(AfterSceneLoadFadeInEvent != null)
    {
        AfterSceneLoadFadeInEvent();
    }
}

(3)创建SceneControllerManager.cs脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;


public class SceneControllerManager : SingletonMonobehaviour<SceneControllerManager>
{
    private bool isFading;
    [SerializeField] private float fadeDuration = 1f;
    [SerializeField] private CanvasGroup faderCanvasGroup = null;
    [SerializeField] private Image faderImage = null;
    public SceneName startingSceneName;


    // This is the main external point of contact and influence from the rest of the project.
    // This will be called when the player wants to switch scenes.
    // sceneName:目标场景名称
    // spawnPosition: 主角出现的位置
    public void FadeAndLoadScene(string sceneName, Vector3 spawnPosition)
    {
        // If a fade isn't happening then start fading and switching scenes.
        if (!isFading)
        {
            StartCoroutine(FadeAndSwitchScenes(sceneName, spawnPosition));
        }
    }
   
    // This is the coroutine where the 'building blocks' of the script are put together.
    private IEnumerator FadeAndSwitchScenes(string sceneName, Vector3 spawnPosition)
    {
        // Call before scene unload fade out event
        EventHandler.CallBeforeSceneUnloadFadeOutEvent();

        // Start fading to block and wait for it to finish before continuing.
        yield return StartCoroutine(Fade(1f));  // 变黑色

        // Set player position
        Player.Instance.gameObject.transform.position = spawnPosition;

        // Call before scene unload event.
        EventHandler.CallBeforeSceneUnloadEvent();

        // Unload the current active scene.
        yield return SceneManager.UnloadSceneAsync(SceneManager.GetActiveScene().buildIndex);

        // Start loading the given scene and wait for it to finish.
        yield return StartCoroutine(LoadSceneAndSetActive(sceneName));

        // Call after scene load event
        EventHandler.CallAfterSceneLoadEvent();

        // Start fading back in and wait for it to finish before exiting the function.
        yield return StartCoroutine(Fade(0f)); // 变白色

        // Call after scene load fade in event
        EventHandler.CallAfterSceneLoadFadeInEvent();

    }

    private IEnumerator Fade(float finalAlpha)
    {
        // Set the fading flag to true so the FadeAndSwitchScenes coroutine won't be called again.
        isFading = true;

        // Make sure the CanvasGroup blocks raycasts into the scene so no more input can be accepted.
        faderCanvasGroup.blocksRaycasts = true;

        // Calculate how fast the CanvasGroup should fade based on it's current alpha,
        // it's final alpha and how long it has to change between the two.
        float fadeSpeed = Mathf.Abs(faderCanvasGroup.alpha - finalAlpha) / fadeDuration;

        // while the CanvasGroup hasn't reached the final alpha yet...
        while( !Mathf.Approximately(faderCanvasGroup.alpha, finalAlpha))
        {
            // ... move the alpha towards it's target alpha.
            faderCanvasGroup.alpha = Mathf.MoveTowards(faderCanvasGroup.alpha, finalAlpha,
                fadeSpeed * Time.deltaTime);

            // Wait for a frame then continue.
            yield return null;
        }

        // Set the flag to false since the fade has finished.
        isFading = false;

        // Stop the CanvasGroup from blocking raycasts so input is no longer ignored.
        faderCanvasGroup.blocksRaycasts = false;
    }


    private IEnumerator LoadSceneAndSetActive(string sceneName)
    {
        // Allow the given scene to load over serval frames and add it to the already
        // loaded scenes (just the Persistent scene at this point).
        yield return SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);

        // Find the scene that was most recently loaded (the one at the last index of the loaded scenes).
        Scene newlyLoadedScene = SceneManager.GetSceneAt(SceneManager.sceneCount - 1);

        // Set the newly loaded scene as the active scene(this marks it as the one to be unloaded next).
        SceneManager.SetActiveScene(newlyLoadedScene);
    }

    private IEnumerator Start()
    {
        // Set the initial alpha to start off with a block screen.
        faderImage.color = new Color(0f, 0f, 0f, 1f);
        faderCanvasGroup.alpha = 1f;

        // Start the first scene loading and wait for it to finish
        yield return StartCoroutine(LoadSceneAndSetActive(startingSceneName.ToString()));

        // If this event has any subscribers, call it
        EventHandler.CallAfterSceneLoadEvent();

        // Once the scene is finished loading, start fading in
        StartCoroutine(Fade(0f));
    }

}
  • CanvasGroup的alpha值,其子元素的alpha是子元素alpha和对应CanvasGroup中alpha的乘积。
  • 周期函数中的Start方法可以直接当成协程用,使用方法也很简单,直接将返回值void改成IEnumerator即可。Update,LateUpdate,FixedUpdate,Awake,OnEnable等都不能这么用

(4)优化UIInventorySlot.cs脚本

对于如下的代码:

parentItem = GameObject.FindGameObjectWithTag(Tags.ItemsParentTransform).transform;、

在切换场景的过程中,我们并不能总是找到他们。因为场景加载需要时间,可能场景还没加载完成就在取值了。

所以需要订阅加载完成的事件,在事件中处理元素的获取。

添加如下代码:

private void OnDisable()
{
    EventHandler.AfterSceneLoadEvent -= SceneLoaded;
}

private void OnEnable()
{
    EventHandler.AfterSceneLoadEvent += SceneLoaded;
}

public void SceneLoaded()
{
    parentItem = GameObject.FindGameObjectWithTag(Tags.ItemsParentTransform).transform;
}

同时注释掉Start()中的元素获取方法。

(5)优化SwitchConfineBoundingShape.cs脚本

元素获取基于同样的理由,需要在场景确认加载完毕后才能去获取。

using UnityEngine;
using Cinemachine;

public class SwitchConfine : MonoBehaviour
{
    //private void Start()
    //{
    //    SwitchBoundingShape();
    //}

    private void OnEnable()
    {
        EventHandler.AfterSceneLoadEvent += SwitchBoundingShape;
    }

    private void OnDisable()
    {
        EventHandler.AfterSceneLoadEvent -= SwitchBoundingShape;
    }

    /// <summary>
    /// Switch the collider that cinemachine uses to define edges of the screen
    /// </summary>
    private void SwitchBoundingShape()
    {
        // Get the polygon collider on the 'boundsconfiner' gameobject which is used by Cinemachine to prevent the camera going beyond the screen edges
        PolygonCollider2D polygonCollider2D = GameObject.FindGameObjectWithTag(Tags.BoundsConfiner).GetComponent<PolygonCollider2D>();

        CinemachineConfiner cinemachineConfiner = GetComponent<CinemachineConfiner>();  

        cinemachineConfiner.m_BoundingShape2D = polygonCollider2D;

        // since the confiner bounds have changed need to call this to clear the cache
        cinemachineConfiner.InvalidatePathCache();
    }
}

(6)优化Player.cs脚本

修改PlayerTestInput()方法。

 private void PlayerTestInput()
 {
     // Trigger Advance Time
     if (Input.GetKeyDown(KeyCode.T))
     {
         TimeManager.Instance.TestAdvanceGameMinute();
     }

     // Trigger Advance Day
     if (Input.GetKeyDown(KeyCode.G))
     {
         TimeManager.Instance.TestAdvanceGameDay();
     }

     // Test scene unload / load
     if (Input.GetKeyDown(KeyCode.L))
     {
         SceneControllerManager.Instance.FadeAndLoadScene(SceneName.Scene1_Farm.ToString(), transform.position);
     }
 }

4、创建对象

 (1)FadeImage对象

在PersistentScene -> UI -> MainGameUICanvas -> UICanvasGroup下新建空对象命名为FadeImage。

作用:让屏幕变黑再变透明。

创建完之后,stretch让其铺满屏幕。

添加Image组件。

将颜色调整为黑色,并且将A置为0。

添加Canvas Group组件,并且勾选 Ignore Parent Groups选项,忽略父物体的管理。

(2)SceneControllerManager对象

在PersistentScene下新建空对象命名为SceneControllerManager。

添加SceneControllerManager组件,并且给元素赋值。

5、改变脚本的执行顺序

在多个脚本中,我们无法确定哪个脚本的Start()方法先被执行。

现在,在其他游戏对象开始访问第一个场景之前,我们需要确保SceneControllerManager对象已经在其Start()方法中加载了第一个场景。

Edit -> Project Settings -> Script Execution Order 

在Default Time之前保证SceneControllerManager的脚本被执行。

6、卸载场景

卸载已有的Scene1_Farm场景,

点击该场景后选择 Unload Scene。

因为SceneControllerManager的Start方法会加载当前场景一次。

执行程序后,按"L"键后效果如下:


http://www.kler.cn/a/528387.html

相关文章:

  • jstat命令详解
  • unity学习24:场景scene相关生成,加载,卸载,加载进度,异步加载场景等
  • 23.Word:小王-制作公司战略规划文档❗【5】
  • 实验八 JSP访问数据库
  • 我的求职面经:(2)C++中空指针请使用nullptr不要使用NULL
  • C++ 静态变量static的使用方法
  • 蓝桥杯刷题DAY1:前缀和
  • 项目练习:重写若依后端报错cannot be cast to com.xxx.model.LoginUser
  • C++ Primer 自定义数据结构
  • Linux-CentOS的yum源
  • 阶段一Python核心编程:走进Python编程的世界001
  • nth_element函数——C++快速选择函数
  • C语言:数组的介绍与使用
  • Excel 技巧23 - 在Excel中用切片器做出查询效果(★★★)
  • 4 [危机13小时追踪一场GitHub投毒事件]
  • javaEE-6.网络原理-http
  • Arduino可以做哪些有意思的项目
  • Java泛型深度解析(JDK23)
  • 牛客网第k小(详解)c++
  • 分布式微服务系统架构第90集:现代化金融核心系统
  • 深度学习之“缺失数据处理”
  • 青少年编程与数学 02-008 Pyhon语言编程基础 11课题、字典与循环语句
  • nginx目录结构和配置文件
  • 交错定理和切比雪夫节点的联系与区别
  • 【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.27 线性代数王国:矩阵分解实战指南
  • XML DOM 解析器