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

View File

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

View File

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

View File

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

View File

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