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

Unity游戏无法保存上次游玩日期及日期对比问题求助

Unity 每日游玩次数重置功能修复方案

问题根源分析

你的代码主要存在三个核心问题:

  1. 变量作用域错误:GetCurrentTime协程内声明了局部的dateTimeStr,未赋值给类成员变量,导致类的dateTimeStr始终为null,保存到PlayerPrefs的是空值。
  2. 异步逻辑顺序错误:Start方法中先启动获取日期的协程,随即执行日期对比逻辑,但协程是异步执行的,此时还未拿到服务器返回的日期,对比逻辑完全失效。
  3. 网络请求结果判断错误:原代码用||判断请求结果,会把协议错误(如404)当成成功情况处理,导致错误的JSON解析。

修复后的完整代码

using System;
using UnityEngine;
using UnityEngine.Advertisements;
using UnityEngine.Networking;
using TMPro;
using System.Collections;

public class GameManager : MonoBehaviour, IUnityAdsLoadListener, IUnityAdsShowListener
{
    public int maxPlayCount = 5;
    public int currentPlayCount;
    public int dailyReward = 5;
    public TextMeshProUGUI playCountText;
    public TextMeshProUGUI rewardText;

    private string apiEndpoint = "https://timeapi.io/api/Time/current/zone?timeZone=Europe/Istanbul";
    private string dateTimeStr;
    private bool isAdAvailable = false;

    string _adUnitId = null;
    [SerializeField] string _androidAdUnitId = "Rewarded_Android";
    [SerializeField] string _iOSAdUnitId = "Rewarded_iOS";

    [Serializable]
    public class TimeData
    {
        public int year;
        public int month;
        public int day;
        public int hour;
        public int minute;
        public int seconds;
        public int milliSeconds;
        public string dateTime;
        public string date;
        public string time;
        public string timeZone;
        public string dayOfWeek;
        public bool dstActive;
    }

    void Awake()
    {
#if UNITY_IOS
        _adUnitId = _iOSAdUnitId;
#elif UNITY_ANDROID
        _adUnitId = _androidAdUnitId;
#endif
    }

    void Start()
    {
        // 先启动日期获取协程,所有初始化逻辑移到协程完成后执行
        StartCoroutine(InitializeGameWithServerDate());
    }

    // 重构初始化逻辑,确保拿到服务器日期后再处理游玩次数
    IEnumerator InitializeGameWithServerDate()
    {
        yield return GetCurrentTime();

        // 确认成功拿到服务器日期后再执行后续逻辑
        if (string.IsNullOrEmpty(dateTimeStr))
        {
            Debug.LogError("无法获取服务器日期,使用本地日期作为 fallback");
            dateTimeStr = DateTime.Now.ToString("yyyy-MM-dd");
        }

        if (!PlayerPrefs.HasKey("LastPlayDateStr"))
        {
            // 首次启动,保存日期并初始化游玩次数
            SavePlayDate();
            Debug.Log("首次启动,初始化游玩次数");
        }
        else
        {
            string lastPlayDateStr = PlayerPrefs.GetString("LastPlayDateStr");
            Debug.Log("上次游玩日期: " + lastPlayDateStr);
            
            if (lastPlayDateStr == dateTimeStr)
            {
                // 同一天,加载已保存的游玩次数
                currentPlayCount = PlayerPrefs.GetInt("CurrentPlayCount");
                Debug.Log("同天游玩,加载剩余次数");
            }
            else
            {
                // 跨天,重置游玩次数
                currentPlayCount = maxPlayCount;
                SavePlayDate();
                Debug.Log("跨天游玩,重置次数");
            }
        }

        UpdateUI();
    }

    IEnumerator GetCurrentTime()
    {
        UnityWebRequest webRequest = UnityWebRequest.Get(apiEndpoint);
        yield return webRequest.SendWebRequest();

        // 修正请求结果判断:只有无错误时才处理
        if (webRequest.result == UnityWebRequest.Result.Success)
        {
            string jsonResult = webRequest.downloadHandler.text;
            TimeData timeData = JsonUtility.FromJson<TimeData>(jsonResult);
            // 赋值给类成员变量,而非局部变量
            dateTimeStr = timeData.date;
            Debug.Log("服务器日期: " + dateTimeStr);
        }
        else
        {
            Debug.LogError("获取服务器日期失败: " + webRequest.error);
            dateTimeStr = null;
        }
    }

    void SavePlayDate()
    {
        PlayerPrefs.SetString("LastPlayDateStr", dateTimeStr);
        PlayerPrefs.SetInt("CurrentPlayCount", maxPlayCount);
        currentPlayCount = maxPlayCount;
        // 保存后立即提交PlayerPrefs,避免数据丢失
        PlayerPrefs.Save();
        UpdateUI();
    }

    void UpdateUI()
    {
        playCountText.text = "剩余次数: " + currentPlayCount;
        rewardText.text = "每日奖励: " + dailyReward;
    }

    public void PlayGame()
    {
        if (currentPlayCount > 0)
        {
            currentPlayCount--;
            PlayerPrefs.SetInt("CurrentPlayCount", currentPlayCount);
            PlayerPrefs.Save();
            UpdateUI();
            Debug.Log("开始游戏...");
        }
        else
        {
            Debug.Log("次数耗尽,观看广告获取次数");
            ShowAd();
        }
    }

    public void OnUnityAdsAdLoaded(string _adUnitId)
    {
        Debug.Log("广告加载完成: " + _adUnitId);
        isAdAvailable = true;
    }

    public void OnUnityAdsFailedToLoad(string _adUnitId, UnityAdsLoadError error, string message)
    {
        Debug.LogWarning("广告加载失败: " + _adUnitId + " 错误: " + error + " 信息: " + message);
        isAdAvailable = false;
    }

    public void OnUnityAdsShowFailure(string _adUnitId, UnityAdsShowError error, string message)
    {
        Debug.LogWarning("广告播放失败: " + _adUnitId + " 错误: " + error + " 信息: " + message);
    }

    public void OnUnityAdsShowStart(string _adUnitId)
    {
        Debug.Log("广告开始播放: " + _adUnitId);
    }

    public void OnUnityAdsShowClick(string _adUnitId)
    {
        Debug.Log("广告被点击: " + _adUnitId);
    }

    public void OnUnityAdsShowComplete(string _adUnitId, UnityAdsShowCompletionState showCompletionState)
    {
        Debug.Log("广告播放完成: " + _adUnitId + " 状态: " + showCompletionState);

        if (showCompletionState == UnityAdsShowCompletionState.COMPLETED)
        {
            currentPlayCount++;
            PlayerPrefs.SetInt("CurrentPlayCount", currentPlayCount);
            PlayerPrefs.Save();
            UpdateUI();
        }
    }

    public void ShowAd()
    {
        if (isAdAvailable)
        {
            Advertisement.Show(_adUnitId, this);
            isAdAvailable = false;
        }
        else
        {
            Advertisement.Load(_adUnitId, this);
        }
    }

    void Update() { }
}

关键修改说明

  1. 重构初始化流程:新增InitializeGameWithServerDate协程,确保拿到服务器日期后再处理游玩次数逻辑,解决异步顺序问题。
  2. 修复变量作用域:GetCurrentTime中直接赋值给类成员dateTimeStr,而非局部变量。
  3. 修正网络请求判断:使用UnityWebRequest.Result.Success判断请求是否成功,避免错误解析。
  4. 增加PlayerPrefs.Save():每次修改数据后立即提交,防止数据未保存就退出。
  5. 广告完成逻辑优化:仅在广告完全播放完成后才增加游玩次数,避免作弊。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 14:52:29