首页 > 编程语言 > Unity实现离线计时器
2020
10-30

Unity实现离线计时器

本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下

一:TimeSpan

TimeSpan是C#中的一个类,常用以下几种方法

using System;
using UnityEngine;
 
public class Test : MonoBehaviour
{
    private void Awake()
    {
        //将TimeSpan结构的新实例初始化为指定的刻度数
        TimeSpan t1 = new TimeSpan(36); //00:00:00.0000036
 
        //将TimeSpan结构的新实例初始化为指定的小时数、分钟数和秒数
        TimeSpan t2 = new TimeSpan(20, 35, 21); //20:35:21
 
        //将TimeSpan结构的新实例初始化为指定的天数、小时数、分钟数和秒数
        TimeSpan t3 = new TimeSpan(4, 20, 35, 21); //4:20:35:21
        TimeSpan t4 = new TimeSpan(4, 24, 35, 21); //*****自动进位5:00:35:21
 
        //将TimeSpan结构的新实例初始化为指定的天数、小时数、分钟数、秒数和毫秒数(1秒=1000毫秒)
        TimeSpan t5 = new TimeSpan(4, 20, 35, 21, 60); //4:20:35:21:0600000
 
        //直接取出TimeSpan结构所表示的时间间隔的天数、小时数、分钟数、秒数和毫秒数
        TimeSpan t6 = new TimeSpan(4, 20, 35, 21, 60);
        Debug.Log(String.Format("天数:{0}\n小时数:{1}\n分钟数:{2}\n秒数:{3}\n毫秒数:{4}", t6.Days, t6.Hours, t6.Minutes, t6.Seconds,
            t6.Milliseconds)); //天数:4    小时数:20    分钟数:35    秒数:21    毫秒数:60
 
        //将TimeSpan结构所表示的时间间隔换算成等效天数、小时数、分钟数、秒数和毫秒数
        TimeSpan t7 = new TimeSpan(4, 20, 35, 21, 60);
        Debug.Log(String.Format("等效天数:{0}\n等效小时数:{1}\n等效分钟数:{2}\n等效秒数:{3}\n等效毫秒数:{4}", t7.TotalDays, t7.TotalHours, t7.TotalMinutes, t7.TotalSeconds,
            t7.TotalMilliseconds)); //等效天数:4.857...    等效小时数:116.58...    等效分钟数:6995.3...    等效秒数:419721...    等效毫秒数:419721060
    }
}
二:实现离线计时器
要实现离线的计时器,需要得到两个数据,一个是关闭游戏时的时间,一个是打开游戏时的时间,打开游戏的时间用DateTime类中的Now就可以得到,每次关闭游戏时的时间需要存储起来,DateTime类中有一个FromBinary方法,可以把一个long类型的字节转换为DateTime格式
将OfflineTimeManager脚本挂载到一个游戏物体身上即可

using System;
using UnityEngine;
 
//时间的类型
public enum TimeType
{
    Hour,
    Minute,
    Second,
}
 
public class OfflineTimeManager : MonoBehaviour
{
    private static OfflineTimeManager _instance;
 
    public static OfflineTimeManager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<OfflineTimeManager>();
            }
 
            return _instance;
        }
    }
 
    /// <summary>
    /// 退出游戏存储离线时的时间
    /// </summary>
    private void OnApplicationQuit()
    {
        PlayerPrefs.SetString("LastTime", DateTime.Now.ToBinary().ToString());
    }
 
    /// <summary>
    /// 离开游戏存储离线时的时间,进入游戏获得离线收益
    /// </summary>
    /// <param name="isPause">是否暂停</param>
    private void OnApplicationPause(bool isPause)
    {
        if (isPause)
        {
            PlayerPrefs.SetString("LastTime", DateTime.Now.ToBinary().ToString());
        }
        else
        {
            //TODO:获得离线收益
        }
    }
 
    /// <summary>
    /// 得到离线的时间(得到的时间是int类型)
    /// </summary>
    /// <param name="type">时间的类型</param>
    public int GetOfflineTimeToInt(TimeType type = TimeType.Minute)
    {
        DateTime nowTime = DateTime.Now;
        long last = Convert.ToInt64(PlayerPrefs.GetString("LastTime", DateTime.Now.ToBinary().ToString()));
        DateTime lastTime = DateTime.FromBinary(last);
        TimeSpan timeSpan = nowTime.Subtract(lastTime);
        switch (type)
        {
            case TimeType.Hour:
                return (int) timeSpan.TotalHours;
            case TimeType.Minute:
                return (int) timeSpan.TotalMinutes;
            case TimeType.Second:
                return (int) timeSpan.TotalSeconds;
            default:
                return (int) timeSpan.TotalMinutes;
        }
    }
 
    /// <summary>
    /// 得到离线的时间(得到的时间是double类型)
    /// </summary>
    /// <param name="type">时间的类型</param>
    public double GetOfflineTimeToDouble(TimeType type = TimeType.Minute)
    {
        DateTime nowTime = DateTime.Now;
        long last = Convert.ToInt64(PlayerPrefs.GetString("LastTime", DateTime.Now.ToBinary().ToString()));
        DateTime lastTime = DateTime.FromBinary(last);
        TimeSpan timeSpan = nowTime.Subtract(lastTime);
        switch (type)
        {
            case TimeType.Hour:
                return timeSpan.TotalHours;
            case TimeType.Minute:
                return timeSpan.TotalMinutes;
            case TimeType.Second:
                return timeSpan.TotalSeconds;
            default:
                return timeSpan.TotalMinutes;
        }
    }
}
三:移动端防作弊的离线计时器
以上的写法通过修改系统时间会影响正常的离线时间计算,所以需要一个获取内部时间的类去获取当前实际时间,需要将IOS和Android的类库导入到项目中:https://download.csdn.net/download/LLLLL__/12238509

using UnityEngine;
using System;
using System.Runtime.InteropServices;
 
public class UnbiasedTime : MonoBehaviour
{
    private static UnbiasedTime instance;
 
    public static UnbiasedTime Instance
    {
        get
        {
            if (instance == null)
            {
                GameObject g = new GameObject("UnbiasedTimeSingleton");
                instance = g.AddComponent<UnbiasedTime>();
                DontDestroyOnLoad(g);
            }
 
            return instance;
        }
    }
 
    // Estimated difference in seconds between device time and real world time
    // timeOffset = deviceTime - worldTime;
    [HideInInspector]
    public long timeOffset = 0;
 
    void Awake()
    {
        SessionStart();
    }
 
    void OnApplicationPause(bool pause)
    {
        if (pause)
        {
            SessionEnd();
        }
        else
        {
            SessionStart();
        }
    }
 
    void OnApplicationQuit()
    {
        SessionEnd();
    }
 
    // Returns estimated DateTime value taking into account possible device time changes
    public DateTime Now()
    {
        return DateTime.Now.AddSeconds(-1.0f * timeOffset);
    }
 
    // timeOffset value is cached for performance reasons (calls to native plugins can be expensive).
    // This method is used to update offset value in cases if you think device time was changed by user.
    //
    // However, time offset is updated automatically when app gets backgrounded or foregrounded.
    //
    public void UpdateTimeOffset()
    {
#if UNITY_ANDROID
   UpdateTimeOffsetAndroid();
#elif UNITY_IPHONE
        UpdateTimeOffsetIOS();
#endif
    }
 
    // Returns true if native plugin was unable to calculate unbiased time and had fallen back to device DateTime.
    // This can happen after device reboot. Player can cheat by closing the game, changing time and rebooting device.
    // This method can help tracking this situation.
    public bool IsUsingSystemTime()
    {
#if UNITY_ANDROID
   return UsingSystemTimeAndroid();
#elif UNITY_IPHONE
        return UsingSystemTimeIOS();
#else
   return true;
#endif
    }
 
    private void SessionStart()
    {
#if UNITY_ANDROID
   StartAndroid();
#elif UNITY_IPHONE
        StartIOS();
#endif
    }
 
    private void SessionEnd()
    {
#if UNITY_ANDROID
   EndAndroid();
#elif UNITY_IPHONE
        EndIOS();
#endif
    }
 
    // Platform specific code
    //
 
#if UNITY_IPHONE
    [DllImport("__Internal")]
    private static extern void _vtcOnSessionStart();
 
    [DllImport("__Internal")]
    private static extern void _vtcOnSessionEnd();
 
    [DllImport("__Internal")]
    private static extern int _vtcTimestampOffset();
 
    [DllImport("__Internal")]
    private static extern int _vtcUsingSystemTime();
 
    private void UpdateTimeOffsetIOS()
    {
        if (Application.platform != RuntimePlatform.IPhonePlayer)
        {
            return;
        }
 
        timeOffset = _vtcTimestampOffset();
    }
 
    private void StartIOS()
    {
        if (Application.platform != RuntimePlatform.IPhonePlayer)
        {
            return;
        }
 
        _vtcOnSessionStart();
        timeOffset = _vtcTimestampOffset();
    }
 
    private void EndIOS()
    {
        if (Application.platform != RuntimePlatform.IPhonePlayer)
        {
            return;
        }
 
        _vtcOnSessionEnd();
    }
 
    private bool UsingSystemTimeIOS()
    {
        if (Application.platform != RuntimePlatform.IPhonePlayer)
        {
            return true;
        }
 
        return _vtcUsingSystemTime() != 0;
    }
#endif
 
 
#if UNITY_ANDROID
 private void UpdateTimeOffsetAndroid() {
  if (Application.platform != RuntimePlatform.Android) {
   return;
  }
 
  using (var activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
  using (var unbiasedTimeClass = new AndroidJavaClass("com.vasilij.unbiasedtime.UnbiasedTime")) {
   var playerActivityContext = activityClass.GetStatic<AndroidJavaObject>("currentActivity");
   if (playerActivityContext != null && unbiasedTimeClass != null) {
    timeOffset = unbiasedTimeClass.CallStatic <long> ("vtcTimestampOffset", playerActivityContext);
   }
  }  
 }
 
 private void StartAndroid() {
  if (Application.platform != RuntimePlatform.Android) {
   return;
  }
 
  using (var activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
  using (var unbiasedTimeClass = new AndroidJavaClass("com.vasilij.unbiasedtime.UnbiasedTime")) {
   var playerActivityContext = activityClass.GetStatic<AndroidJavaObject>("currentActivity");
   if (playerActivityContext != null && unbiasedTimeClass != null) {
    unbiasedTimeClass.CallStatic ("vtcOnSessionStart", playerActivityContext);
    timeOffset = unbiasedTimeClass.CallStatic <long> ("vtcTimestampOffset");
   }
  }
 }
 
 private void EndAndroid() {
  if (Application.platform != RuntimePlatform.Android) {
   return;
  }
 
  using (var activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
  using (var unbiasedTimeClass = new AndroidJavaClass("com.vasilij.unbiasedtime.UnbiasedTime")) {
   var playerActivityContext = activityClass.GetStatic<AndroidJavaObject>("currentActivity");
   if (playerActivityContext != null && unbiasedTimeClass != null) {
    unbiasedTimeClass.CallStatic ("vtcOnSessionEnd", playerActivityContext);
   }
  }
 }
 
 private bool UsingSystemTimeAndroid() {
  if (Application.platform != RuntimePlatform.Android) {
   return true;
  }
  
  using (var activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
  using (var unbiasedTimeClass = new AndroidJavaClass("com.vasilij.unbiasedtime.UnbiasedTime")) {
   var playerActivityContext = activityClass.GetStatic<AndroidJavaObject>("currentActivity");
   if (playerActivityContext != null && unbiasedTimeClass != null) {
    return unbiasedTimeClass.CallStatic <bool> ("vtcUsingDeviceTime");
   }
  }
  return true;
 }
#endif
}
之后修改一下之前的OfflineTimeManager类,将OfflineTimeManager脚本挂载到一个游戏物体身上即可

using System;
using UnityEngine;
 
//时间的类型
public enum TimeType
{
    Hour,
    Minute,
    Second,
}
 
public class OfflineTimeManager : MonoBehaviour
{
    private static OfflineTimeManager _instance;
 
    public static OfflineTimeManager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<OfflineTimeManager>();
            }
 
            return _instance;
        }
    }
 
    /// <summary>
    /// 退出游戏存储离线时的时间
    /// </summary>
    private void OnApplicationQuit()
    {
        PlayerPrefs.SetString("LastTime", DateTime.Now.ToBinary().ToString());
    }
 
    /// <summary>
    /// 离开游戏存储离线时的时间,进入游戏获得离线收益
    /// </summary>
    /// <param name="isPause">是否暂停</param>
    private void OnApplicationPause(bool isPause)
    {
        if (isPause)
        {
            PlayerPrefs.SetString("LastTime", DateTime.Now.ToBinary().ToString());
        }
        else
        {
            //TODO:获得离线收益
        }
    }
 
    /// <summary>
    /// 得到离线的时间(得到的时间是int类型)
    /// </summary>
    /// <param name="type">时间的类型</param>
    public int GetOfflineTimeToInt(TimeType type = TimeType.Minute)
    {
        DateTime nowTime = UnbiasedTime.Instance.Now();
        long last = Convert.ToInt64(PlayerPrefs.GetString("LastTime", nowTime.ToBinary().ToString()));
        DateTime lastTime = DateTime.FromBinary(last);
        TimeSpan timeSpan = nowTime.Subtract(lastTime);
        switch (type)
        {
            case TimeType.Hour:
                return (int) timeSpan.TotalHours;
            case TimeType.Minute:
                return (int) timeSpan.TotalMinutes;
            case TimeType.Second:
                return (int) timeSpan.TotalSeconds;
            default:
                return (int) timeSpan.TotalMinutes;
        }
    }
 
    /// <summary>
    /// 得到离线的时间(得到的时间是double类型)
    /// </summary>
    /// <param name="type">时间的类型</param>
    public double GetOfflineTimeToDouble(TimeType type = TimeType.Minute)
    {
        DateTime nowTime = UnbiasedTime.Instance.Now();
        long last = Convert.ToInt64(PlayerPrefs.GetString("LastTime", nowTime.ToBinary().ToString()));
        DateTime lastTime = DateTime.FromBinary(last);
        TimeSpan timeSpan = nowTime.Subtract(lastTime);
        switch (type)
        {
            case TimeType.Hour:
                return timeSpan.TotalHours;
            case TimeType.Minute:
                return timeSpan.TotalMinutes;
            case TimeType.Second:
                return timeSpan.TotalSeconds;
            default:
                return timeSpan.TotalMinutes;
        }
    }
}
 

编程技巧