GAME 1303 — Helper Blueprints: Build Guide

Unreal Engine 5.8 · Enhanced Input · UMG
Instructor build instructions for the GAME1303_Helpers content pack

This is the Unreal counterpart to the Unity HelperScripts library. Blueprints are
binary assets, so they can't be written as text files — this document is a
node-by-node specification for building each one in the editor, once, and exporting
the result as a content pack for students.

Build order matters. BPI_Interactable and BP_GameState are referenced by
later assets, so build them in the order given.


Project setup before you start

  1. New project → Games → Blank → Blueprint, Starter Content off. Name it GAME1303_HelperSource.
  2. Edit → Plugins → enable Paper 2D and Enhanced Input. Restart.
  3. Edit → Project Settings → Input → Default Classes:
    • Default Player Input Class → EnhancedPlayerInput
    • Default Input Component Class → EnhancedInputComponent
  4. Create Content/GAME1303/ with subfolders:
Content/GAME1303/
  Core/          BP_GameState, BP_TriggerZone, BP_Checkpoint
  Movement/      BPC_PlayerMove2D, BPC_PlayerMove3D
  Verbs/         BPC_Dash, BPC_Grabber, BPC_Shooter, BP_Projectile,
                 BPC_Swapper, BPC_Grappler, BPC_Interactor, BPI_Interactable,
                 BP_DialogueTrigger
  Feedback/      BP_SoundPlayer, BPC_ScreenShake, BPC_HitFlash
  Progression/   BPC_Health, BP_Score, BP_Spawner, BP_ObjectPool
  UI/            WBP_GameUI, WBP_HUD, WBP_Title, WBP_Pause, WBP_Win, WBP_Lose
  Testing/       BP_PlaytestLogger
  Input/         IA_Move, IA_Jump, IA_Dash, IA_Grab, IA_Fire, IA_Swap,
                 IA_Grapple, IA_Interact, IA_Pause, IMC_Default
  1. Create these tags (Project Settings → not GameplayTags — plain Actor Tags used in the Details panel): Player, Grabbable, Swappable, Anchor, Enemy.

Exporting the pack when finished

Right-click Content/GAME1303 → Migrate… → choose a staging project's Content
folder. Zip the result as GAME1303_Helpers.zip and upload to Canvas → Files → HelperScripts.


Conventions used throughout

Convention Rule
Prefix BP_ actor · BPC_ ActorComponent · BPI_ interface · WBP_ widget · IA_/IMC_ input
Variables Anything a student should tune is Instance Editable ✔ and Blueprint Read Only ✘, with a Tooltip and a Category
Categories Match the Unity headers: Movement, Jump, Feel, Rules, Events, Debug
Events Expose an Event Dispatcher for anything the Unity version raises as a UnityEvent
Comments Every graph gets a comment box (C key) with the same explanation text as the C# header

Set every tunable variable's Tooltip. The Unity library teaches through its
Inspector tooltips; students will look for the same thing in the Details panel, and
a variable with no tooltip is a variable they will not touch.


Core

BP_GameState — Actor

The single source of truth. Unity equivalent: GameStateManager.cs.

Variables

Name Type Default Category Tooltip
CurrentState E_GameState (enum) Playing Debug Read-only at runtime
RestartKey Key R Restart Key that restarts while developing
RestartDelay Float 0.0 Restart The consequence dial. Under 0.3s failure feels blameless; over 1.5s it feels dreadful
Lives Integer 0 Lives 0 = disabled. Above 0, Lose spends a life
LivesRemaining Integer 0 Lives Runtime only
RespawnAtCheckpoint Boolean false Respawn Send player to last checkpoint instead of reloading
PlayerRef Actor (object ref) none Respawn Leave empty to find by tag
ShowDebugState Boolean true Debug Draws state on screen. Temporary UI until Ex 7

First create the enum: right-click Core/ → Blueprints → Enumeration, name it
E_GameState, with entries Playing, Paused, Won, Lost.

Event Dispatchers

OnWin, OnLose, OnRestart, OnPause, OnResume, and OnStateChanged (with one
input parameter of type E_GameState).

Graph — Event BeginPlay

Event BeginPlay
  → Set LivesRemaining = Lives
  → Branch (IsValid PlayerRef)
      False → Get All Actors With Tag ("Player") → Get (0) → Set PlayerRef
  → Enable Input (Get Player Controller)

Function — Win

Win
  → Branch (CurrentState == Playing)          // ignore a second goal touch
      True → Set CurrentState = Won
           → Call OnStateChanged (Won)
           → Call OnWin

Function — Lose

Lose
  → Branch (CurrentState == Playing)  — False: return
  → Branch (Lives > 0)
      True  → Decrement LivesRemaining
            → Branch (LivesRemaining > 0)
                True → Set Timer by Function Name ("DoRestart", RestartDelay) → return
  → Set CurrentState = Lost
  → Call OnStateChanged (Lost) → Call OnLose
  → Branch (RestartDelay > 0 OR RespawnAtCheckpoint)
      True → Set Timer by Function Name
               (RespawnAtCheckpoint ? "DoRespawn" : "DoRestart", RestartDelay)

Function — DoRestart

DoRestart
  → Set Global Time Dilation (1.0)        // always clear pause before travel
  → Call OnRestart
  → Open Level (by Name: Get Current Level Name)

The trap: Open Level needs the level's name, not its path, and the level
must appear in Project Settings → Packaging → List of maps to include in a
packaged build
. This works in the editor and fails silently in a package — which
is exactly why students build in Week 13.

Function — DoRespawn

DoRespawn
  → Branch (IsValid PlayerRef)  — False: call DoRestart
  → Set Actor Location (PlayerRef, BP_Checkpoint::LastPosition)
  → Get Component by Class (CharacterMovement) → Set Velocity (0,0,0)
  → Get Component by Class (BPC_Health) → Revive        (if present)
  → Set CurrentState = Playing → Call OnStateChanged (Playing)

Functions — TogglePause / Pause / Resume

Pause    → Set Global Time Dilation (0.0) → Set CurrentState = Paused
         → Call OnStateChanged → Call OnPause
Resume   → Set Global Time Dilation (1.0) → Set CurrentState = Playing
         → Call OnStateChanged → Call OnResume

Use Set Global Time Dilation, not a Boolean "isPaused" flag. The Screen Layout
milestone checks that pause actually freezes gameplay rather than just drawing
an overlay.

Graph — Event Tick (debug display)

Event Tick
  → Branch (ShowDebugState)
      True → Draw Text (Print String, Duration 0, Key "state")
             text = CurrentState → to String, appended with LivesRemaining

BP_TriggerZone — Actor

Unity equivalent: TriggerZone.cs. The workhorse of the whole library.

Components

Variables

Name Type Default Tooltip
RequiredTag Name Player Only actors with this tag fire the events. Empty = anything
TriggerOnce Boolean false Fire once and stop. ON for goals, OFF for hazards
HasFired Boolean false Runtime only

Event Dispatchers

OnEnter, OnExit

Graph

On Component Begin Overlap (Box)
  → Branch (RequiredTag is None) OR (Other Actor → Actor Has Tag (RequiredTag))
      False → return
  → Branch (TriggerOnce AND HasFired) → True: return
  → Set HasFired = true
  → Call OnEnter

On Component End Overlap (Box)   → same tag check → Call OnExit

How students use it: place the actor, then in the level select it and bind
OnEnter in the Details → Events panel, or from the Level Blueprint, to
BP_GameState → Win.

The Unreal-specific trap to document for students: always filter with the tag
check or a Cast To BP_Player. Without it a stray physics cube rolling into the
goal wins the game.


BP_Checkpoint — Actor

Unity equivalent: Checkpoint.cs.

Components

Box (Box Collision, OverlapAllDynamic) + FlagMesh (visible marker)

Variables

Name Type Default Tooltip
RequiredTag Name Player
SpawnOffset Vector (0,0,50) Where the player actually reappears
Claimed Boolean false Runtime

LastPosition must persist across respawns. Store it on your Game Instance
(create BP_GAME1303_GameInstance with a CheckpointLocation Vector variable, and
set it in Project Settings → Maps & Modes → Game Instance Class).

Graph

On Component Begin Overlap
  → Branch (NOT Claimed) AND (Actor Has Tag RequiredTag)
  → Set Claimed = true
  → Get Game Instance → Cast to BP_GAME1303_GameInstance
      → Set CheckpointLocation = GetActorLocation + SpawnOffset
  → Call OnActivated  (dispatcher — students hook a sound or flag animation)

Teaching note for the exercise text: checkpoint spacing is the difficulty
curve. Far apart makes each attempt precious; close together makes players willing
to try the risky thing.


Movement

BPC_PlayerMove2D — Actor Component

Unity equivalent: PlayerMove2D.cs. Most Unreal 2D work can lean on
CharacterMovementComponent, so this component configures and extends it rather
than replacing it.

Variables

Name Type Default Category Tooltip
MoveSpeed Float 520 Movement Max Walk Speed, cm/s
Acceleration Float 3000 Movement The feel value. Try 500 then 4000
Deceleration Float 2600 Movement Braking Deceleration Walking
JumpHeight Float 620 Jump Jump Z Velocity
CoyoteTime Float 0.12 Jump Grace period to still jump after leaving a ledge
JumpBuffer Float 0.12 Jump Grace period for a jump pressed before landing
FallMultiplier Float 1.8 Jump Gravity while falling. Above 1 = snappier fall than rise
ConstrainToPlane Boolean true 2D Locks side-scrolling to one plane

Graph — Event BeginPlay

Event BeginPlay
  → Get Owner → Cast To Character → Get Character Movement
  → Set Max Walk Speed (MoveSpeed)
  → Set Max Acceleration (Acceleration)
  → Set Braking Deceleration Walking (Deceleration)
  → Set Jump Z Velocity (JumpHeight)
  → Branch (ConstrainToPlane)
      True → Set Plane Constraint Enabled (true)
           → Set Plane Constraint Normal (0, 1, 0)

Functions — DoMove / DoJump

DoMove (input: Vector2D)
  → Break Vector2D
  → Add Movement Input (World Direction: Get Actor Right Vector, Scale: X)
  → [3D only] Add Movement Input (Forward Vector, Scale: Y)

DoJump
  → Branch (Is Falling)
      False → Jump
      True  → Branch (Time Since Left Ground < CoyoteTime) → True: Jump
            → False: Set JumpBufferedUntil = Now + JumpBuffer

Coyote time and jump buffering need two float variables (LeftGroundAt,
JumpBufferedUntil) updated on Tick — this is the one place the Blueprint version is
meaningfully more work than the C# one. Document it heavily in the graph comment.

Fall multiplier — Event Tick

Event Tick
  → Branch (Character Movement → Velocity.Z < 0)
      True → Set Gravity Scale (BaseGravity * FallMultiplier)
      False → Set Gravity Scale (BaseGravity)

BPC_PlayerMove3D — Actor Component

Same variables plus TurnSpeed, LocomotionMode (enum E_Locomotion:
Smooth / Teleport), UseComfortVignette, TeleportDistance.

Movement is camera-relative:

DoMove (Vector2D)
  → Get Control Rotation → Break Rotator → make Rotator (0, 0, Yaw)
  → Get Forward Vector → Add Movement Input (scale Y)
  → Get Right Vector   → Add Movement Input (scale X)

VR note to carry into the exercise: if LocomotionMode is Smooth, drive a
post-process vignette from movement intensity. Comfort is graded, and smooth
locomotion without a vignette makes a meaningful number of people ill.


Verbs

One per core mechanic. Students attach exactly one. All are Actor Components so
they drop onto any Character.

BPC_Dash

Variable Type Default Tooltip
DashSpeed Float 2400
DashDuration Float 0.15 Short = a decision. Long = a movement option
DashCooldown Float 0.6
SteerableDash Boolean false A design choice, not a setting. OFF makes the dash a commitment
TryDash
  → Branch (CooldownRemaining <= 0 AND NOT IsDashing)
  → Set DashDirection = (last input vector, or Actor Forward if none)
  → Set IsDashing = true, DashTimeLeft = DashDuration, CooldownRemaining = DashCooldown
  → Call BP_SoundPlayer → Play ("dash")
  → Call BPC_ScreenShake → Shake (0.12, 0.08)

Event Tick
  → Branch (IsDashing)
      True → Launch Character (DashDirection * DashSpeed, XYOverride ✔, ZOverride ✘)
           → DashTimeLeft -= DeltaTime → Branch (<= 0) → Set IsDashing = false
  → CooldownRemaining -= DeltaTime

BPC_Grabber

Variable Type Default Tooltip
GrabRange Float 150
HoldOffset Vector (80, 0, 20)
ThrowForce Float 1200
ThrowArc Float 0.35 Upward bias so objects arc instead of sliding
DisablePhysicsWhileHeld Boolean true Leave on unless you want physics chaos
CarrySpeedMultiplier Float 1.0 The design value. Below 1 makes carrying a cost
HeldActor Actor ref none Runtime
ToggleGrab
  → Branch (IsValid HeldActor) → True: Release, False: TryGrab

TryGrab
  → Sphere Overlap Actors (GrabRange, filter by tag "Grabbable")
  → sort by distance → take nearest → Set HeldActor
  → Set Simulate Physics (false)  [if DisablePhysicsWhileHeld]
  → Attach Actor To Component (Owner mesh, Snap to Target, HoldOffset)
  → Character Movement → Set Max Walk Speed (Base * CarrySpeedMultiplier)

Release
  → Detach From Actor (Keep World)
  → Set Simulate Physics (true)
  → Add Impulse (Forward Vector + Up * ThrowArc, normalized * ThrowForce)
  → Restore Max Walk Speed → Clear HeldActor

BPC_Shooter + BP_Projectile

BPC_Shooter variables: ProjectileClass (class ref), MuzzleOffset (Vector),
ProjectileSpeed (1400), FireRate (4.0), Automatic (bool), Spread (float, deg),
Recoil (float), ShakeAmount (0.08).

TryFire
  → Branch (CooldownRemaining <= 0) — False: return
  → Set CooldownRemaining = 1 / FireRate
  → Spawn Actor From Class (ProjectileClass) at Owner location + MuzzleOffset
      → rotation = aim direction, rotated by Random Float in Range (-Spread, Spread)
  → Cast to BP_Projectile → Launch (direction, ProjectileSpeed, Owner)
  → Branch (Recoil > 0) → Add Impulse (-direction * Recoil)
  → BPC_ScreenShake → Shake (ShakeAmount, 0.06) · BP_SoundPlayer → Play ("shoot")

BP_Projectile — Actor. Components: Sphere (collision, generate overlaps) +
Mesh + Projectile Movement.

Variable Default Tooltip
Lifetime 2.5 NEVER leave at 0. Use Initial Life Span on the actor
Damage 1
DestroyOnHit true
DamageTags [Enemy]
ImpactEffect none An impact puff sells a hit more than the hit does
Event BeginPlay → Set Life Span (Lifetime)
On Component Begin Overlap (Sphere)
  → Branch (Other Actor != Owner)
  → Branch (Other Actor Has Tag in DamageTags)
      True → Get Component by Class (BPC_Health) → Take Damage (Damage)
  → Spawn Emitter at Location (ImpactEffect) · Play ("impact")
  → Branch (DestroyOnHit) → Destroy Actor

BPC_Swapper

SwapRange (600), SwapTargetTag (Swappable), SwapCooldown (0.4),
RequireLineOfSight (bool), PreserveVelocity (bool), SwapEffect.

DoSwap
  → Sphere Overlap Actors (SwapRange, tag SwapTargetTag)
  → Branch (RequireLineOfSight)
      True → Line Trace By Channel (Owner → candidate); discard if blocked
  → nearest valid → store MyLocation and TheirLocation
  → Set Actor Location (Owner, TheirLocation)
  → Set Actor Location (Target, MyLocation)
  → Branch (NOT PreserveVelocity) → Set Velocity (0,0,0)

RequireLineOfSight is the whole design. OFF = the puzzle is about range and
positioning. ON = the puzzle is about sightlines and cover. Pick one and build
levels for it.

BPC_Grappler

MaxRange (800), AnchorTag (Anchor), PullSpeed (1800),
ReleaseOnArrive (bool), ArriveDistance (60), plus a Cable Component added to
the owning Character for the visible line.

TryAttach  → Sphere Overlap (MaxRange, tag AnchorTag) → nearest → Set AnchorActor
           → Cable → Set Visibility (true), Set Attach End To (AnchorActor)
Detach     → Clear AnchorActor → Cable Set Visibility (false)
Event Tick → Branch (IsValid AnchorActor)
               True → direction = Anchor - Owner, normalized
                    → Branch (ReleaseOnArrive AND distance < ArriveDistance) → Detach
                    → Set Velocity (direction * PullSpeed)

BPI_Interactable — Blueprint Interface

Functions: Interact · Focus · Unfocus · GetPrompt (returns Text) ·
CanInteract (returns Boolean).

Any actor implements this and fills in the events. This is the backbone of the
Narrative track.

BP_DialogueTrigger — Actor

Unity equivalent: DialogueTrigger.cs. Used in Ex 6 and by every Narrative project.

Components: Box (Box Collision, OverlapAllDynamic).

Variable Type Default Tooltip
Beats Array of Text empty One line per element. Keep them short — two lines of screen text maximum
AdvanceMode E_Advance enum (Timed / OnInput) Timed
DisplayDuration Float 3.0 Seconds per beat in Timed mode
GapDuration Float 0.25 A short gap reads as a breath
PlayOnce Boolean true
PlayOnEnter Boolean true Off = call Play yourself from an Interactable

Dispatchers: OnBeat (Text parameter), OnStarted, OnFinished.

Play
  → Branch (NOT IsPlaying AND NOT (PlayOnce AND Spent))
  → Set Spent = true, IsPlaying = true → Call OnStarted
  → ForEachLoop (Beats)
      → Call OnBeat (line) · BP_SoundPlayer → Play ("dialogue")
      → Branch (AdvanceMode == Timed)
          True  → Delay (DisplayDuration)
          False → wait until Advance is called  (use a Gate + custom event)
      → Delay (GapDuration)
  → Set IsPlaying = false → Call OnFinished

Blueprint gotcha: a ForEachLoop cannot contain a Delay — latent nodes break the
loop. Build this as a recursive custom event (ShowBeat(Index)) that delays and
then calls itself with Index + 1, stopping when Index >= Beats.Length. Put that
warning in the graph comment; students will hit it when they extend this.

The design note to carry into Ex 6: environmental storytelling beats a wall of
text. Three short beats attached to things the player chose to look at land harder
than twelve lines that play automatically.


BPC_Interactor

InteractRange (180), FocusedActor (Actor ref), ShowPrompt (bool).

Event Tick
  → Sphere Overlap Actors (InteractRange) → filter Does Implement Interface (BPI_Interactable)
  → nearest → Branch (changed from FocusedActor)
      True → old: Unfocus · new: Focus · Set FocusedActor
DoInteract → Branch (IsValid FocusedActor) → Interact (message)

Feedback

BP_SoundPlayer — Actor

Unity equivalent: SoundPlayer.cs. Place one per level; students call it via
Get Actor Of Class.

Create a Structure S_Sound with members: Name (Name), Cue (Sound Base),
Volume (Float), PitchVariance (Float), MaxConcurrent (Integer).

Variable Type Tooltip
Sounds Array of S_Sound Name them jump, shoot, hit, win, lose — the helpers already call those names
MusicTrack Sound Base
MusicVolume Float 0.5
MasterVolume Float 1.0
Play (input: Name)
  → Find item in Sounds array by Name  (Array Find, or a Map<Name, S_Sound>)
  → Branch (found AND ActiveCount[Name] < MaxConcurrent)
  → Play Sound 2D (Cue, Volume, Pitch = 1 + Random Float in Range (-PV, +PV))

PitchVariance is the value that matters. Repeated identical sounds fatigue
the ear within about five plays; 0.08 makes a footstep loop bearable for an hour.

Reminder to put in the exercise: audio is 2 points of the Week 15 rubric and
the most-skipped points in the course. Music plus three effects is the whole
requirement.

BPC_ScreenShake

Use Unreal's built-in system rather than hand-rolling it:

  1. Create a Legacy Camera Shake Blueprint, CS_Hit, parent class LegacyCameraShake.
  2. Set Oscillation → Loc → X/Y Amplitude ~2.0, Frequency ~25, Duration 0.15.
  3. BPC_ScreenShake::Shake (Scale) → Get Player Camera Manager → Start Camera Shake (CS_Hit, Scale).

The mistake everyone makes is shaking ten times too hard. Amplitude 2 is a
solid hit; amplitude 20 is a building collapsing. Good shake is felt, not seen.
Add a GlobalScale variable that multiplies every request — setting it to 0 is a
real accessibility option.

BPC_HitFlash

FlashColor (white), FlashDuration (0.06), FlashCount (1).

Requires a material with a scalar parameter FlashAmount on the mesh. Create a
Dynamic Material Instance on BeginPlay, then:

Flash
  → Set Scalar Parameter Value (FlashAmount, 1.0)
  → Delay (FlashDuration)
  → Set Scalar Parameter Value (FlashAmount, 0.0)

Keep it short. Anything over 0.2s looks broken.


Progression

BPC_Health

Variable Default Tooltip
MaxHealth 3
Current 3 Runtime
InvulnerabilityTime 0.5 Below ~0.3 and overlapping hazards kill instantly. The most common "my game is broken" bug
DestroyOnDeath false Turn OFF for the player

Dispatchers: OnHealthChanged (Integer), OnDamaged, OnHealed, OnDeath.

TakeDamage (Integer)
  → Branch (NOT IsDead AND Now > InvulnerableUntil)
  → Current = Clamp (Current - Amount, 0, MaxHealth)
  → InvulnerableUntil = Now + InvulnerabilityTime
  → Call OnHealthChanged (Current) · OnDamaged · Play ("hurt")
  → Branch (Current <= 0) → Die

Also expose TakeOneDamage, Heal, Kill, and Revive so students can bind them
directly to a BP_TriggerZone event with no wiring.

BP_Score — Actor

ComboWindow (2.0), ComboStep (0.5), MaxCombo (8), SaveHighScore (bool).
Dispatchers OnScoreChanged (Integer), OnComboChanged (Integer), OnNewHighScore.

High score persists via a SaveGame object (BP_GAME1303_Save with an
HighScore Integer) — Save Game to Slot / Load Game from Slot.

Expose AddOne, AddFive, AddTen, AddFifty, AddHundred as separate functions
so pickups can bind straight to them from the Details panel.

ComboWindow is the design question. Short (1s) makes combos about speed and
rewards aggression. Long (5s) makes them about not making mistakes.

BP_Spawner — Actor

Mode (enum E_SpawnMode: Continuous / Waves / Manual), PrefabClass,
SpawnInterval (2.0), WaveSize (5), WaveInterval (6.0),
RampRate (0.02), MinimumInterval (0.35), SpawnPoints (array of Actor refs),
MaxAlive (0).

Event Tick (Continuous)
  → Timer -= DeltaTime → Branch (<= 0)
  → Branch (MaxAlive == 0 OR AliveCount < MaxAlive)
  → SpawnOne
  → CurrentInterval = Max (MinimumInterval, CurrentInterval * (1 - RampRate))
  → Timer = CurrentInterval

RampRate IS your difficulty curve. It is not a technical setting. Students
graph it in Ex 9 before they tune it.

BP_ObjectPool — Actor

PrefabClass, InitialSize (20), Expandable (bool), plus arrays Available and
All. Functions Get (Location, Rotation) and Return (Actor) — Return calls
Set Actor Hidden In Game ✔, Set Actor Enable Collision ✘, and Set Actor
Tick Enabled
✘ rather than destroying.

Reach for it when anything spawns more than about ten of something per second.


UI

Widget structure

Unity puts all five screens in one UXML. UMG works better with one widget per
screen plus a switcher
, so:

WBP_GameUI

Event Construct
  → Get Actor Of Class (BP_GameState)
  → Bind Event to OnStateChanged → HandleStateChanged

HandleStateChanged (E_GameState)
  → Switch on E_GameState
      Playing → Set Active Widget Index (1)   // HUD
      Paused  → Set Active Widget Index (2)
      Won     → Set Active Widget Index (3)
      Lost    → Set Active Widget Index (4)

Add it to the viewport from BP_GameState's BeginPlay: Create Widget → Add to Viewport.

Required named widgets

Widget Elements
WBP_Title GameTitle (Text), StartButton, QuitButton, ControlsText
WBP_HUD HealthBar (Progress Bar), ScoreText, ComboText
WBP_Pause ResumeButton, PauseRestartButton, PauseMenuButton
WBP_Win WinTitle, FinalScoreText, PlayAgainButton, WinMenuButton
WBP_Lose LoseTitle, RetryButton, LoseMenuButton

Every button name is unique, mirroring the Unity fix — two buttons sharing a
purpose but each needing its own binding. A button with no binding is a dead end,
which costs a point in Screen Layout.

Live HUD binding

This is what makes the HUD "live" rather than hard-coded, and the grader checks it:

WBP_HUD :: Event Construct
  → Get Player Pawn → Get Component by Class (BPC_Health)
  → Bind Event to OnHealthChanged → UpdateHealthBar
  → Get Actor Of Class (BP_Score) → Bind to OnScoreChanged → UpdateScore

UpdateHealthBar (Integer)
  → HealthBar → Set Percent (Current / MaxHealth)

Do not use a property binding on the Progress Bar — those poll every frame and
teach the wrong habit. Bind to the dispatcher.

Styling

Ship the widgets deliberately plain, as with the Unity USS. Restyling is the graded
design decision in Ex 7 and Screen Layout. Provide a WBP_ButtonStyle and a single
accent-colour parameter so a student can transform the whole UI by changing one
value — that makes the restyle achievable in the 75-minute block.

Put the controls on the title screen. ControlsText exists for exactly that, and
it is worth real points in the Week 15 build rubric.


Testing

BP_PlaytestLogger — Actor

TesterName (String), LogSessionStartEnd (bool), HeartbeatSeconds (30),
EchoToConsole (bool).

Unreal has no direct File.WriteAllText in Blueprint. Two options:

  1. Recommended: enable the free "Blueprint File Utilities"-style plugin, or add
    a tiny C++ UBlueprintFunctionLibrary with one SaveStringToFile node. Ten lines
    of C++, exposed as a single Blueprint node.
  2. No-C++ fallback: log with Print String (Print to Log ✔) and have students
    retrieve Saved/Logs/<ProjectName>.log after the session. Less tidy, works today.
Log (String)
  → Format Text ("[{Time}s] {Message}")
  → Append to LogLines array
  → Branch (EchoToConsole) → Print String
  → SaveStringToFile (Join(LogLines), Saved/Playtests/playtest_<TesterName>_<stamp>.txt)

Expose LogDeath, LogWin, LogLevelComplete, LogCheckpoint as bindable
functions so students hook them to BPC_Health::OnDeath and BP_GameState::OnWin
from the Details panel.

The Ex 10 rubric asks for playtest evidence. This file is the evidence.


Input assets

Build all of these in Content/GAME1303/Input/.

Asset Value Type Default bindings in IMC_Default
IA_Move Axis2D W (Swizzle YXZ) · S (Swizzle YXZ + Negate) · A (Negate) · D · Gamepad Left Thumbstick
IA_Jump Digital Space Bar · Gamepad Face Button Bottom
IA_Dash Digital Left Shift · Gamepad Right Trigger
IA_Grab Digital E · Gamepad Face Button Left
IA_Fire Digital Left Mouse Button · Gamepad Right Trigger
IA_Swap Digital Q · Gamepad Face Button Top
IA_Grapple Digital Left Mouse Button · Gamepad Right Shoulder
IA_Interact Digital E · Gamepad Face Button Bottom
IA_Pause Digital Escape · Gamepad Special Right

The Swizzle and Negate modifiers on IA_Move are Unreal's equivalent of Unity's 2D
composite, and they trip up every student the first time. Ship IMC_Default
pre-built so Week 1 doesn't disappear into it — but still walk through why it
works in the lecture, because students will need to add IA_Dash themselves in
Week 3.


Build checklist

29 assets. Budget a full day, and build them in the order listed — the later ones
reference the earlier ones.