119 lines
4.0 KiB
C#
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;
|
|
}
|
|
} |