You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity门场景切换系统:玩家生成位置偶尔未更新问题求助

场景切换后门的玩家重生位置不稳定问题排查与修复

问题背景

我制作了一套Unity场景切换门系统,玩家触发不同门后应被传送到目标场景的指定位置,但存在玩家位置有时不更新、加载场景后停留在原位置的问题,且多在首次加载项目时出现。loadScene脚本的Debug.Log始终显示正确的spawnPoint坐标,但Unity未正确将玩家放置到该位置。

系统核心组件与逻辑:

  1. SceneSwitcher脚本(挂载在门对象上):鼠标悬停门时更新目标场景和重生点坐标,点击后触发场景切换动画
  2. loadScene脚本:在动画场景中加载目标场景
  3. gameStatsManager:用静态变量存储目标场景和重生点(未挂载到场景对象)
  4. playerSpawnPointSet脚本(每个场景的空对象上):读取gameStatsManager的重生点,设置玩家位置

各脚本代码如下:

SceneSwitcher.cs

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

public class SceneSwitcher : MonoBehaviour
{
    float distance;
    public GameObject player;
    public GameObject interactUI;
    public Animator Transition;
    public string levelToLoadNext;
    public float newSpawPointx;
    public float newSpawPointy;
    public float newSpawPointz;

    void OnMouseOver()
    {
        distance = Vector3.Distance(player.transform.position, this.transform.position);
        if (distance <= 5.5f)
        {
            interactUI.SetActive(true);
            gameStatsManager.levelToLoad = levelToLoadNext;
            gameStatsManager.spawnPoint = new Vector3(newSpawPointx, newSpawPointy, newSpawPointz);
            if (Input.GetMouseButtonDown(0))
            {
                StartCoroutine(LoadLevel());
            }
        }
        else
        {
            interactUI.SetActive(false);
        }
    }
    void OnMouseExit()
    {
        interactUI.SetActive(false);
    }

    void doorAnimationScene()
    {
        SceneManager.LoadScene("doorAnimationScene");
    }

    IEnumerator LoadLevel()
    {
        Transition.SetTrigger("Start");
        yield return new WaitForSeconds(1);
        doorAnimationScene();
    }
}

loadScene.cs

using UnityEngine;
using UnityEngine.SceneManagement;

public class loadScene : MonoBehaviour
{
    public void loadCorrectLevel()
    {
        SceneManager.LoadScene(gameStatsManager.levelToLoad);
        Debug.Log(gameStatsManager.spawnPoint);
    }
}

gameStatsManager.cs

using UnityEngine;

public class gameStatsManager : MonoBehaviour
{
    public static string levelToLoad;
    public static Vector3 spawnPoint = new Vector3(1381.94f, 60.86f, 859.23f);
}

playerSpawnPointSet.cs

using UnityEngine;

public class playerSpawnPointSet : MonoBehaviour
{
    public GameObject player;
    void Start()
    {
        player = GameObject.FindWithTag("Player");
        player.transform.position = gameStatsManager.spawnPoint;
    }
}

核心问题分析

  1. 脚本执行顺序冲突:playerSpawnPointSet的Start()方法执行时机不确定,如果玩家对象自身的Start()/Awake()脚本中有位置重置逻辑,会覆盖重生点赋值;且GameObject.FindWithTag("Player")可能在玩家对象未完全初始化时执行,导致赋值无效。
  2. 静态变量的初始化风险:首次加载项目时,gameStatsManager的静态变量可能因CLR初始化顺序问题,出现值未正确传递的情况(尽管Debug显示值正确,但实际赋值给玩家时可能存在时序差)。
  3. 频繁更新静态变量:OnMouseOver中每帧都更新levelToLoad和spawnPoint,可能在场景切换过程中被意外覆盖。

修复方案

1. 调整玩家位置赋值时机与可靠性

修改playerSpawnPointSet,改用Awake()(比Start()更早执行),并增加空引用判断,确保赋值生效:

using UnityEngine;

public class playerSpawnPointSet : MonoBehaviour
{
    void Awake()
    {
        GameObject player = GameObject.FindWithTag("Player");
        if (player != null)
        {
            // 强制赋值,避免被其他脚本覆盖
            player.transform.position = gameStatsManager.spawnPoint;
        }
        else
        {
            Debug.LogError("场景中未找到带有Player标签的对象");
        }
    }
}

如果玩家对象有其他位置控制脚本,可在Project Settings -> Script Execution Order中把playerSpawnPointSet设为最高优先级,确保先执行。

2. 将gameStatsManager改为单例并持久化

避免静态变量的初始化问题,将其挂载到永不销毁的对象上,确保状态全程一致:

using UnityEngine;

public class gameStatsManager : MonoBehaviour
{
    public static gameStatsManager Instance;
    public string levelToLoad;
    public Vector3 spawnPoint = new Vector3(1381.94f, 60.86f, 859.23f);

    void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}

然后修改所有引用该类的脚本,将静态变量调用改为单例引用:

  • SceneSwitcher中:
    gameStatsManager.Instance.levelToLoad = levelToLoadNext;
    gameStatsManager.Instance.spawnPoint = new Vector3(newSpawPointx, newSpawPointy, newSpawPointz);
    
  • loadScene中:
    SceneManager.LoadScene(gameStatsManager.Instance.levelToLoad);
    Debug.Log(gameStatsManager.Instance.spawnPoint);
    
  • playerSpawnPointSet中:
    player.transform.position = gameStatsManager.Instance.spawnPoint;
    

3. 优化静态变量更新逻辑

只在玩家点击门时更新目标场景和重生点,避免OnMouseOver每帧重复赋值:

void OnMouseOver()
{
    distance = Vector3.Distance(player.transform.position, this.transform.position);
    if (distance <= 5.5f)
    {
        interactUI.SetActive(true);
        if (Input.GetMouseButtonDown(0))
        {
            // 仅点击时更新状态
            gameStatsManager.Instance.levelToLoad = levelToLoadNext;
            gameStatsManager.Instance.spawnPoint = new Vector3(newSpawPointx, newSpawPointy, newSpawPointz);
            StartCoroutine(LoadLevel());
        }
    }
    else
    {
        interactUI.SetActive(false);
    }
}

内容的提问来源于stack exchange,提问作者Gobhniu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 02:32:02