麦田物语学习笔记:创建TransitionManager控制人物场景切换

发布于:2025-02-10 ⋅ 阅读:(39) ⋅ 点赞:(0)

基本流程

        制作场景之间的切换

1.代码思路

        (1)为了实现不同场景切换,并且保持当前的persistentScene一直存在,则需要一个Manager去控制场景的加载和卸载,并且在加载每一个场景之后,都要将当前的场景Set Active Scene,保证其为激活的场景,在卸载的时候也可以方便调用当前激活的场景,然后把它卸载掉

        (2)每次创建一个新的场景之后都要去Build Settings当中,将所有创建的场景都添加到这个列表当中

        (3)由于游戏的场景是逐一叠加的,那么异步加载的模式就为Additive(在原有的场景当中叠加)

        (4)场景之间切换是通过碰撞触发的,需要指定切换后的场景和坐标

        (5)切换场景需要用一个新的脚本来控制,它所需要的变量有目标场景名,目标坐标,要注意在卸载场景的时候人物是不能被玩家控制的,还需要注意的是,在传送过程中选中物品的情况(举起物品),也就是说要在卸载场景之前,恢复player的基本动画(AnimatorOverride.cs),取消物品的高亮显示(InventoryUI.cs)

2.代码实现

TransitionManager.cs

namespace FuliFarm.Transition
{
    public class TransitionManager : MonoBehaviour
    {
        //游戏开始的场景
        public string startSceneName = string.Empty;

        private void Start()
        {
            StartCoroutine(LoadSceneSetActive(startSceneName));
        }

        private void OnEnable()
        {
            EventHandler.TransitionEvent += OnTransitionEvent;
        }

        private void OnDisable()
        {
            EventHandler.TransitionEvent -= OnTransitionEvent;
        }

        private void OnTransitionEvent(string sceneToGo, Vector3 positionToGo)
        {
            StartCoroutine(Transition(sceneToGo, positionToGo));
        }

        /// <summary>
        /// 卸载一个场景,加载另外一个场景
        /// </summary>
        /// <param name="sceneName">目标场景</param>
        /// <param name="targetPosition">目标位置</param>
        /// <returns></returns>
        private IEnumerator Transition(string sceneName, Vector3 targetPosition)
        {
            //呼叫卸载场景之前要做的事
            EventHandler.CallBeforeSceneUnloadEvent();

            yield return SceneManager.UnloadSceneAsync(SceneManager.GetActiveScene());

            yield return LoadSceneSetActive(sceneName);

            //移动人物坐标
            EventHandler.CallMoveToPosition(targetPosition);

            //呼叫加载场景之后的事件
            EventHandler.CallAfterSceneLoadedEvent();
        }

        /// <summary>
        /// 加载场景并设置为激活
        /// </summary>
        /// <param name="sceneName">场景名</param>
        /// <returns></returns>
        private IEnumerator LoadSceneSetActive(string sceneName)
        {
            //协成当中所有的加载场景都是异步加载
            yield return SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);

            Scene newScene = SceneManager.GetSceneAt(SceneManager.sceneCount - 1);//序号从0开始,而数量(count)是从1开始的,所以减1

            SceneManager.SetActiveScene(newScene);
        }
    }
}

  Teleport.cs

namespace FuliFarm.Transition
{
    public class Teleport : MonoBehaviour
    {
        public string sceneToGo;

        public Vector3 positionToGo;

        private void OnTriggerEnter2D(Collider2D other)
        {
            if (other.CompareTag("Player"))
            {
                //把目标位置和目标场景传给TransitionManager中,然后调用协成Transition,就去到了对应的场景
                EventHandler.CallTransitionEvent(sceneToGo, positionToGo);
            }
        }
    }
}

新增事件

//切换场景
public static event Action<string, Vector3> TransitionEvent;
public static void CallTransitionEvent(string sceneName,Vector3 pos)
{
    TransitionEvent?.Invoke(sceneName,pos);
}

//卸载场景之前的事件
public static event Action BeforeSceneUnloadEvent;
public static void CallBeforeSceneUnloadEvent()
{
    BeforeSceneUnloadEvent?.Invoke();
}

//加载场景之后的事件
public static event Action AfterSceneLoadedEvent;
public static void CallAfterSceneLoadedEvent()
{
    AfterSceneLoadedEvent?.Invoke();
}

//传送到所在位置
public static event Action<Vector3> MoveToPosition;
public static void CallMoveToPosition(Vector3 targetPosition)
{ 
    MoveToPosition?.Invoke(targetPosition);
}

修改SwitchBounds.cs

public class SwitchBounds : MonoBehaviour
{
    //注册事件
    private void OnEnable()
    {
        EventHandler.AfterSceneLoadedEvent += SwitchConfinerShape;
    }

    private void OnDisable()
    {
        EventHandler.AfterSceneLoadedEvent -= SwitchConfinerShape;
    }

    private void SwitchConfinerShape()
    {
        PolygonCollider2D confinerShape = GameObject.FindGameObjectWithTag("BoundsConfiner").GetComponent<PolygonCollider2D>();

        CinemachineConfiner confiner = GetComponent<CinemachineConfiner>();

        confiner.m_BoundingShape2D = confinerShape;

        //Call this if the bounding shape's points change at runtime
        confiner.InvalidatePathCache();
    }
}

 修改ItemManager.cs

public class ItemManager : MonoBehaviour
{
    public Item itemPrefab;

    private Transform itemParent;

    private void OnEnable()
    {
        EventHandler.InstantiateItemInScene += OnInstantiateItemInScene;
        EventHandler.AfterSceneLoadedEvent += OnAfterSceneLoadedEvent;
    }

    private void OnDisable()
    {
        EventHandler.InstantiateItemInScene -= OnInstantiateItemInScene;
        EventHandler.AfterSceneLoadedEvent -= OnAfterSceneLoadedEvent;
    }

    private void OnAfterSceneLoadedEvent()
    {
        itemParent = GameObject.FindWithTag("ItemParent").transform;
    }

    private void OnInstantiateItemInScene(int ID, Vector3 pos)
    {
        var item = Instantiate(itemPrefab, pos, Quaternion.identity, itemParent);
        item.itemID = ID;
    }
}

新增Player.cs中的函数以及变量,并修改Update

    private bool inputDisable;
   
    private void OnEnable()
    {
        EventHandler.BeforeSceneUnloadEvent += OnBeforeSceneUnloadEventd;
        EventHandler.AfterSceneLoadedEvent += OnAfterSceneLoadedEvent;
        EventHandler.MoveToPosition += OnMoveToPosition;
    }

    private void OnDisable()
    {
        EventHandler.BeforeSceneUnloadEvent -= OnBeforeSceneUnloadEventd;
        EventHandler.AfterSceneLoadedEvent -= OnAfterSceneLoadedEvent;
        EventHandler.MoveToPosition -= OnMoveToPosition;
    }

    private void OnMoveToPosition(Vector3 targetPosition)
    {
        transform.position = targetPosition;
    }

    private void OnAfterSceneLoadedEvent()
    {
        inputDisable = false;
    }

    private void OnBeforeSceneUnloadEventd()
    {
        inputDisable = true;
    }

    private void Update()
    {
        if(inputDisable == false)
            PlayerInput();
        SwitchAnimation();
    }

 AnimatorOverride.cs新增注册事件以及具体实现

private void OnEnable()
{
    EventHandler.ItemSelectedEvent += OnItemSelectedEvent;
    EventHandler.BeforeSceneUnloadEvent += OnBeforeSceneUnloadEvent;
}

private void OnDisable()
{
    EventHandler.ItemSelectedEvent -= OnItemSelectedEvent;
    EventHandler.BeforeSceneUnloadEvent -= OnBeforeSceneUnloadEvent;
}

private void OnBeforeSceneUnloadEvent()
{
    holdItem.enabled = false;
    SwitchAnimator(PartType.None);
}

新增InventoryUI.cs注册事件以及实现

private void OnEnable()
{
    EventHandler.UpdateInventoryUI += OnUpdateInventoryUI;
    EventHandler.BeforeSceneUnloadEvent += OnBeforeSceneUnloadEvent;
}

private void OnDisable()
{
    EventHandler.UpdateInventoryUI -= OnUpdateInventoryUI;
    EventHandler.BeforeSceneUnloadEvent -= OnBeforeSceneUnloadEvent;
}

private void OnBeforeSceneUnloadEvent()
{
    UpdateSlotHightlight(-1);
}

最终效果

可以通过门来回切换场景,在举着东西的状态下,切换场景后,人物动画以及UI的选中高亮将会被重置

补充知识点

1.协程

这个视频讲得很好,听一遍就会

2.unity代码执行顺序

图片来源


网站公告

今日签到

点亮在社区的每一天
去签到