using System.Collections.Generic; using System.Threading; using Code.Infrastructure.UI; using Code.OfficeGame.BattleMode.Timeline; using Cysharp.Threading.Tasks; using LitMotion; using LitMotion.Extensions; using UnityEngine; namespace Code.OfficeGame.BattleMode.UI { public class BattleTimelinePanel : UIPanel { [SerializeField] private int maxFrames = 250; [SerializeField] private GameObject timelineKeyPrefab; [SerializeField] private float duration = 0.35f; [SerializeField] private RectTransform timelineItself; // TODO Вырезать или пощадить private readonly Dictionary _entityEndFrames = new(); private readonly Dictionary _entityKeys = new(); private int _lastKnownCurrentFrame; private MotionHandle _motionHandle; public void SetAction(TimelineBattleAction action) { _entityEndFrames[action.EntityId] = action.Frame; if (!_entityKeys.TryGetValue(action.EntityId, out RectTransform keyRect)) { keyRect = (RectTransform)Instantiate(timelineKeyPrefab, timelineItself).transform; _entityKeys[action.EntityId] = keyRect; } UpdateKeyPosition(keyRect, action.Frame - _lastKnownCurrentFrame); } public void OnFrameTick(int currentFrame) { _lastKnownCurrentFrame = currentFrame; foreach (var (entityId, endFrame) in _entityEndFrames) { if (_entityKeys.TryGetValue(entityId, out RectTransform keyRect)) { int remainingFrames = Mathf.Max(0, endFrame - currentFrame); UpdateKeyPosition(keyRect, remainingFrames); } } } private void UpdateKeyPosition(RectTransform keyRect, int relativeFrame) { var rectWidth = timelineItself.rect.width; var frameSize = rectWidth / maxFrames; keyRect.anchoredPosition = new Vector2(frameSize * relativeFrame - rectWidth / 2f, 0f); } public override async UniTask Open(CancellationToken cts = default) { gameObject.SetActive(true); float startY = -((RectTransform)transform).rect.height; float targetY = 0f; _motionHandle = LMotion.Create(startY, targetY, duration) .WithEase(Ease.OutCubic) .WithScheduler(MotionScheduler.UpdateIgnoreTimeScale) .BindToAnchoredPositionY((RectTransform)transform); await _motionHandle.ToUniTask(cts); } public override async UniTask Close(CancellationToken cts = default) { float startY = 0; float targetY = ((RectTransform)transform).rect.height; _motionHandle = LMotion.Create(startY, targetY, duration) .WithEase(Ease.InBack) .WithScheduler(MotionScheduler.UpdateIgnoreTimeScale) .BindToAnchoredPositionY((RectTransform)transform); await _motionHandle.ToUniTask(cts); gameObject.SetActive(false); } } }