Skip to main content

Presentation Layer

GCS ยท Runtime

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

Use this when building custom battle UI, wiring FX nodes, or debugging animations that hold up phase transitions.

The presentation layer makes battle state visible. It observes runtime state and events, requests feedback, and animates results. It never owns gameplay rules. A single card play can fan out into damage numbers, status icons, VFX, and a camera shake, all driven by the same event stream:

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, events.Pixel layout, sound routing, particle quality.
UIHand widgets, unit bars, status icons, intent widgets, choice/reward panels.Battle rules or card legality.
Unit viewUnit sprite, procedural motion, hit/heal/death reactions, status visuals.Damage calculation or status stacks.
FX nodesRequests for VFX, SFX, floating text, flash, slide, shake, animation.Final 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.

Do not encode pipeline-specific assumptions into gameplay graphs. Keep graphs requesting gameplay feedback; let the asset and presentation layer decide how to render it.


Failure behaviorโ€‹

Presentation degrades gracefully.

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 for diagnosis, but never block battles on optional presentation assets.