Transformation System

v1.0.0

Workflows

Complete recipes — the wiring for each piece of the game mode, end to end.

Each recipe below is a complete piece of wiring. They compose — the round loop assumes the transform recipe, the camera recipe assumes a disguise — but none of them requires any other to work.

Transform & Revert

The base loop. Transform Into Prop reads the prop the character is currently looking at — no dropdown, no target to pick — so both triggers can sit on the same key, separated only by the condition.

On Key Down (E)  +  Can Clone Prop = true   ->  Transform Into Prop     (Self)
On Key Down (E)  +  Is Disguised   = true   ->  Revert To Character     (Self)

Switching directly from one prop to another is supported: the player does not have to revert first. If you want a fixed disguise instead — a scripted cutscene, a saved loadout, a network message — use Transform Into Prop (by ID) and drag the definition into the Prop ID getter rather than typing a GUID.

Add a Transform FX component and assign it to the controller's FX slot and the particles and sound fire automatically on both transitions — no extra nodes. Use Play Transform FX / Play Revert FX only when you want them in a custom sequence.

Hunter Damage

The asset applies no damage itself. What a guess costs is authored on the instruction that applies it, so there is exactly one number and it is the one that runs.

Prop-side (recommended)

On Was Found fires on the prop player who was identified, so no hunter-side routing is needed — and the same wiring is correct in multiplayer.

─ On the hider prefab ─────────────────────────────────────
On Was Found     +  Is Round Phase = Hunt
  ->  Deal Damage   Target = Self   Stat = Health   Amount = 100

─ On the hunter prefab ────────────────────────────────────
On Wrong Guess
  ->  Deal Damage   Target = Self   Stat = Health   Amount = 25

From a melee hit instead of a raycast

If your hunters swing a weapon rather than press an inspect key, evaluate the struck object instead of firing a ray. Place the trigger on the object that was hit.

On Melee Hit   (on the world prop or the character)
  ->  Hunter Inspect Target
        Hunter:    Target      (GC2 passes the attacker)
        Inspected: Self

Damage through a disguise

Deal Damage to Prop Player routes damage to whoever is currently wearing a struck world prop, and is a silent no-op if nobody is. On a plain character trigger it applies the damage directly.

On Melee Hit   (on a world prop)
  ->  Deal Damage to Prop Player   Prop = Self   Stat = Health   Amount = 100
Wrong Guess Requires A Prop is off by default so that no existing wiring changes behaviour on upgrade. Switch it on in your Hunter Settings unless you genuinely want shooting the floor to count against the hunter.

Camera Framing That Adapts to the Prop

A shot framed for a 1.8 m character is wrong for a 0.4 m crate and wrong again for a 4 m tree. No extra component and nothing to keep in sync — the package exposes the dimensions it already measures, and Game Creator's own Change Aim instruction consumes them.

─ Adapt on transform ──────────────────────────────────────
On Transform Complete
  ->  Change Aim
        Shot          = your third-person Shot Camera
        Offset Lift   = Prop Size ▸ Height           × 0.15
        Offset Radius = Prop Size ▸ Bounding Radius  × 1.0
        Duration      = 0.3

─ Restore on revert ───────────────────────────────────────
On Reverted
  ->  Change Aim
        Shot          = the same Shot Camera
        Offset Lift   = 0
        Offset Radius = 0
        Duration      = 0.3

Change Aim's inputs are offsets, added on top of what is authored on the shot every frame — so reverting is just zero. No cached state, nothing to get out of sync, and no risk of a disguise leaving the camera permanently displaced if a player disconnects mid-round.

Prop Size measurementWhat it isTypical use
HeightFull height above the floorLift — raise the camera on tall props
Width (X) / Depth (Z)Footprint on one axisAsymmetric framing
FootprintThe wider of X and ZRadius when only ground size matters
Bounding RadiusHalf-diagonal of the whole propRadius — grows with both height and width
Keep the lift well under the prop's height. Lift raises the camera pivot, so a generous factor puts the camera above a tall prop looking straight down at its own canopy — and the player then cannot see anything to aim at, including the next prop they want to become. Most of the framing work should be done by radius, not lift.

Not disguised? You get zero. Prop Size returns 0 when the target has no controller, is not disguised, or has just reverted — and with additive offsets, zero means "leave the shot as authored". The safe value and the neutral value are the same number, so there is no failure mode where a stale measurement strands your camera.

Lean, Rotate & Whistle

The three things that turn hiding from a state into a skill. All three need Prop Clone Motion or Prop Whistle on the hider, and all three reset themselves on revert.

Lean and rotate — one trigger per key press

On Key Down (Q)  +  Is Disguised = true  ->  Lean Prop     Self   Lean X: -1
On Key Down (E)  +  Is Disguised = true  ->  Lean Prop     Self   Lean X: +1
On Key Down (R)  +  Is Disguised = true  ->  Rotate Prop   Self   Degrees: 45
Do not drive Lean Prop from On Update. It accumulates and clamps each axis to −1…1, so an update-driven lean reaches full tilt within a few frames and stays there. For a smooth analogue lean from a stick or mouse axis, call PropCloneMotion.SetLeanInput(Vector2) from your own script instead — that overload sets the value rather than adding to it. Passing Vector2.zero returns the prop smoothly to upright.

Whistle, and the hunter reacting to it

─ On the hider ────────────────────────────────────────────
On Key Down (F)  +  Is Disguised = true
                 +  Is Whistle On Cooldown = false
  ->  Play Whistle   Self

─ On the hunter ───────────────────────────────────────────
On Hunter Hears Whistle
  ->  your directional indicator, alert sound, or AI reaction

On Hunter Hears Whistle fires on the hunter only when the whistle happens within the configured range. The lower-level On Whistle event fires for every whistle regardless of distance, and carries the world position — do your own range check in the condition if you use that one.

Drive a cooldown ring straight from the Whistle Cooldown property getter (0–1) into an Image fill, or drop the Prop Whistle Cooldown UI component onto the image and it does it for you.

A Full Round Loop

Registration, start, elimination, result, restart. Registration is deliberately explicit: nothing joins a round by existing.

─ On each hider ───────────────────────────────────────────
On Start
  ->  Register Prop Player      Hider = Self

On Vital Depleted   (Target = Self)
  ->  Mark Prop Eliminated      Target = Self
  ->  Report Prop Eliminated    Eliminated = Self

─ On a scene-level object ─────────────────────────────────
On Start  (or a Start button)
  ->  Reset All Stats           (each hider)
  ->  Start Round

On Round State Changed  +  Is Round Phase = Hunt
  ->  Move To Spawn Point       Target = <hunter>   Spawn Point = Prop Player

On Hunters Win   ->  show your "hunters win" screen
On Props Win     ->  show your "props win" screen
Start Round begins round 2 directly from Round End — no Reset needed, so one button can run a whole session. Reset Round exists only for lobbies that need to return to Waiting. Assign a different Round Settings asset to a Start Round instruction to shape that round alone: a long tutorial prep, a sudden-death hunt.

For the scoreboard, bind Props Alive and Props Registered into a label for an "x of y remaining" display, Round Number for the match counter, and Round Timer (mm:ss) for the clock — none of which needs a script.

Lobby Board UI

The package ships three sample view components under Samples/BasicHideAndSeek/UI/: Lobby Board (the root panel), Player Row (one row per prop player) and Round Timer Display. They are pure views — all data comes from Round System and Transform Controller events, so you can replace the prefab visuals without touching the scripts.

Canvas  (Screen Space - Overlay, Canvas Scaler at 1920x1080)
+-- LobbyPanel                  <- LobbyBoard component here
    +-- StateLabel              (Text)
    +-- RoundNumberLabel        (Text)
    +-- PropsAliveLabel         (Text)
    +-- TimerLabel              (Text)   <- RoundTimerDisplay here
    +-- PlayerListScroll        (Scroll View)
        +-- Content             (Vertical Layout Group + Content Size Fitter)
            +-- PlayerRow       (prefab, instantiated one per player)
                +-- NameLabel · PropIcon · AliveIndicator · EliminatedIndicator

Nothing to wire for the player list. The board mirrors the round's own roster: on enable it rebuilds from RegisteredPropPlayers, then keeps itself in step through the register and unregister events. A panel that starts inactive and is revealed mid-round therefore shows the correct list the moment it appears, and alive/eliminated badges are re-read on every refresh, so starting a new round un-marks every row without the board tracking anything itself.

Leave Round System empty on both Lobby Board and Round Timer Display to bind to whichever round system is in the scene — including one spawned after the panel. The prop icon comes from the Thumbnail Sprite on each Prop Definition.

Saving Prop Choices

Nothing in the package persists, deliberately — see Core Concepts. What you usually want to save is your own content: which props a player has unlocked, cosmetics, match history. The durable handle for all of it is Prop Definition ID.

─ Saving ──────────────────────────────────────────────────
Current Prop ID   (property getter)  ->  a GC2 Global Variable
                                     ->  a Remember / Memory component

─ Restoring ───────────────────────────────────────────────
On Start  (after the load)
  ->  Transform Into Prop (by ID)
        Target: Self
        ID:     <the saved string, or the Prop ID getter>

The ID is a stable GUID generated once, never changed, guarded against duplication, and safe in a save file or a network payload. Store the strings and look the definitions back up through the registry at load time.

No pre-built Memory component ships for the disguise itself, and that is on purpose: when a disguise should be restored is a game design question with more than one right answer. Doing it yourself is a few nodes in a graph and needs nothing from the package that is not already public.