Files
Hasebe/Assets/Code/OfficeGame/BattleMode/Timeline/BattleTimeline.cs
T
2026-10-27 20:47:16 +03:00

119 lines
4.0 KiB
C#

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<int> _currentBattleFrame = new(0);
public ReadOnlyReactiveProperty<int> CurrentBattleFrame => _currentBattleFrame;
private readonly BattleTimelinePanel _timelinePanel;
private List<EntityTimelineActor> _entities;
private DisposableBag _battleSubscriptions;
public BattleTimeline(BattleTimelinePanel timelinePanel)
{
_timelinePanel = timelinePanel;
}
public async UniTask RunBattleLoopAsync(List<EntityTimelineActor> 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<EntityTimelineActor> entitiesNeedOrders = await PlayTimelineUntilPause(ctx);
await DecideActions(entitiesNeedOrders, ctx);
}
}
finally
{
_battleSubscriptions.Dispose();
}
}
private async UniTask DecideActions(List<EntityTimelineActor> entityActionMakers, CancellationToken ct)
{
foreach (var entity in entityActionMakers)
{
Debug.Log($"Deciding action for {entity.name}");
await entity.DecideAction(ct);
}
}
private async UniTask<List<EntityTimelineActor>> PlayTimelineUntilPause(CancellationToken ct)
{
float accumulator = 0f;
var readyEntities = new List<EntityTimelineActor>();
while (readyEntities.Count == 0 && !ct.IsCancellationRequested)
{
await UniTask.Yield(PlayerLoopTiming.Update, ct);
accumulator += Time.unscaledDeltaTime;
while (accumulator >= TickDelta)
{
accumulator -= TickDelta;
SimulateSingleFrame(readyEntities);
if (readyEntities.Count > 0) return readyEntities;
}
}
return readyEntities;
}
private void SimulateSingleFrame(List<EntityTimelineActor> 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;
}
}