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; 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 isMainThread; [SerializeField] private bool isConcurrent; [SerializeField] private string lastTickTime="0"; private readonly Timer _timer = new(); private float _deltaTime; private double _lastTime; private void Start() { tickRate = Mathf.Clamp(tickRate, 1, 128); _deltaTime = 1f / tickRate; _timer.Elapsed += Tick; _timer.AutoReset = isConcurrent; _timer.Interval = TimeSpan.FromSeconds(_deltaTime).TotalMilliseconds; _timer.Start(); destroyCancellationToken.Register(() => { _timer.Stop(); _timer.Dispose(); }); } private async void Tick(object sender, ElapsedEventArgs e) { TickCount++; try { var delta = (float)(BITApp.Time.TimeAsDouble - _lastTime); _lastTime = BITApp.Time.TimeAsDouble; if (isMainThread) await UniTask.SwitchToMainThread(destroyCancellationToken); #if UNITY_EDITOR if (EditorApplication.isPlaying is false) { Restart(); } #endif while (_ActionQueue.TryDequeue(out var action)) { action?.Invoke(); } _TickEvents?.Invoke(delta); // using var xEnumerator = _TickActions.GetEnumerator(); // while (xEnumerator.MoveNext()) // { // xEnumerator.Current!.Invoke(delta); // } //_TickEvents?.Invoke((float)delta); } 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(); } } } } }