# Part 2: Create the Player
[← Previous: Part 1 - Project Setup](part-1-project-setup.md) | [Back to Index](README.md) | [Next: Part 3 - Create the Bullet →](part-3-create-bullet.md)
---
## Step 2.1: Create Player Blueprint
1. In Content Browser, double-click "Blueprints" folder to open it
2. Right-click in empty space → **Blueprint Class**
3. In the popup window "Pick Parent Class":
- Click "Pawn" (NOT Character - we want simple 2D control)
- Click "Select"
4. Name the new Blueprint: `BP_Player`
5. Double-click "BP_Player" to open the Blueprint Editor
### Expected Result
A new tab opens showing the Blueprint Editor with:
- Components panel on the left
- Viewport in the center
- Details panel on the right
- Tab bar above the Viewport with: Viewport | Construction Script | Event Graph (third from left)
---
## Step 2.2: Add Player Visual Components
1. In the Components panel (left side), you'll see "DefaultSceneRoot"
- Click on "DefaultSceneRoot" to select it (this ensures new components are added as children)
2. With DefaultSceneRoot selected, click **"Add"** button (green, top of Components panel)
- Search for "Paper Sprite"
- Click "Paper Sprite" to add it (it will be added as a child of DefaultSceneRoot)
- Rename it to `PlayerSprite` (click on it, then press F2)
> **NOTE:** If Paper Sprite is not available:
>
> - Go to Edit → Plugins (from main menu bar)
> - Search for "Paper2D"
> - Make sure "Paper2D" plugin is ENABLED
> - Restart the editor if prompted
3. With "PlayerSprite" selected, look at Details panel (right side):
- Find "Source Sprite" property
- For now, leave it empty (we'll create sprites later)
- Under "Transform", set Scale to `(1.0, 1.0, 1.0)`
4. With DefaultSceneRoot still selected, click **"Add"** button again:
- Search for "Box Collision"
- Click "Box Collision" to add it (it will be added as a child of DefaultSceneRoot)
- Rename it to `PlayerCollision`
5. With "PlayerCollision" selected, in Details panel:
- Under "Shape", set Box Extent to: `X=25, Y=25, Z=10`
- Under "Collision", click "Collision Presets" dropdown
- Select "Custom..."
- Set "Collision Enabled" to "Query Only (No Physics Collision)"
- Enable "Generate Overlap Events" box
6. With DefaultSceneRoot still selected, click **"Add"** button:
- Search for "Arrow"
- Click "Arrow" to add it (it will be added as a child of DefaultSceneRoot)
- This shows which direction is "forward" (useful for debugging)
7. With DefaultSceneRoot still selected, click **"Add"** button:
- Search for "Cube" → add "Cube" (Static Mesh)
- Rename it to `TempVisual`
- In Details panel, set Scale to `(0.5, 0.5, 0.1)` so it's small and flat
- This provides a visible placeholder until we add proper sprites in [Part 9](part-9-final-setup.md)
8. With DefaultSceneRoot still selected, click **"Add"** button:
- Search for "Camera" → add "Camera"
- Rename it to `PlayerCamera`
- In Details panel, set Location to: `X=0, Y=0, Z=1000` (1000 units above player)
- Set Rotation to: `X=0, Y=-90, Z=0` (looking straight down)
- In Details panel, find "Activation" category → "Auto Activate" checkbox
- This should already be ENABLED by default
- If not, check/enable it - this tells Unreal to use THIS camera when playing
- This camera will follow the player, making testing easy
> **NOTE:** This temporary camera ensures you can see the player during testing.
> In [Part 8](part-8-game-mode-level.md), we'll set up a proper fixed camera for the final game.
### Expected Result
Components panel shows:
```
DefaultSceneRoot
├── PlayerSprite (Paper Sprite)
├── PlayerCollision (Box Collision)
├── Arrow
├── TempVisual (Cube)
└── PlayerCamera (Camera)
```
9. **IMPORTANT - Enable Auto Possession for testing:**
- Click on "BP_Player (Self)" at the top of the Components panel (the root)
- In Details panel, find "Pawn" category
- Find "Auto Possess Player" dropdown (currently set to "Disabled")
- Change it to **"Player 0"**
- This makes the engine automatically possess this pawn when playing, which activates the PlayerCamera
> **NOTE:** This setting will be overridden later when we set up the GameMode
> in [Part 8](part-8-game-mode-level.md), which handles possession automatically.
---
## Step 2.3: Create Player Variables
_(Continue in the same BP_Player Blueprint Editor tab that was opened in Step 2.1)_
1. In the Blueprint Editor, look at the left panel (below the Components panel)
2. Find "My Blueprint" section
3. Under "Variables", click the **"+"** button
4. Create these variables one by one (click +, then set properties in Details):
> **NOTE:** Variables are NOT added to the Components hierarchy. They appear in a
> separate "Variables" list within the "My Blueprint" panel. Variables store
> data values, while Components are physical/visual parts of the actor.
| # | Variable Name | Type | Default Value |
| --- | -------------- | ----------------- | ----------------- |
| 1 | `MoveSpeed` | Float | 750.0 |
| 2 | `BoundsMin` | Vector 2D | X=-850, Y=-450 |
| 3 | `BoundsMax` | Vector 2D | X=850, Y=450 |
| 4 | `FireInterval` | Float | 0.08 |
| 5 | `FireTimer` | Float | 0.0 |
| 6 | `BulletSpeed` | Float | 2200.0 |
| 7 | `MaxLives` | Integer | 3 |
| 8 | `CurrentLives` | Integer | 3 |
| 9 | `VolleySize` | Integer | 3 |
| 10 | `VolleySpread` | Float | 12.0 |
| 11 | `SpecialUsed` | Boolean | false (unchecked) |
| 12 | `BulletClass` | Class Reference\* | (set later) |
> \*For BulletClass: In the type dropdown, go to "Object Types" category → "Actor" → select "Class Reference" (the subcategory under Actor). This will hold reference to bullet blueprint class for spawning.
>
> **TIP:** For Variable 1 (MoveSpeed), click the "eye" icon to make it public/editable. Compile the blueprint (Click on "Compile" third option from left or simply `Ctrl + Alt`) before setting the default value.
5. Click **"Compile"** button (top left, blue checkmark icon)
6. Click **"Save"** button (floppy disk icon next to Compile)
### Expected Result
My Blueprint panel shows:
- All 12 variables you created listed under "Variables"
- A "Components" category with 4 component references (DefaultSceneRoot, PlayerSprite, PlayerCollision, Arrow) - these are automatically created from the components you added in Step 2.2
---
## Step 2.4: Set Up Enhanced Input System
UE5 uses the Enhanced Input System. Before creating movement logic in our Blueprint, we first need to create Input Actions and an Input Mapping Context.
### 0. ENABLE ENHANCED INPUT IN PROJECT SETTINGS (CRITICAL)
Before creating any input assets, you **MUST** configure the project to use Enhanced Input as the default input system:
1. Go to **Edit → Project Settings**
2. In the left sidebar, scroll down to "Engine" section
3. Click on "Input"
4. Find "Default Classes" section (near the top)
5. Set "Default Player Input Class" to: `EnhancedPlayerInput`
6. Set "Default Input Component Class" to: `EnhancedInputComponent`
7. Close Project Settings
> ⚠️ **WARNING:** If you skip this step, WASD controls will NOT work even if everything else is set up correctly!
### 1. SET UP INPUT ASSETS (in Content Drawer)
#### A) CREATE INPUT ACTIONS
1. Open Content Drawer (`Ctrl+Space` or click "Content Drawer" at bottom)
2. In Content folder, right-click → **New Folder** → name it `Input`
3. Inside Input folder, right-click → **Input → Input Action**
- Name it `IA_Move`
- Double-click to open
- Set "Value Type" to `Axis2D (Vector2D)`
- Save and close
4. Create another Input Action: `IA_Fire`
- Set "Value Type" to `Digital (bool)` (default)
5. Create another Input Action: `IA_Special`
- Set "Value Type" to `Digital (bool)`
#### B) CREATE INPUT MAPPING CONTEXT
1. In Input folder, right-click → **Input → Input Mapping Context**
- Name it `IMC_Default`
- Double-click to open
2. Click **"+"** next to "Mappings" to add IA_Move:
- Select "IA_Move" from dropdown
- Click "+" under IA_Move to add key bindings:
> **NOTE:** In Unreal's top-down view, X axis = up/down, Y axis = left/right
**For W key (move UP on screen):**
- Click "+", select "W"
- No modifiers needed (outputs X positive = up)
- TOTAL MODIFIERS FOR W: **0**
**For S key (move DOWN on screen):**
- Click "+", select "S"
- Click "+" next to "Modifiers" → Add "Negate"
- TOTAL MODIFIERS FOR S: **1** (Negate only)
**For A key (move LEFT on screen):**
- Click "+", select "A"
- Click "+" next to "Modifiers" → Add "Swizzle Input Axis Values" → Order: "YXZ"
- Click "+" next to "Modifiers" AGAIN → Add "Negate"
- TOTAL MODIFIERS FOR A: **2** (Swizzle AND Negate)
**For D key (move RIGHT on screen):**
- Click "+", select "D"
- Click "+" next to "Modifiers" → Add "Swizzle Input Axis Values" → Order: "YXZ"
- TOTAL MODIFIERS FOR D: **1** (Swizzle only)
**Summary Table - verify your IMC_Default matches this:**
| Key | Modifiers | Output Vector |
| --- | ---------------------------------------- | -------------- |
| W | (none) | (1, 0) = UP |
| S | Negate | (-1, 0) = DOWN |
| A | Swizzle Input Axis Values (YXZ) + Negate | (0, -1) = LEFT |
| D | Swizzle Input Axis Values (YXZ) | (0, 1) = RIGHT |
After adding all keys, press `Ctrl+S` to SAVE IMC_Default!
3. Click "+" to add **IA_Fire**:
- Select "IA_Fire"
- Add key: `Z`
- Add key: `Left Mouse Button`
4. Click "+" to add **IA_Special**:
- Select "IA_Special"
- Add key: `X`
- Add key: `Right Mouse Button`
5. Save the Input Mapping Context
### 2. NOW OPEN BP_PLAYER BLUEPRINT
1. In Content Drawer, navigate to Content → Blueprints
2. Double-click "BP_Player" to open the Blueprint Editor
3. Click on **"Event Graph"** tab (above the main view, third tab from left, next to Construction Script)
You should see three default events (red nodes):
- Event BeginPlay
- Event ActorBeginOverlap
- Event Tick
### 3. ADD MAPPING CONTEXT IN BEGINPLAY
From "Event BeginPlay" node:
1. Right-click in empty space → search `Get Player Controller` → add it
- Use the one under "Game" → "Player" category
- It has a "Player Index" input (default 0) and "Return Value" output
2. From the blue "Return Value" output, drag and search `Get Enhanced Input Local Player Subsystem`
3. From that blue output, drag and search `Add Mapping Context`
4. For "Mapping Context" input:
- Click dropdown and select `IMC_Default`
- Or drag IMC_Default from Content Drawer
5. Set "Priority" to `0`
6. **CRITICAL - Connect the EXECUTION wire (white wire):**
- The WHITE TRIANGLE pins are different from the BLUE CIRCLE pins!
- Blue circles = DATA (what values to use)
- White triangles = EXECUTION (when to run)
- On "Event BeginPlay", find the WHITE TRIANGLE on the RIGHT side
- On "Add Mapping Context", find the WHITE TRIANGLE on the LEFT side
- Click and DRAG from BeginPlay's white triangle to Add Mapping Context's white triangle
**WITHOUT THIS WHITE WIRE, THE CODE NEVER RUNS!**
Your graph should look like this:
```
┌──────────────────┐ ┌─────────────────────────────────────┐
│ Event BeginPlay │ │ Add Mapping Context │
│ ├────────►│► │
│ │ WHITE │ Target ○────────────────┐ │
└──────────────────┘ WIRE │ Mapping Context: IMC_Default │
│ Priority: 0 │
└─────────────────────────────────────┘
▲
┌──────────────────┐ │
│Get Player │ ┌─────────────────────────┐
│Controller ├───►│ Enhanced Input Local │
│ Player Index [0] │ │ Player Subsystem ├───┘
└──────────────────┘ └─────────────────────────┘
(blue data wires)
```
**VERIFY:** You must have BOTH:
- Blue data wires connecting the nodes (passes the subsystem reference)
- White execution wire from Event BeginPlay to Add Mapping Context (makes it run)
### 4. INPUT DEBUG TEST (verify input works - separate from movement logic)
Before creating movement, let's verify input is working with a separate debug setup. This debug logic is COMPLETELY INDEPENDENT from movement - you can delete it later without affecting anything.
#### a) Create debug variables (these are ONLY for debugging)
- In My Blueprint panel → Variables → click "+"
- Name: `DEBUG_LastMoveInput`
- Type: Vector 2D
- Default Value: (0, 0)
- Compile to save
#### b) Create a SEPARATE debug event using a Custom Event
- Right-click in empty space → search `Custom Event` → add it
- Name it `DEBUG_PrintInput`
- This keeps debug logic completely isolated
#### c) Build the debug logic from DEBUG_PrintInput
1. **Get the current input value:**
- Right-click → "Get Player Controller"
- Drag → "Get Enhanced Input Local Player Subsystem"
- Drag → search "IA_Move" (under Input → Enhanced Action Values)
2. **Compare to last value (only print on change):**
- Right-click → "Not Equal (Vector2D)"
- Connect IA_Move to TOP input
- "Get DEBUG_LastMoveInput" → connect to BOTTOM input
3. **Branch on change:**
- From DEBUG_PrintInput WHITE pin → "Branch"
- Connect != result to Condition
4. **On TRUE (changed):**
- "Set DEBUG_LastMoveInput" = IA_Move value
- Then → "Print String" with IA_Move value
#### d) Call the debug event from Event Tick
- From "Event Tick" WHITE pin → search `DEBUG_PrintInput`
- This adds a node that calls your custom event
**Visual (two separate areas in your graph):**
```
AREA 1 - Event Tick (will later have movement):
┌─────────────┐ ┌─────────────────────┐
│ Event Tick │─────►│ DEBUG_PrintInput │
└─────────────┘ │ (call custom event) │
└─────────────────────┘
AREA 2 - Debug logic (completely separate):
┌─────────────────────┐
│ DEBUG_PrintInput │
│ (Custom Event) │
└──────────┬──────────┘
│
▼
┌─────────────────────────────┐
│ Branch │
│ Condition: IA_Move != Last │
└──────┬──────────────────────┘
│ TRUE
▼
┌──────────────────┐ ┌──────────────┐
│Set DEBUG_LastMove│────►│ Print String │
└──────────────────┘ └──────────────┘
```
#### e) Compile and Save
#### f) Drag BP_Player into level, Press Play (`Alt+P`)
#### g) IMPORTANT: Click inside the game viewport to give it keyboard focus
#### h) Press WASD keys and look at top-left of screen
**Expected:** Message appears ONLY when you press or release a key:
- Press W: prints "X=1.0 Y=0.0"
- Release W: prints "X=0.0 Y=0.0"
- Log is NOT spammed every frame
**TO DISABLE DEBUG LATER:** Simply delete the "DEBUG_PrintInput" call node from Event Tick. The Custom Event and its logic can stay (unused) or be deleted entirely - movement will be unaffected either way.
#### Debugging Steps (if you see "X=0.0 Y=0.0" always)
DEBUGGING STEP 1 - Verify the pawn is possessed
- When playing, look at the Outliner (right panel)
- Find BP_Player in the list
- If it shows a small controller icon next to it, it's possessed
- If NOT possessed: Check that "Auto Possess Player" = "Player 0" in BP_Player
DEBUGGING STEP 2 - Verify the game window has keyboard focus
- After pressing Play, CLICK INSIDE THE GAME VIEWPORT
- The viewport must have focus to receive keyboard input
- Try pressing `Shift+F1` to release mouse, then click viewport again
- Then press WASD again
DEBUGGING STEP 3 - Test with a simpler Print String
- Delete the current Print String setup
- From Event Tick, drag → Print String
- Type "Tick is running" in the In String field (just plain text)
- Play the game - you should see "Tick is running" spam in top-left
- If you DON'T see this: The blueprint isn't running at all
DEBUGGING STEP 4 - Test BeginPlay is running
- From Event BeginPlay, AFTER the Add Mapping Context node
- Add another Print String with text "BeginPlay executed"
- Connect the white wire: BeginPlay → Add Mapping Context → Print String
- Play the game - you should see "BeginPlay executed" once at start
- If you DON'T see this: BeginPlay isn't running (pawn not spawned?)
DEBUGGING STEP 5 - Check if there are MULTIPLE BP_Player in level
- Look in Outliner for multiple BP_Player instances
- Delete ALL of them
- Drag in exactly ONE fresh BP_Player from Content Drawer
- Try again
DEBUGGING STEP 6 - Nuclear option (create fresh test)
- Create a NEW blueprint: "BP_InputTest" (Pawn class)
- Add ONLY: Auto Possess Player = Player 0
- In Event Graph: Event BeginPlay → Add Mapping Context (IMC_Default)
- In Event Graph: Event Tick → Print String (connected to Get IA_Move)
- Drag this minimal test blueprint into level
- If THIS works, something is wrong with your BP_Player
**IF THIS TEST WORKS:** Continue to step 5. You can optionally delete the DEBUG_PrintInput call from Event Tick to stop the debug prints.
**IF THIS TEST FAILS:** Work through debugging steps above.
### 5. MOVEMENT LOGIC - Create this node network
> **NOTE:** If you added debug from step 4, Event Tick already has a wire to DEBUG_PrintInput. To have BOTH debug AND movement run from Event Tick, you need a Sequence node (one output pin = one wire only).
#### a) INSERT A SEQUENCE NODE between Event Tick and debug
- Delete the wire from Event Tick to DEBUG_PrintInput
- Right-click → search `Sequence` → add it
- Connect Event Tick → Sequence (input)
- Connect Sequence "Then 0" → DEBUG_PrintInput
- Sequence "Then 1" will be used for movement
```
RESULT:
Event Tick → Sequence ─┬─ Then 0 → DEBUG_PrintInput
│
└─ Then 1 → (movement, next steps)
```
If you skipped debug, just connect Event Tick directly to movement.
#### b) Starting point for movement
- If using Sequence: drag from "Then 1" output
- If no debug: drag from Event Tick directly
#### c) Right-click → search `Get Player Controller` and add it
(This is a NEW Get Player Controller - separate from the one in debug)
#### d) From Player Controller output, drag → `Get Enhanced Input Local Player Subsystem`
#### e) From subsystem output, drag and right-click → search `IA_Move`
- Look under "Input" → "Enhanced Action Values" → select "IA_Move"
- This node returns the current value of the IA_Move input action
#### f) The output is an Input Action Value (Vector2D since we set IA_Move to Axis2D)
Right-click the output pin → "Split Struct Pin" to get X and Y components,
OR drag from output and search "To Vector 2D" to convert it
#### g) Right-click → `Make Vector`
- Connect the X from movement input to X
- Connect the Y from movement input to Y
- Set Z to 0
#### h) Right-click → `Normalize`
- Connect the vector output to Normalize input
#### i) Right-click → `Get World Delta Seconds`
#### j) Right-click → `Get MoveSpeed` (your variable)
#### k) Right-click → Multiply (float \* float)
- Connect Delta Seconds output to first input
- Connect MoveSpeed output to second input
#### l) Right-click → Multiply (vector \* float)
- Connect Normalized vector (from step h) to vector input
- Connect (DeltaSeconds \* MoveSpeed) result (from step k) to float input
- This output is the "movement delta" - how far to move this frame
#### m) Right-click → `Get Actor Location`
#### n) Right-click → Add (vector + vector)
- Connect current location (from step m) to first input
- Connect movement delta (from step l) to second input
#### o) CLAMP X COORDINATE
First, break apart the new position vector from step n:
- Right-click on the output pin of the Add node (from step n) → "Split Struct Pin"
- This splits the vector into three separate pins: X, Y, Z
Now clamp the X value:
- Right-click in empty space → search `Clamp (float)` → add it
- Connect the "X" output (from the split Add node) to "Value" input of Clamp
Get the min bound:
- Right-click → search `Get BoundsMin` (your variable) → add it
- Right-click on BoundsMin output pin → "Split Struct Pin" (splits into X, Y)
- Connect BoundsMin's "X" to Clamp's "Min" input
Get the max bound:
- Right-click → search `Get BoundsMax` (your variable) → add it
- Right-click on BoundsMax output pin → "Split Struct Pin"
- Connect BoundsMax's "X" to Clamp's "Max" input
The Clamp node now outputs the X position clamped within bounds.
#### p) CLAMP Y COORDINATE
- Right-click → add another `Clamp (float)` node
- Connect the "Y" output (from the split Add node in step o) to "Value"
- Connect BoundsMin's "Y" (already split) to "Min"
- Connect BoundsMax's "Y" (already split) to "Max"
#### q) Right-click → `Make Vector`
- Connect clamped X
- Connect clamped Y
- Set Z to 0
#### r) Right-click → `Set Actor Location`
- Connect the clamped vector to "New Location"
#### s) CRITICAL - Connect the execution wire (white wire)
- If using Sequence (from step a): drag from "Then 1" to Set Actor Location
- If no debug: drag from Event Tick to Set Actor Location
- Without this execution wire, the movement code NEVER runs!
**Final Structure (with debug - using Sequence node):**
```
Event Tick → Sequence ─┬─ Then 0 → DEBUG_PrintInput
│
└─ Then 1 → [movement] → Set Actor Location
```
**Final Structure (without debug):**
```
Event Tick → [movement logic] → Set Actor Location
```
**TO REMOVE DEBUG:** Delete the wire from "Then 0" to DEBUG_PrintInput. Movement on "Then 1" is completely unaffected.
### 6. Click Compile and Save
### Expected Result after Compile
- Compile button shows GREEN checkmark (no errors)
- No warnings about unconnected pins
### How to Test at This Stage
1. Open any level (or the default "Untitled" level)
2. From Content Drawer, drag BP_Player into the viewport
3. Press Play (`Alt+P`)
4. Click inside the game viewport to give it keyboard focus
5. You should see the cube (TempVisual added in Step 2.2) and control it with WASD
### Expected Result when tested
- Player pawn moves smoothly when pressing WASD keys
- Movement is frame-rate independent (consistent speed)
- Player cannot move outside the screen bounds (stops at edges)
- Releasing keys immediately stops movement (no drift)
> **NOTE:** Full game testing will be possible after completing [Part 8 (Level Setup)](part-8-game-mode-level.md).
### Visual Diagram of Movement Nodes
```
┌─────────────┐ ┌──────────┐
│ Event Tick │────►│ Sequence │─┬─ Then 0 ──► DEBUG_PrintInput (optional)
└─────────────┘ └──────────┘ │
└─ Then 1 ──► Movement logic ──► Set Actor Location
(If no debug, skip Sequence and connect Event Tick directly to movement)
┌──────────────────────────────────┐ ┌────────────────────┐
│ Get Enhanced Input Subsystem │ │ Set Actor Location │
│ → Get Action Value (IA_Move) │ └────────────────────┘
│ → Get as Vector2D │ ▲
└────────────────┬─────────────────┘ │
│ ┌───────────────┐
▼ │ Clamped Vector│
┌────────────────────────────┐ └───────────────┘
│ Make Vector │───►Normalize───► ▲
│ X=Input.X, Y=Input.Y, Z=0 │ (direction) │
└────────────────────────────┘ │ ┌─────────────┐
▼ │ + Location │
┌──────────────┐ └─────────────┘
│ * MoveSpeed │─────────┘
│ * DeltaTime │
└──────────────┘
```
---
## Step 2.5: Create Player Firing Logic
1. Continue in Event Graph, we'll add firing after movement
2. After the movement logic, add firing check using Enhanced Input:
### a) Right-click → search `IA_Fire`
- Look under "Input" → "Enhanced Action Events" category (diamond ◇ icons)
- Select "IA_Fire" (the one with diamond icon, NOT the square icon)
- This creates an EVENT node (red, like Event Tick) that fires when the button is pressed
> **NOTE:**
>
> - "Enhanced Action Events" (diamond ◇) = triggers when button pressed
> - "Enhanced Action Values" (square □) = reads current value continuously
>
> For firing, we want the EVENT.
#### b) From the "Triggered" execution pin, build the fire rate limiter
1. **Get current timer value:**
- Right-click → `Get FireTimer` (your variable)
2. **Subtract frame time from it:**
- Right-click → `Get World Delta Seconds` (time since last frame, ~0.016 at 60fps)
- Right-click → search "float - float" or "Subtract" → add Subtract node
- Connect FireTimer to the TOP input (A)
- Connect Delta Seconds to the BOTTOM input (B)
- Result = FireTimer minus DeltaSeconds
3. **Store the new timer value:**
- Right-click → `Set FireTimer`
- Connect the Subtract result to it
- Connect execution wire: Triggered → Set FireTimer
4. **Check if we can fire:**
- From Set FireTimer, drag execution → `Branch`
- Right-click → `<= (float)` (less than or equal)
- Connect FireTimer (use the output from Set, or Get it again) to top input
- Type `0` in bottom input
- Connect result to Branch's Condition
5. **On TRUE branch (timer expired, can fire):**
- Right-click → `Set FireTimer`
- Right-click → `Get FireInterval` (your variable)
- Connect FireInterval to Set FireTimer (resets the cooldown)
- For now, add a Print String with text "FIRE!" to verify it works
- Leave the execution wire after this Set FireTimer OPEN (we'll connect FireVolley function here in [Step 3.4](part-3-create-bullet.md#step-34-complete-player-firing-logic-bp_player))
**Visual:**
```
┌──────────────────┐
│ IA_Fire Triggered│
└────────┬─────────┘
│ (white wire)
▼
┌─────────────────────────────────────┐
│ Set FireTimer │
│ = FireTimer - GetWorldDeltaSeconds │
└────────┬────────────────────────────┘
│
▼
┌─────────────────┐ TRUE ┌─────────────────────┐
│ Branch │───────────►│ Set FireTimer │──► Print "FIRE!"
│ FireTimer <= 0 │ │ = FireInterval │ (placeholder)
└────────┬────────┘ └─────────────────────┘
│ FALSE
▼
(do nothing)
```
**TEST NOW:** Press Play, hold Z key. You should see "FIRE!" spam in top-left at rapid intervals (every 0.08 seconds = FireInterval).
> **NOTE:** Since IA_Fire was set up with Z key and Left Mouse Button in the Input Mapping Context, both inputs will trigger this action automatically.
### 3. PLACEHOLDER FOR BULLET SPAWNING
The Print String "FIRE!" is a placeholder. We will replace it with actual bullet spawning in [STEP 3.4](part-3-create-bullet.md#step-34-complete-player-firing-logic-bp_player), AFTER creating BP_Bullet in Part 3.
Leave the execution wire open after the "Set FireTimer = FireInterval" node. We'll connect FireVolley function there in STEP 3.4.
### 4. Compile and Save
### Expected Result after Compile
- Compile button shows GREEN checkmark
- Fire rate limiter logic works (Print String spams when holding Z)
- Actual bullets will be added in [STEP 3.4](part-3-create-bullet.md#step-34-complete-player-firing-logic-bp_player) after BP_Bullet is created
---
## Step 2.6: Create Player Damage and Special Ability
> **IMPORTANT - Dependency Note:**
> The `HandleDeath` function needs to call `BP_GameDirector`, which we create in [Part 6](part-6-game-director.md).
> In this step, we will create **placeholder functions** that compile but aren't fully functional.
> We will complete `HandleDeath` in [Part 6](part-6-game-director.md#step-63-game-director-update-logic) after GameDirector exists.
---
### 1. CREATE "HandleDeath" FUNCTION (Placeholder)
We create this FIRST because `TakeHit` will call it. For now, it's a placeholder.
#### a) Create the function
1. In My Blueprint panel (left side), find "Functions" section
2. Click the **"+"** button next to "Functions"
3. Name it `HandleDeath`
4. Double-click to open the function graph
#### b) Add placeholder logic (will be completed in Part 6)
1. The function graph opens with a purple "HandleDeath" entry node
2. **Hide the player:**
- From the entry node's WHITE execution pin, drag and search `Set Actor Hidden in Game`
- Check the "New Hidden" checkbox (set to TRUE)
- This makes the player invisible when they die
3. **Add a placeholder comment:**
- Right-click in empty space → search `Add Comment` (or press C)
- Type: "TODO: Call GameDirector.HandlePlayerDeath (Part 6)"
- Position it near the end of the function
4. **Connect execution:**
```
HandleDeath (entry) ──► Set Actor Hidden in Game
│
▼
[TODO comment - no connection needed]
```
5. Click "Compile" - should show GREEN checkmark
> **NOTE:** This function is incomplete! We'll add the GameDirector call in [Part 6, Step 6.3](part-6-game-director.md#step-63-game-director-update-logic).
---
### 2. CREATE "TakeHit" FUNCTION
Now we can create TakeHit, which calls HandleDeath.
#### a) Create the function
1. In My Blueprint panel → Functions → click **"+"**
2. Name it `TakeHit`
3. Double-click to open the function graph
#### b) Add input parameter
1. With the function graph open, look at the Details panel (right side)
2. Find "Inputs" section
3. Click **"+"** to add a new input
4. Name: `Damage`
5. Type: `Integer`
6. The purple entry node now shows a "Damage" output pin
#### c) Build the function logic
**Step 1 - Get current lives:**
- Right-click in empty space → search `Get CurrentLives` → add it
- This reads the CurrentLives variable value
**Step 2 - Subtract damage from lives:**
- Right-click → search `integer - integer` or `Subtract (int)` → add it
- Connect `CurrentLives` (from Step 1) to the TOP input (A)
- Connect `Damage` (from the purple entry node) to the BOTTOM input (B)
- Result = CurrentLives minus Damage
**Step 3 - Clamp the result (prevent negative lives):**
- Right-click → search `Clamp (int)` → add the "Clamp (integer)" node
- Connect the Subtract result (from Step 2) to the "Value" input
- Set "Min" to `0` (type directly in the field)
- Set "Max" to `99` (or any high number - we just want to prevent negatives)
- The output is the clamped value (0 or higher)
**Step 4 - Store the new lives value:**
- Right-click → search `Set CurrentLives` → add it
- Connect the Clamp output (from Step 3) to the input
- **IMPORTANT:** Connect execution wire from entry node → Set CurrentLives
**Step 5 - Check if player should die:**
- From `Set CurrentLives`, drag execution → search `Branch` → add it
- Right-click → search `<= (integer)` (less than or equal) → add it
- Connect `CurrentLives` (drag from Set node's output, or add new Get) to TOP input
- Type `0` in BOTTOM input
- Connect the `<=` result (boolean) to Branch's "Condition" input
**Step 6 - On TRUE (player is dead):**
- From Branch's "True" execution pin, drag → search `HandleDeath` → add it
- This calls the function we created in step 1
**Step 7 - On FALSE (player still alive):**
- Leave the "False" pin unconnected (do nothing, player survives)
#### d) Visual diagram of TakeHit function
```
┌────────────────────────┐
│ TakeHit (entry) │
│ Damage ○───────┼──────────────────────┐
└───────────┬────────────┘ │
│ (white execution wire) │
▼ ▼
┌───────────────────────────────────────────────────────┐
│ Set CurrentLives │
│ = Clamp( CurrentLives - Damage, Min=0, Max=99 ) │
└───────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ Branch │
│ Condition: CurrentLives <= 0│
└───────┬─────────────┬───────┘
│ TRUE │ FALSE
▼ ▼
┌───────────────┐ (nothing)
│ HandleDeath │
└───────────────┘
```
#### e) Click Compile and Save
---
### 3. SPECIAL ABILITY (Screen Clear) - Input Setup Only
> **IMPORTANT - Dependency Note:**
> The special ability needs to destroy BP_Enemy and BP_Bullet actors, which don't exist yet.
> In this step, we create ONLY the input handling and mark-as-used logic.
> We will complete the destruction logic in [Part 4, Step 4.6](part-4-create-enemy.md#step-46-complete-special-ability-in-bp_player) after BP_Enemy exists.
Using Enhanced Input (IA_Special was already set up with X and Right Mouse in Step 2.4):
#### a) Create the event node
1. Go back to the **Event Graph** tab (not inside a function)
2. Right-click in empty space → search `EnhancedInputAction IA_Special`
- Look under "Input" → "Enhanced Action Events" category
- Select "IA_Special" (the EVENT with diamond ◇ icon)
3. This creates a red event node that fires when X or Right Mouse is pressed
#### b) Check if special was already used
1. From the "Triggered" execution pin, drag → search `Branch` → add it
2. Right-click → search `Get SpecialUsed` → add it
3. Right-click → search `NOT Boolean` → add it
4. Connect SpecialUsed → NOT → Branch Condition
- We branch on "NOT SpecialUsed" (true = hasn't been used yet)
#### c) On TRUE (special available) - placeholder
1. From Branch's "True" pin, drag → search `Set SpecialUsed` → add it
2. Check the box to set it to TRUE (marks ability as used)
3. **Add placeholder Print String:**
- From Set SpecialUsed, drag → search `Print String` → add it
- Type: "SPECIAL ABILITY ACTIVATED!" in the In String field
- This verifies the input works; we'll replace it with actual destruction in Part 4
4. **Add a placeholder comment:**
- Right-click in empty space → search `Add Comment` (or press C)
- Type: "TODO: Add enemy/bullet destruction (Part 4)"
- Position it after the Print String
#### d) On FALSE (special already used)
- Leave unconnected (nothing happens on second press)
#### e) Visual diagram (placeholder version)
```
┌─────────────────────────────┐
│ EnhancedInputAction │
│ IA_Special - Triggered │
└───────────┬─────────────────┘
│
▼
┌─────────────────────────────┐
│ Branch │
│ Condition: NOT SpecialUsed │
└───────┬─────────────┬───────┘
│ TRUE │ FALSE
▼ ▼
┌───────────────┐ (nothing - ability already used)
│Set SpecialUsed│
│ = true │
└───────┬───────┘
│
▼
┌────────────────────────────────────┐
│ Print String │
│ "SPECIAL ABILITY ACTIVATED!" │
└────────────────────────────────────┘
│
▼
[TODO comment - destruction logic added in Part 4]
```
#### f) Compile and Save
#### g) Test the placeholder
1. Press Play
2. Press X key (or Right Mouse Button)
3. You should see "SPECIAL ABILITY ACTIVATED!" appear once
4. Pressing X again should do nothing (SpecialUsed = true)
> **REMINDER:** Complete the special ability with actual enemy/bullet destruction in [Part 4, Step 4.6](part-4-create-enemy.md#step-46-complete-special-ability-in-bp_player) after creating BP_Enemy.
---
### 4. Compile and Save
### Expected Result after Compile
- Compile button shows GREEN checkmark
- "TakeHit" and "HandleDeath" functions appear under Functions in My Blueprint panel
- Event Graph shows IA_Special event with branching logic
### Expected Result in Play mode (at this stage)
- Press X key: "SPECIAL ABILITY ACTIVATED!" appears once in top-left
- Press X again: Nothing happens (SpecialUsed is now true)
- TakeHit and HandleDeath functions exist but cannot be tested yet (nothing calls them)
> **NOTE:** Full testing of damage/death systems requires:
>
> - BP_Bullet collision (Part 3) to call TakeHit
> - BP_GameDirector (Part 6) for HandleDeath to notify
> - BP_Enemy (Part 4) for special ability to destroy
>
> **REMINDER:** Complete the `HandleDeath` function in [Part 6, Step 6.3](part-6-game-director.md#step-63-game-director-update-logic) after creating BP_GameDirector.
---
[← Previous: Part 1 - Project Setup](part-1-project-setup.md) | [Back to Index](README.md) | [Next: Part 3 - Create the Bullet →](part-3-create-bullet.md)