Unity应用关闭时如何实现每24小时自动执行分数折算逻辑
问题背景
现有24小时周期分数折算逻辑规则为:每间隔86400000毫秒(24小时)检测玩家分数,若分数高于7000分,将超出7000分的部分除以2折算。原实现仅在应用运行时可正常触发,应用关闭状态下无法按周期执行折算。
原有实现存在几个核心缺陷:
- 依赖
MonoBehaviour.Update帧轮询判断时间,应用进程关闭、系统挂起时轮询完全停止 - 未处理离线时间追算,应用关闭期间经过的折算周期不会被执行
- 首次启动时
LastReset无默认存储值,直接解析字符串会抛出异常 - 每帧执行分数判断逻辑存在不必要的性能开销
实现思路
不需要强制应用在关闭状态下保持后台运行(移动端、桌面端系统都会严格限制后台进程存活,该方案兼容性极差),改用启动/前台唤醒时离线追算的方案即可实现100%准确的周期执行:
- 持久化存储两个核心数据:玩家当前分数、上次折算完成的时间戳
- 在两个固定节点触发折算逻辑:应用冷启动完成时、应用从后台切回前台时
- 触发时计算当前时间与上次折算时间的间隔,统计间隔内包含多少个完整24小时周期,逐次执行分数折算
- 折算完成后更新上次折算时间为「原上次折算时间 + 折算周期数*24小时」,避免时间累计漂移
- 处理首次启动边界:无历史存储数据时,将上次折算时间初始化为当前启动时间
修正后代码
using System; using UnityEngine; public class LeagueClock : MonoBehaviour { private const long ResetIntervalTicks = TimeSpan.TicksPerDay; // 24小时对应的Ticks值,避免毫秒换算误差 private const int ScoreThreshold = 7000; private long lastResetTicks; private int scrMain; private void Awake() { // 初始化读取持久化数据,处理首次启动默认值 scrMain = PlayerPrefs.GetInt("scrMain", 0); // 首次启动无记录时,默认上次重置时间为当前时间 if (PlayerPrefs.HasKey("LastReset")) { lastResetTicks = long.Parse(PlayerPrefs.GetString("LastReset")); } else { lastResetTicks = DateTime.Now.Ticks; PlayerPrefs.SetString("LastReset", lastResetTicks.ToString()); PlayerPrefs.Save(); } // 启动时先追算离线期间的折算 CalculateOfflineSettlement(); } private void OnApplicationFocus(bool hasFocus) { // 应用从后台切回前台时,追算后台期间的折算 if (hasFocus) { CalculateOfflineSettlement(); } } /// <summary> /// 追算离线期间所有到期的分数折算 /// </summary> private void CalculateOfflineSettlement() { long currentTicks = DateTime.Now.Ticks; // 计算距离上次重置过了多少个完整24小时周期 long totalPassedTicks = currentTicks - lastResetTicks; int resetCount = (int)(totalPassedTicks / ResetIntervalTicks); if (resetCount <= 0) return; // 逐次执行折算,和在线时的计算逻辑完全一致 for (int i = 0; i < resetCount; i++) { if (scrMain <= ScoreThreshold) break; // 分数低于阈值后不需要继续折算 scrMain -= (scrMain - ScoreThreshold) / 2; } // 更新存储数据,重置时间按周期数对齐,避免时间漂移 lastResetTicks += resetCount * ResetIntervalTicks; PlayerPrefs.SetInt("scrMain", scrMain); PlayerPrefs.SetString("LastReset", lastResetTicks.ToString()); PlayerPrefs.Save(); Debug.Log($"完成{resetCount}次离线折算,当前分数:{scrMain}"); } }
方案说明
- 该方案完全不依赖应用后台运行,不管应用关闭几天、几个月,只要下次打开就会自动按24小时周期补算所有折算,结果和应用一直保持在线完全一致
- 用
Ticks做时间计算代替毫秒浮点数,避免长周期下的浮点数精度误差 - 移除了原
Update中的每帧轮询逻辑,折算逻辑仅在必要节点触发,无冗余性能开销 - 处理了首次启动、分数低于阈值后停止折算等边界情况,不会出现空值解析报错、无意义计算的问题
内容的提问来源于stack exchange,提问作者Ilya
相关产品推荐
相关产品推荐

