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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 06:24:31