Skip to content

Stratkit Map Notifications

Framework for transient, world-anchored UI shown on a shared world-space canvas - pool-backed, billboarded, screen-pixel scaled. Each notification kind lives in a consumer package (e.g. com.whs.map-notifications) and supplies its own prefab + spawner, fed by a *Request entity or a Producer<T> event.

Choosing a base class

  • AFloatingMapNotificationBehaviour - code-driven kinds with a fade-in / hold / fade-out envelope and an optional off-screen-edge fade. Subclass overrides UpdateContent() for per-frame text/icon updates; alpha composition is owned by this base. Recommended starting point.
  • AAnimatedMapNotificationBehaviour - Animator-driven kinds. A non-looping Lifecycle state drives a nested content CanvasGroup (fade-in / hold / fade-out, movement as animation); this base calls Finish() when the clip completes and adds an external dim/restore gate, SetExternallyVisible(bool), that fades the root alpha without ending the lifetime (e.g. gated by the zoom tier). Subclass overrides UpdateContent().
  • AMapNotificationBehaviour - direct base. Use for persistent button-closed kinds (set alpha = 1 in Setup; bind a button to RequestClose), or any kind whose lifecycle / fade story doesn't match the floating or animated envelopes. Subclass overrides OnLateUpdate() and owns alpha.

Adding a new notification kind

Mirror an existing consumer subfolder (e.g. com.whs.map-notifications/Runtime/ArmyCommand/).

Held / out-of-view kinds

A kind whose anchor can leave view holds the notification and shows a marker instead. MapNotificationPendingTag flags the held state; HeldNotificationRegistry<TKey, TPayload> (in Stratkit.MapNotifications.Core) is a keyed hold store - a per-key accumulator, a snapshot-safe reconcile, and a payload free-list - keyed by Entity (one anchor) or a position cell (a cluster). It is a plain managed class, not a generic ECS system. See the WHS Damage kind (per-army window) for a consumer.

Lifecycle

  • Spawn is two-step. MapNotificationSpawnHelper.Spawn(...) returns a pooled GameObject parented under the per-region canvas; the spawner then calls the subclass's strongly-typed Setup(...) (each kind's Setup signature differs, so a single-call shape isn't viable).
  • Push-close. Subclasses call protected Finish() themselves when they're done - no "is finished" predicate on the base. Finish is idempotent.

Gotchas

  • AFloatingMapNotificationBehaviour seals OnLateUpdate for envelope composition - subclasses extending it override UpdateContent(), not OnLateUpdate.
  • Default ComputeScale assumes RectTransform.sizeDelta is in natural screen pixels; prefabs without ContentSizeFitter must override.
  • A damage number is counted only for an update where nothing but damage changed the army: the unit count moved by exactly the casualties the server reports. An update with a split, a merge or a reinforcement counts nothing.
  • A removed army counts as killed only when the server's ArmyDestroyed (own army) or EnemyArmyDestroyed (enemy killed by the player's army) event names it. An enemy killed by an ally's army shows only the in-window loss.