Skip to main content

Presentation Layer

Guide

How runtime state reaches UI, unit views, VFX, audio, and floating text, and why gameplay never waits on missing visuals

Hunter battle right after Poison Arrow resolves: enemy HP reduced, Poison stack icon applied, energy spent, card in discard

Presentation boundaries

AreaOwnsDoes not own
RuntimeHP, armor, energy, piles, statuses, phase, eventsPixel layout, sound routing, particle quality
UIHand widgets, unit bars, status icons, intent widgets, choice/reward panelsBattle rules or card legality
Unit viewUnit sprite, procedural motion, hit/heal/death reactions, status visualsDamage calculation or status stacks
FX nodesRequests for VFX, SFX, floating text, flash, slide, shake, animationFinal asset polish, render pipeline compatibility, mixer setup

If a visual effect fails to play, gameplay still resolves

Event-driven UI

The reactive loop is always the same: a controller mutation raises an event, your handler wakes up, reads fresh state through GCSApi, and updates the widget

Custom UI should subscribe to runtime events, then read current state:

private IDisposable _energySub;

void OnEnable()
{
_energySub = GCSApi.OnEnergyChanged(_ => RefreshEnergy());
RefreshEnergy();
}

void OnDisable()
{
_energySub?.Dispose();
}

void RefreshEnergy()
{
energyLabel.text = $"{GCSApi.PlayerEnergy}/{GCSApi.MaxEnergy}";
}

Events tell you when to refresh. GCSApi tells you what the current value is

Core UI refresh events

UI areaEvents to watchState to read
Turn and phase labelOnTurnStarted, OnTurnEnded, OnEnemyTurnStarted, OnBattleEndedGCSApi.Phase, GCSApi.TurnNumber, GCSApi.IsPlayerTurn
EnergyOnEnergyChanged, OnEnergyGained, OnEnergySpentGCSApi.PlayerEnergy, GCSApi.MaxEnergy
HandCard lifecycle events, OnCardModified, OnEnergyChangedGCSApi.Hand(), GCSApi.CanPlayCard, GCSApi.GetEffectiveEnergyCost
Unit barsOnUnitHpChanged, OnArmorGained, OnArmorLost, OnUnitDiedGCSApi.Hp, GCSApi.MaxHp, GCSApi.Armor, GCSApi.IsAlive
Status widgetsOnStatusChanged, OnStatusTicked, status transition eventsGCSApi.StatusesOn(unit)
Enemy intentsOnEnemyUnitIntentChanged, OnEnemyPhaseChangedGCSApi.PendingIntents, GCSApi.CurrentIntentTag, GCSApi.CurrentIntentValue
Choice UIOnEffectChoiceOffered, OnEffectChoiceSelected, OnEffectChoiceSkippedChoice payload plus GCSApi.IsWaitingForChoice
Reward UIOnBattleRewardOffered, OnBattleRewardSelected, OnBattleRewardSkippedReward payload plus GCSApi.IsWaitingForReward

UnitView behavior

The built-in UnitView binds to a UnitState and subscribes to:

  • OnUnitHpChanged
  • OnUnitDied
  • OnStatusChanged
  • OnStatusTicked

It uses those events to play hit, heal, death, and status animations; it also registers itself by UnitId, so presentation nodes can find the view for a target unit

Custom projects can reuse UnitView, subclass their own view behavior, or replace it entirely; the stable integration point is the runtime event payload plus UnitId

Floating text

FloatingTextSystem exposes a presentation request event:

Request fieldMeaning
TargetUnit receiving the text
KindDamage, Heal, Armor, Block, or Custom
ContentAmount or Text
AmountNumeric value to display
TextCustom label
Color, UseCustomColorOptional color override
IncludeSignWhether to show a plus or minus sign
ScaleMultiplier, DurationMultiplierPer-request visual tuning
OffsetPosition offset near the target

The FlowGraph Show Floating Text node raises these requests; A custom floating text presenter can subscribe to FloatingTextSystem.Requested

FlowGraph presentation nodes

NodeRuntime effect
Play VFXInstantiates or requests a VFX prefab at a unit or position
Play AnimationSends an animation trigger to the target unit presentation
Play SFXPlays a one-shot audio clip
Flash UnitCalls UnitView.Flash when the target view exists
Slide UnitMoves the target presentation briefly
Show Floating TextSends a FloatingTextRequest
Shake CameraRequests a camera shake

Use these nodes after gameplay state changes so the visual reflects the actual result

Animation gates and waits

The state machine can defer transitions while presentation waits complete

FeatureUse
Animation gateHolds phase transitions while a gated coroutine or animation is running
Flow waitLets a graph delay continuation by seconds or by animation id
Enemy gapsAdds readable timing between enemy actions

The Gate tab of the Game Card Monitor shows who currently holds the animation gate, the acquire/release log, and the timeout, which makes stuck transitions easy to diagnose:

Game Card Monitor Gate tab showing current animation gate holders and the acquire/release log

This is also why custom UI should handle state changes reactively; A card play can start state changes, animations, waits, choices, and later transitions

VFX, audio, and render pipeline scope

The graph contract for presentation is stable, but final assets are separate:

  • VFX prefabs can be replaced or upgraded later
  • SFX clips and mixer routing can be configured later
  • Render pipeline specifics (materials, shaders, post-processing) belong to the asset layer, not to graphs

Keep pipeline-specific assumptions in assets and the presentation layer; gameplay graphs request feedback, while the active presentation setup decides how to render it

Failure behavior

Missing itemExpected result
Missing unit viewGameplay still resolves; the unit-specific visual request may be skipped
Missing VFX prefabGameplay still resolves; no particle appears
Missing SFX clipGameplay still resolves; no sound plays
Missing floating text presenterGameplay still resolves; the request has no visible subscriber
Animation wait not scheduledExecution continues immediately

Use runtime logs and the Monitor to diagnose missing feedback; optional presentation assets may be absent without blocking battle resolution