using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Timers; using Cysharp.Threading.Tasks; #if UNITY_EDITOR using UnityEditor; #endif using UnityEngine; using UnityEngine.LowLevel; namespace BITKit { [Serializable] public sealed class GameTick:ITicker { public ulong TickCount => GameTickService.TickCount; public void Add(Action action)=>GameTickService.Add(action); public void Add(Action action)=>GameTickService.Add(action); public void Remove(Action action)=>GameTickService.Remove(action); public void ManualTick(float delta) { throw new NotImplementedException(); } } [CustomType(typeof(ITicker))] public class GameTickService : MonoBehaviour,ITicker { [RuntimeInitializeOnLoadMethod] private static void Reload() { _ActionQueue.Clear(); //_TickActions.Clear(); TickCount = ulong.MinValue; } private static readonly Queue _ActionQueue = new(); // private static readonly CacheList> _TickActions = new(); // public static void Add(Action action) => _TickActions.Add(action); // public static void Remove(Action action) => _TickActions.Remove(action); private static event Action _TickEvents; public static void Add( Action action)=>_TickEvents += action; public static void Remove( Action action)=>_TickEvents -= action; public static void Add(Action action)=>_ActionQueue.Enqueue(action); public static ulong TickCount { get; private set; } ulong ITicker.TickCount => TickCount; void ITicker.Add(Action action)=>Add(action); void ITicker.Add(Action action)=>Add(action); void ITicker.Remove(Action action)=>Remove(action); public void ManualTick(float delta) { throw new NotImplementedException(); } [SerializeField] private int tickRate = 32; [SerializeField] private bool isConcurrent; [SerializeField] private string lastTickTime="0"; private readonly Timer _timer = new(); private double _deltaTime; private PlayerLoopSystem _playerLoop; private int _update; private float _lastTickTime; private void Start() { _playerLoop = PlayerLoop.GetCurrentPlayerLoop(); tickRate = Mathf.Clamp(tickRate, 1, 128); _deltaTime = 1d / tickRate; _timer.Elapsed += Tick; _timer.AutoReset = isConcurrent; _timer.Interval = TimeSpan.FromSeconds(_deltaTime).TotalMilliseconds; _timer.Start(); destroyCancellationToken.Register(_timer.Stop); destroyCancellationToken.Register(_timer.Dispose); } private void Update() { #if UNITY_EDITOR if (EditorApplication.isPlaying is false) { _update = 0; _lastTickTime = Time.time; return; } #endif for (var i = 0; i < _update; i++) { var delta = Time.time - _lastTickTime; _lastTickTime = Time.time; while (_ActionQueue.TryDequeue(out var action)) { action?.Invoke(); } _TickEvents?.Invoke(delta); } _update = 0; } private void Tick(object sender, ElapsedEventArgs e) { TickCount++; try { _update++; } catch (OperationCanceledException) { return; } catch (Exception exception) { BIT4Log.LogException(exception); } lastTickTime = BITApp.Time.TimeAsDouble.ToString(CultureInfo.InvariantCulture); Restart(); return; void Restart() { if (isConcurrent is false && destroyCancellationToken.IsCancellationRequested is false) { _timer.Start(); } } } } }