Unity中应用退出到重启时长计算异常问题排查
问题根源
你遇到的异常时长是因为退出时间没有被持久化存储。TimeQuitRecorder里的_timeQuit是静态变量,当Unity应用完全退出后,所有内存中的数据(包括静态变量)都会被销毁。重启应用后,_timeQuit会被初始化为System.DateTime.MinValue(即公元1年1月1日),用当前时间减去这个值,自然会得到一个极大的时间差,也就是你看到的63826919893.997秒。
修复方案
需要将退出时间保存到持久化存储中,比如Unity的PlayerPrefs,或者你已有的PlayerData系统。下面是基于PlayerPrefs的修改方案:
修改TimeQuitRecorder类
using UnityEngine; public class TimeQuitRecorder : MonoBehaviour { public static TimeQuitRecorder instance; private const string TimeQuitKey = "LastQuitTime"; public System.DateTime TimeQuit { get; private set; } private void Awake() { if (instance == null) { instance = this; DontDestroyOnLoad(gameObject); // 启动时从PlayerPrefs加载上次退出时间 LoadLastQuitTime(); } else { Destroy(gameObject); } } private void OnApplicationQuit() { // 退出时保存当前时间到PlayerPrefs SaveCurrentTime(); } private void SaveCurrentTime() { // 将DateTime转换为可存储的字符串(ISO格式) string timeString = System.DateTime.Now.ToString(System.DateTimeFormatInfo.InvariantInfo.SortableDateTimePattern); PlayerPrefs.SetString(TimeQuitKey, timeString); PlayerPrefs.Save(); } private void LoadLastQuitTime() { if (PlayerPrefs.HasKey(TimeQuitKey)) { string timeString = PlayerPrefs.GetString(TimeQuitKey); // 解析字符串为DateTime if (System.DateTime.TryParse(timeString, System.DateTimeFormatInfo.InvariantInfo, System.DateTimeStyles.None, out System.DateTime loadedTime)) { TimeQuit = loadedTime; } else { // 解析失败时设为当前时间(避免异常差值) TimeQuit = System.DateTime.Now; } } else { // 第一次启动时,设为当前时间 TimeQuit = System.DateTime.Now; } } }
优化CoinGenerator类的异常处理
给CoinsEarnedSincePlayerQuit方法添加系统时间回拨的判断,避免出现负数时长:
private float CoinsEarnedSincePlayerQuit () { System.DateTime now = System.DateTime.Now; System.TimeSpan secondsSinceQuit = now.Subtract(TimeQuitRecorder.instance.TimeQuit); // 处理系统时间被回拨的情况,避免负数时长 if (secondsSinceQuit.TotalSeconds < 0) { Debug.LogWarning("系统时间被回拨,重置离线时长为0"); secondsSinceQuit = System.TimeSpan.Zero; } float generatedCoins = coinsPerHour * ((float)secondsSinceQuit.TotalSeconds / 3600f); float coinsEarned = Mathf.Clamp(generatedCoins, 0, capacity); Debug.Log("离线时长(秒): " + secondsSinceQuit.TotalSeconds); return coinsEarned; }
额外注意事项
- 如果你使用
PlayerData进行持久化,可以将退出时间存储到你的PlayerData对象中,再保存到本地文件或云端,比PlayerPrefs更适合复杂数据场景。 - 跨平台测试:不同平台的
PlayerPrefs存储位置不同,但Unity已做兼容处理,无需额外调整。 - 防作弊:如果需要防止用户修改系统时间刷金币,可以考虑接入服务器时间校验——记录上次在线时的服务器时间,下次启动时和服务器同步计算离线时长。
内容的提问来源于stack exchange,提问作者Clarc Jae Virgil Magbujos
相关产品推荐
相关产品推荐

