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
- New project → Games → Blank → Blueprint, Starter Content off. Name it
GAME1303_HelperSource. - Edit → Plugins → enable Paper 2D and Enhanced Input. Restart.
- Edit → Project Settings → Input → Default Classes:
- Default Player Input Class →
EnhancedPlayerInput - Default Input Component Class →
EnhancedInputComponent
- Default Player Input Class →
- 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
- 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 Levelneeds 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
Box(Box Collision) as root — Collision Preset OverlapAllDynamic, Generate Overlap Events ✔DebugMesh(Static Mesh, Cube) — Hidden in Game ✔, translucent green material, so students can see invisible triggers in the editor
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 aCast 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
LocomotionModeis 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)
RequireLineOfSightis 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 withIndex + 1, stopping whenIndex >= 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))
PitchVarianceis 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:
- Create a Legacy Camera Shake Blueprint,
CS_Hit, parent classLegacyCameraShake. - Set Oscillation → Loc → X/Y Amplitude ~2.0, Frequency ~25, Duration 0.15.
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 aGlobalScalevariable 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.
ComboWindowis 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
RampRateIS 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_Title,WBP_HUD,WBP_Pause,WBP_Win,WBP_Lose— each a User WidgetWBP_GameUI— the container, holding a Widget Switcher with all five as children
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:
- Recommended: enable the free "Blueprint File Utilities"-style plugin, or add
a tiny C++UBlueprintFunctionLibrarywith oneSaveStringToFilenode. Ten lines
of C++, exposed as a single Blueprint node. - No-C++ fallback: log with Print String (Print to Log ✔) and have students
retrieveSaved/Logs/<ProjectName>.logafter 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_Moveare Unreal's equivalent of Unity's 2D
composite, and they trip up every student the first time. ShipIMC_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 addIA_Dashthemselves 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.