using System.Collections.Generic; using System.Threading; using Code.OfficeGame.BattleMode.Actions; using Code.OfficeGame.BattleMode.UI; using Cysharp.Threading.Tasks; using R3; using UnityEngine; namespace Code.OfficeGame.BattleMode.Timeline { public class BattleTimeline { public const int TicksPerSecond = 60; public const float TickDelta = 1f / TicksPerSecond; private readonly ReactiveProperty _currentBattleFrame = new(0); public ReadOnlyReactiveProperty CurrentBattleFrame => _currentBattleFrame; private readonly BattleTimelinePanel _timelinePanel; private List _entities; private DisposableBag _battleSubscriptions; public BattleTimeline(BattleTimelinePanel timelinePanel) { _timelinePanel = timelinePanel; } public async UniTask RunBattleLoopAsync(List entities, CancellationToken ctx) { _entities = entities; _currentBattleFrame.Value = 0; _battleSubscriptions = new DisposableBag(); try { _currentBattleFrame .Subscribe(_timelinePanel.OnFrameTick) .AddTo(ref _battleSubscriptions); foreach (var entity in entities) entity.OnActionSet .Subscribe(action => { action.Frame += _currentBattleFrame.Value; _timelinePanel.SetAction(action); }) .AddTo(ref _battleSubscriptions); await DecideActions(_entities, ctx); while (!IsBattleOver() && !ctx.IsCancellationRequested) { List entitiesNeedOrders = await PlayTimelineUntilPause(ctx); await DecideActions(entitiesNeedOrders, ctx); } } finally { _battleSubscriptions.Dispose(); } } private async UniTask DecideActions(List entityActionMakers, CancellationToken ct) { foreach (var entity in entityActionMakers) { Debug.Log($"Deciding action for {entity.name}"); await entity.DecideAction(ct); } } private async UniTask> PlayTimelineUntilPause(CancellationToken ctx) { float accumulator = 0f; var readyEntities = new List(); while (readyEntities.Count == 0 && !ctx.IsCancellationRequested) { await UniTask.Yield(PlayerLoopTiming.Update, ctx); accumulator += Time.unscaledDeltaTime; while (accumulator >= TickDelta) { accumulator -= TickDelta; SimulateSingleFrame(readyEntities); if (readyEntities.Count > 0) return readyEntities; } } return readyEntities; } private void SimulateSingleFrame(List outReadyEntities) { _currentBattleFrame.Value++; foreach (var entity in _entities) { entity.CurrentAction.Tick(TickDelta); entity.animator.Update(TickDelta); if (entity.Finished) { outReadyEntities.Add(entity); } } Physics.Simulate(TickDelta); Physics.SyncTransforms(); } private bool IsBattleOver() => false; } }