VanguardReMaster/client/Assets/Vanguard/Runtime/Board3D/Animation/CardMoveAnimator.cs

218 lines
6.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using UnityEngine;
using Vanguard.Battle;
using Vanguard.Board3D;
namespace Vanguard.Board3D
{
/// <summary>
/// Единый планировщик карточных перемещений. Карточки анимируются
/// Card3D, аниматор отвечает за stagger, очередь и InputLock.
/// </summary>
public sealed class CardMoveAnimator
{
private sealed class Request
{
public Card3D Card;
public Vector3 FromPosition;
public Quaternion FromRotation;
public Vector3 FromScale;
public float Duration;
public float ArcHeight;
}
private sealed class ActiveMove
{
public Card3D Card;
public float Remaining;
}
private readonly Queue<Request> _queue = new Queue<Request>();
private readonly List<ActiveMove> _active = new List<ActiveMove>();
private readonly InputLockService _inputLock;
private AnimationConfig _config;
private float _delay;
private bool _locked;
public CardMoveAnimator(
InputLockService inputLock,
AnimationConfig config)
{
_inputLock = inputLock;
_config = config;
}
public bool IsBusy => _queue.Count > 0 || _active.Count > 0;
public void SetConfig(AnimationConfig config)
{
_config = config;
}
public void Enqueue(
Card3D card,
Vector3 fromPosition,
Quaternion fromRotation,
Vector3 fromScale,
float duration,
float arcHeight)
{
if (card == null || card.IsDragging)
return;
EnsureLock();
_queue.Enqueue(new Request
{
Card = card,
FromPosition = fromPosition,
FromRotation = fromRotation,
FromScale = fromScale,
Duration = duration,
ArcHeight = arcHeight,
});
}
public void Tick(float deltaTime)
{
if (_active.Count > 0)
{
var delta = Mathf.Max(0f, deltaTime);
for (var i = _active.Count - 1; i >= 0; i--)
{
var active = _active[i];
var card = active?.Card;
if (active == null
|| card == null
|| !card.gameObject.activeInHierarchy)
{
if (card != null)
card.StopMove(snap: false);
_active.RemoveAt(i);
continue;
}
active.Remaining -= delta;
if (!card.IsMoving)
{
_active.RemoveAt(i);
continue;
}
// Страховка: карта могла быть снята с рендера до
// завершения Tick. Не держим InputLock вечно.
if (active.Remaining <= 0f)
{
card.StopMove(snap: true);
_active.RemoveAt(i);
}
}
}
if (_queue.Count > 0)
{
_delay -= Mathf.Max(0f, deltaTime);
if (_delay <= 0f)
StartNext();
}
if (!IsBusy)
ReleaseLock();
}
public void CancelAll(bool snapCards = false)
{
_queue.Clear();
foreach (var active in _active)
{
if (active?.Card != null)
active.Card.StopMove(snapCards);
}
_active.Clear();
_delay = 0f;
ReleaseLock();
}
public void CancelCard(Card3D card)
{
if (card == null)
return;
var remaining = new Queue<Request>();
while (_queue.Count > 0)
{
var request = _queue.Dequeue();
if (request.Card != card)
remaining.Enqueue(request);
}
while (remaining.Count > 0)
_queue.Enqueue(remaining.Dequeue());
for (var i = _active.Count - 1; i >= 0; i--)
{
if (_active[i]?.Card != card)
continue;
card.StopMove(snap: false);
_active.RemoveAt(i);
}
if (!IsBusy)
ReleaseLock();
}
private void StartNext()
{
if (_queue.Count == 0)
return;
var request = _queue.Dequeue();
var card = request.Card;
if (card == null || card.IsDragging)
{
_delay = 0f;
return;
}
card.StartArcMove(
request.FromPosition,
request.FromRotation,
request.FromScale,
request.Duration,
request.ArcHeight);
if (card.IsMoving)
{
_active.Add(new ActiveMove
{
Card = card,
Remaining = Mathf.Max(0.1f, request.Duration) + 0.05f,
});
}
_delay = Mathf.Max(0.01f, _config != null
? _config.moveDelayBetweenCards
: 0.18f);
}
private void EnsureLock()
{
if (_locked)
return;
_inputLock?.Acquire("card-move");
_locked = true;
}
private void ReleaseLock()
{
if (!_locked)
return;
_inputLock?.Release("card-move");
_locked = false;
}
}
}