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

Unity秒表跨场景及重载后不重置持续计时问题求助

解决Unity秒表跨场景/重载后持续计时的问题

核心问题原因

你的秒表脚本挂载的GameObject在场景切换/重载时会被销毁,timer等变量随之重置;之前用PlayerPrefs没成功,大概率是没有在合适的时机读写数据——比如场景切换前没保存计时状态,加载新场景后没读取旧数据。

方案一:单例模式+DontDestroyOnLoad(最常用的跨场景持久化方案)

把秒表所在的GameObject设置为跨场景不销毁,同时用单例确保全局唯一实例,避免重复创建:

修改后的完整代码:

public class Stopwatch : MonoBehaviour
{
    // 全局唯一实例
    public static Stopwatch Instance { get; private set; }

    float timer;
    float seconds;
    float minutes;
    float hours;

    bool start;

    [SerializeField] TextMeshProUGUI stopWatchText;

    private void Awake()
    {
        // 单例逻辑:保证全局只有一个秒表实例
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
        }
        else
        {
            Instance = this;
            // 标记该对象跨场景不销毁
            DontDestroyOnLoad(gameObject);
        }
    }

    private void Start()
    {
        // 仅初始化状态,不要重置timer(保留之前的计时数据)
        UpdateStopwatchUI();
    }

    private void Update()
    {
        StopWatchCalcul();
    }

    void StopWatchCalcul()
    {
        if (start)
        { 
            timer += Time.deltaTime;
            seconds = (int)(timer % 60);
            minutes = (int)((timer / 60) % 60);
            hours = (int)(timer / 3600);

            UpdateStopwatchUI();
        }
    }

    // 单独封装UI更新逻辑,方便复用
    void UpdateStopwatchUI()
    {
        if (stopWatchText != null)
        {
            stopWatchText.text = $"{hours:00}:{minutes:00}:{seconds:00}";
        }
    }

    public void startTimer()
    {
        start = true;
    }

    public void stopTimer()
    {
        start = false;
    }

    public void resetTimer()
    {
        start = false;
        timer = 0;
        UpdateStopwatchUI();
    }

    // 可选:新场景加载后重新绑定UI组件
    public void BindUI(TextMeshProUGUI newText)
    {
        stopWatchText = newText;
        UpdateStopwatchUI();
    }
}

使用步骤:

  • 把脚本挂载到一个空GameObject上(命名比如GlobalStopwatch)
  • 确保首次启动游戏时加载包含该对象的场景,之后切换场景它会自动保留
  • 如果新场景里有秒表UI,在新场景的初始化脚本中调用Stopwatch.Instance.BindUI(你的Text组件)完成绑定

方案二:正确使用PlayerPrefs(适合需要持久化到本地的场景)

如果需要退出游戏再打开也能保留计时,用PlayerPrefs在场景销毁、应用暂停时保存数据,启动时读取:

修改后的代码:

public class Stopwatch : MonoBehaviour
{
    float timer;
    float seconds;
    float minutes;
    float hours;

    bool start;

    [SerializeField] TextMeshProUGUI stopWatchText;

    // 定义存储用的键名
    private const string TimerKey = "Stopwatch_Timer";
    private const string StartStateKey = "Stopwatch_StartState";

    private void Start()
    {
        // 加载之前保存的计时数据
        LoadTimerData();
        UpdateStopwatchUI();
    }

    // 场景销毁前保存数据
    private void OnDestroy()
    {
        SaveTimerData();
    }

    // 应用暂停时也保存数据(避免意外退出丢失)
    private void OnApplicationPause(bool pauseStatus)
    {
        if (pauseStatus)
        {
            SaveTimerData();
        }
    }

    private void Update()
    {
        StopWatchCalcul();
    }

    void StopWatchCalcul()
    {
        if (start)
        { 
            timer += Time.deltaTime;
            seconds = (int)(timer % 60);
            minutes = (int)((timer / 60) % 60);
            hours = (int)(timer / 3600);

            UpdateStopwatchUI();
        }
    }

    void UpdateStopwatchUI()
    {
        if (stopWatchText != null)
        {
            stopWatchText.text = $"{hours:00}:{minutes:00}:{seconds:00}";
        }
    }

    // 保存计时数据到PlayerPrefs
    void SaveTimerData()
    {
        PlayerPrefs.SetFloat(TimerKey, timer);
        PlayerPrefs.SetInt(StartStateKey, start ? 1 : 0);
        PlayerPrefs.Save(); // 强制写入磁盘,避免数据丢失
    }

    // 从PlayerPrefs加载数据
    void LoadTimerData()
    {
        timer = PlayerPrefs.GetFloat(TimerKey, 0f);
        start = PlayerPrefs.GetInt(StartStateKey, 0) == 1;
        // 重新计算时分秒
        seconds = (int)(timer % 60);
        minutes = (int)((timer / 60) % 60);
        hours = (int)(timer / 3600);
    }

    public void startTimer()
    {
        start = true;
    }

    public void stopTimer()
    {
        start = false;
    }

    public void resetTimer()
    {
        start = false;
        timer = 0;
        UpdateStopwatchUI();
        SaveTimerData(); // 重置后同步保存数据
    }
}

注意事项:

  • PlayerPrefs仅适合存储小体积数据,不要用它存大量内容
  • 必须调用PlayerPrefs.Save(),否则数据可能只存在内存中,未写入磁盘

方案三:用ScriptableObject共享状态(适合多场景同步计时)

创建ScriptableObject资产存储计时数据,所有场景的秒表脚本都引用同一个资产,实现状态共享:

  1. 先创建ScriptableObject类:
using UnityEngine;

[CreateAssetMenu(fileName = "StopwatchData", menuName = "ScriptableObjects/StopwatchData")]
public class StopwatchData : ScriptableObject
{
    public float timer;
    public bool isRunning;
}
  1. 在Unity编辑器右键选择ScriptableObjects/StopwatchData,创建一个资产(命名比如GlobalStopwatchData)

  2. 修改秒表脚本:

public class Stopwatch : MonoBehaviour
{
    float seconds;
    float minutes;
    float hours;

    [SerializeField] StopwatchData stopwatchData;
    [SerializeField] TextMeshProUGUI stopWatchText;

    private void Start()
    {
        UpdateStopwatchUI();
    }

    private void Update()
    {
        StopWatchCalcul();
    }

    void StopWatchCalcul()
    {
        if (stopwatchData.isRunning)
        { 
            stopwatchData.timer += Time.deltaTime;
            seconds = (int)(stopwatchData.timer % 60);
            minutes = (int)((stopwatchData.timer / 60) % 60);
            hours = (int)(stopwatchData.timer / 3600);

            UpdateStopwatchUI();
        }
    }

    void UpdateStopwatchUI()
    {
        if (stopWatchText != null)
        {
            stopWatchText.text = $"{hours:00}:{minutes:00}:{seconds:00}";
        }
    }

    public void startTimer()
    {
        stopwatchData.isRunning = true;
    }

    public void stopTimer()
    {
        stopwatchData.isRunning = false;
    }

    public void resetTimer()
    {
        stopwatchData.isRunning = false;
        stopwatchData.timer = 0;
        UpdateStopwatchUI();
    }
}

使用说明:

  • 把创建的GlobalStopwatchData资产拖到脚本的stopwatchData字段上
  • 任何场景的秒表脚本只要引用同一个资产,就能同步计时状态
  • 注意:ScriptableObject的数据在编辑模式下会保留,但打包后运行时修改的数据不会保存到磁盘(需要持久化的话,要结合PlayerPrefs)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 21:33:20