Queue battle feedback animations with skip
CI / test (push) Has been cancelled Details

This commit is contained in:
MpakobecEx 2026-10-22 00:51:31 +05:00
parent 3c5c124ab3
commit 5874d6982f
5 changed files with 68 additions and 12 deletions

View File

@ -283,8 +283,8 @@
- [x] Boost: выбор карты заднего ряда как поддержки перед атакой.
- [x] Event-фидбек: атака, guard, drive, урон, heal, retire, perfect guard.
- [x] Отображение силы/крита из `MatchView`; модификаторы уже посчитаны сервером.
- [ ] Очередь анимаций строго по событиям сервера: ride, call, attack, drive, damage, retire, boost, guard.
- [ ] Скип/ускорение анимаций.
- [x] Очередь event-анимаций и кнопка «Пропустить анимации».
- [ ] Полноценные move/attack/drive/damage-анимации карт и стола.
- [x] Таймер хода, индикатор реконнекта, кнопка сдачи (базово).
- [x] Экран результата: победа/поражение; Gold/XP и «ещё матч» — дальше.
- [x] Догон состояния по `resync`/snapshot после реконнекта (без переигрывания анимаций).

View File

@ -48,6 +48,9 @@ namespace Vanguard.UI
private float _feedbackTime;
private float _feedbackDuration;
private Color _feedbackColor;
private readonly Queue<FeedbackStep> _feedbackQueue = new Queue<FeedbackStep>();
private Button _skipFeedback;
private bool _feedbackPlaying;
private readonly List<string> _logLines = new List<string>();
private GameConnection _connection;
@ -129,6 +132,11 @@ namespace Vanguard.UI
_status.horizontalOverflow = HorizontalWrapMode.Wrap;
UiKit.SetPreferredHeight(_status, 74f);
_skipFeedback = UiKit.CreateButton(
right, "SkipFeedback", "Пропустить анимации", SkipFeedback, 15);
UiKit.SetPreferredHeight(_skipFeedback, 38f);
_skipFeedback.gameObject.SetActive(false);
var logTitle = UiKit.CreateText(
right, "LogTitle", "Журнал", 16, new Color(0.72f, 0.80f, 0.94f), TextAnchor.MiddleLeft);
logTitle.fontStyle = FontStyle.Bold;
@ -182,9 +190,12 @@ namespace Vanguard.UI
_showAllIntents = false;
_logLines.Clear();
RebuildLog();
_feedbackQueue.Clear();
_feedbackPlaying = false;
_feedbackTime = 0f;
_feedback.gameObject.SetActive(false);
_flash.gameObject.SetActive(false);
_skipFeedback.gameObject.SetActive(false);
_topInfo.text = (info.DeckName ?? string.Empty) + " · " + info.MatchId;
SetStatus("Подключаемся к матчу…", Muted);
_root.gameObject.SetActive(true);
@ -209,6 +220,7 @@ namespace Vanguard.UI
_result.gameObject.SetActive(false);
_feedback.gameObject.SetActive(false);
_flash.gameObject.SetActive(false);
_skipFeedback.gameObject.SetActive(false);
_view = null;
}
@ -226,23 +238,40 @@ namespace Vanguard.UI
private void ShowFeedback(string text, Color color, float duration = 1.25f, bool flash = false)
{
_feedback.text = text;
_feedbackColor = color;
_feedbackDuration = duration;
_feedbackTime = duration;
_feedbackQueue.Enqueue(new FeedbackStep(text, color, duration, flash));
if (!_feedbackPlaying)
StartNextFeedback();
}
private void StartNextFeedback()
{
if (_feedbackQueue.Count == 0)
{
_feedbackPlaying = false;
_skipFeedback.gameObject.SetActive(false);
return;
}
var step = _feedbackQueue.Dequeue();
_feedbackPlaying = true;
_feedback.text = step.Text;
_feedbackColor = step.Color;
_feedbackDuration = step.Duration;
_feedbackTime = step.Duration;
_feedback.gameObject.SetActive(true);
_feedback.rectTransform.localScale = Vector3.one * 1.12f;
_skipFeedback.gameObject.SetActive(true);
if (flash)
if (step.Flash)
{
_flash.gameObject.SetActive(true);
_flash.color = new Color(color.r, color.g, color.b, 0.22f);
_flash.color = new Color(step.Color.r, step.Color.g, step.Color.b, 0.22f);
}
}
private void UpdateFeedback()
{
if (_feedbackTime <= 0f)
if (!_feedbackPlaying)
return;
_feedbackTime -= Time.unscaledDeltaTime;
@ -260,6 +289,33 @@ namespace Vanguard.UI
_feedback.gameObject.SetActive(false);
if (_flash != null)
_flash.gameObject.SetActive(false);
StartNextFeedback();
}
private void SkipFeedback()
{
_feedbackQueue.Clear();
_feedbackPlaying = false;
_feedbackTime = 0f;
_feedback.gameObject.SetActive(false);
_flash.gameObject.SetActive(false);
_skipFeedback.gameObject.SetActive(false);
}
private readonly struct FeedbackStep
{
public FeedbackStep(string text, Color color, float duration, bool flash)
{
Text = text;
Color = color;
Duration = duration;
Flash = flash;
}
public string Text { get; }
public Color Color { get; }
public float Duration { get; }
public bool Flash { get; }
}
private RectTransform BuildResultPanel(Transform parent)

View File

@ -65,7 +65,7 @@ http://localhost:5080
- Боевой gray-box: доска, рука, часы, кнопки всех легальных интентов,
сдача с подтверждением и экран результата.
- Боевой UX: клик по картам/зонам, подсветка целей, сгруппированные
действия, журнал событий и подсказки.
действия, журнал событий и подсказки; event-анимации с очередью и скипом.
- Карты в бою и коллекции: процедурные арты по клану, рамка, имя, ранг,
триггер, сила/крит, REST и рубашка для скрытых карт.
- Реальные арты: `CardImageService` ищет `Resources/CardArt/{imageKey}`;

View File

@ -754,7 +754,7 @@ Battle; Boot и общие сервисы остаются персистент
`tools/import_card_art.py`.
- Boost UI: клик по своему `SUP`-слоту выбирает поддержку, затем цель.
- Event-фидбек: всплывающие атака/guard/drive/урон/heal/retire, не
блокирует ввод.
блокирует ввод; очередь событий и кнопка «Пропустить анимации».
- Боевой UX: клик по карте/зоне, подсветка целей, отдельная группировка
действий, журнал последних событий и подсказки выбора.
- Поле: `R V R` / `R R R` у каждого игрока, зона урона слева, колода и

View File

@ -61,7 +61,7 @@
(+soul), справа колода и ниже дроп; у противника зеркально; между
полями — зона защиты со щитом и проверками.
- Анимации: event-фидбек (атака, guard, drive, урон, retire, heal) поверх
стола, не блокирует ввод.
стола, не блокирует ввод; очередь событий + кнопка «Пропустить анимации».
- Реальные арты: `imageKey` из `/api/cards` + `CardImageService` ищет
`Resources/CardArt/{imageKey}`. Скачаны все 124 арта. Импорт:
`tools/import_card_art.py`.