chore: markdown linter and fixes

This commit is contained in:
Krzysztof kuhy Rudnicki 2026-01-05 18:43:37 +01:00
parent 9bf78a2dc1
commit 472f6dc19d
16 changed files with 837 additions and 436 deletions

View File

@ -0,0 +1,18 @@
{
"default": true,
"MD013": {
"line_length": 500,
"code_blocks": false,
"tables": false
},
"MD024": {
"siblings_only": true
},
"MD033": false,
"MD041": false,
"MD036": false,
"MD040": false,
"MD046": false,
"MD060": false,
"MD029": false
}

View File

@ -0,0 +1,6 @@
{
"proseWrap": "preserve",
"tabWidth": 2,
"useTabs": false,
"printWidth": 300
}

View File

@ -17,11 +17,13 @@ This tutorial recreates the Unity "magisterka_1" bullet-hell shooter in Unreal E
## Table of Contents
### Part 1: Project Setup
- [Step 1.1: Create New Project](part-1-project-setup.md#step-11-create-new-project)
- [Step 1.2: Set Up 2D Game View](part-1-project-setup.md#step-12-set-up-2d-game-view)
- [Step 1.3: Create Folder Structure](part-1-project-setup.md#step-13-create-folder-structure)
### Part 2: Create the Player
- [Step 2.1: Create Player Blueprint](part-2-create-player.md#step-21-create-player-blueprint)
- [Step 2.2: Add Player Visual Components](part-2-create-player.md#step-22-add-player-visual-components)
- [Step 2.3: Create Player Variables](part-2-create-player.md#step-23-create-player-variables)
@ -30,12 +32,14 @@ This tutorial recreates the Unity "magisterka_1" bullet-hell shooter in Unreal E
- [Step 2.6: Create Player Damage and Special Ability](part-2-create-player.md#step-26-create-player-damage-and-special-ability)
### Part 3: Create the Bullet
- [Step 3.1: Create Bullet Blueprint](part-3-create-bullet.md#step-31-create-bullet-blueprint)
- [Step 3.2: Bullet Movement Logic](part-3-create-bullet.md#step-32-bullet-movement-logic)
- [Step 3.3: Bullet Collision Logic](part-3-create-bullet.md#step-33-bullet-collision-logic)
- [Step 3.4: Complete Player Firing Logic](part-3-create-bullet.md#step-34-complete-player-firing-logic-bp_player)
### Part 4: Create the Enemy
- [Step 4.1: Create Enemy Blueprint](part-4-create-enemy.md#step-41-create-enemy-blueprint)
- [Step 4.2: Enemy Initialization](part-4-create-enemy.md#step-42-enemy-initialization)
- [Step 4.3: Enemy Movement Logic](part-4-create-enemy.md#step-43-enemy-movement-logic)
@ -45,16 +49,19 @@ This tutorial recreates the Unity "magisterka_1" bullet-hell shooter in Unreal E
- [Step 4.7: Complete Bullet Collision Logic](part-4-create-enemy.md#step-47-complete-bullet-collision-logic-bp_bullet)
### Part 5: Create Enemy Spawner
- [Step 5.1: Create Spawner Blueprint](part-5-create-spawner.md#step-51-create-spawner-blueprint)
- [Step 5.2: Spawn Rate Curve](part-5-create-spawner.md#step-52-spawn-rate-curve)
- [Step 5.3: Spawning Logic](part-5-create-spawner.md#step-53-spawning-logic)
### Part 6: Create Game Director
- [Step 6.1: Create Game Director Blueprint](part-6-game-director.md#step-61-create-game-director-blueprint)
- [Step 6.2: Game Director Initialization](part-6-game-director.md#step-62-game-director-initialization)
- [Step 6.3: Game Director Update Logic](part-6-game-director.md#step-63-game-director-update-logic)
### Part 7: Create Score Manager / UI
- [Step 7.1: Create UI Widget Blueprint](part-7-score-manager-ui.md#step-71-create-ui-widget-blueprint)
- [Step 7.2: Design HUD Layout](part-7-score-manager-ui.md#step-72-design-hud-layout)
- [Step 7.3: Create Score Manager Blueprint](part-7-score-manager-ui.md#step-73-create-score-manager-blueprint)
@ -62,6 +69,7 @@ This tutorial recreates the Unity "magisterka_1" bullet-hell shooter in Unreal E
- [Step 7.5: Score Manager Functions](part-7-score-manager-ui.md#step-75-score-manager-functions)
### Part 8: Create Game Mode and Level
- [Step 8.1: Create Custom Game Mode](part-8-game-mode-level.md#step-81-create-custom-game-mode)
- [Step 8.2: Configure Project to Use Game Mode](part-8-game-mode-level.md#step-82-configure-project-to-use-game-mode)
- [Step 8.3: Create Game Level](part-8-game-mode-level.md#step-83-create-game-level)
@ -69,6 +77,7 @@ This tutorial recreates the Unity "magisterka_1" bullet-hell shooter in Unreal E
- [Step 8.5: Set Default Level](part-8-game-mode-level.md#step-85-set-default-level)
### Part 9: Final Setup and Testing
- [Step 9.1: Assign Blueprint References](part-9-final-setup.md#step-91-assign-blueprint-references)
- [Step 9.2: Create Final Visuals](part-9-final-setup.md#step-92-create-final-visuals-replace-placeholder)
- [Step 9.3: Add Background](part-9-final-setup.md#step-93-add-background-optional)
@ -76,6 +85,7 @@ This tutorial recreates the Unity "magisterka_1" bullet-hell shooter in Unreal E
- [Step 9.5: Build Standalone Game](part-9-final-setup.md#step-95-build-standalone-game)
### Appendices
- [Appendix A: Complete Variable Reference](appendix-a-variables.md)
- [Appendix B: Troubleshooting](appendix-b-troubleshooting.md)
- [Appendix C: Unity to Unreal Conversion Notes](appendix-c-unity-conversion.md)
@ -94,10 +104,11 @@ This tutorial recreates the Unity "magisterka_1" bullet-hell shooter in Unreal E
## Navigation
Each page includes:
- **← Previous** and **Next →** links at the top and bottom
- Links back to this index
- Cross-references to related sections
---
*This tutorial is part of a master's thesis comparing Unity and Unreal Engine.*
_This tutorial is part of a master's thesis comparing Unity and Unreal Engine._

View File

@ -7,7 +7,7 @@
## BP_Player Variables
| Variable Name | Type | Default Value |
|---------------|------|---------------|
| -------------- | --------- | ------------- |
| `MoveSpeed` | Float | 750.0 |
| `BoundsMin` | Vector 2D | (-850, -450) |
| `BoundsMax` | Vector 2D | (850, 450) |
@ -26,7 +26,7 @@
## BP_Bullet Variables
| Variable Name | Type | Default Value |
|---------------|------|---------------|
| ------------------- | ------- | ------------- |
| `TravelDirection` | Vector | (0, 1, 0) |
| `TravelSpeed` | Float | 1200.0 |
| `RemainingLifetime` | Float | 4.0 |
@ -38,7 +38,7 @@
## BP_Enemy Variables
| Variable Name | Type | Default Value |
|---------------|------|---------------|
| --------------------- | --------- | ------------- |
| `MaxHealth` | Integer | 12 |
| `CurrentHealth` | Integer | 12 |
| `ScoreValue` | Integer | 50 |
@ -61,7 +61,7 @@
## BP_EnemySpawner Variables
| Variable Name | Type | Default Value |
|---------------|------|---------------|
| ------------------------ | ----------- | -------------------- |
| `EnemyClass` | Class Ref | BP_Enemy |
| `SpawnAreaHalfWidth` | Float | 900.0 |
| `GameDuration` | Float | 300.0 |
@ -76,7 +76,7 @@
## BP_GameDirector Variables
| Variable Name | Type | Default Value |
|---------------|------|---------------|
| ------------------ | ---------- | ---------------- |
| `PlayerReference` | Object Ref | (set at runtime) |
| `SpawnerReference` | Object Ref | (set at runtime) |
| `GameDuration` | Float | 300.0 |
@ -88,7 +88,7 @@
## BP_ScoreManager Variables
| Variable Name | Type | Default Value |
|---------------|------|---------------|
| ---------------- | ---------- | ---------------- |
| `Score` | Integer | 0 |
| `CurrentLives` | Integer | 3 |
| `HUDWidget` | Object Ref | (set at runtime) |

View File

@ -143,25 +143,31 @@
## General Debugging Tips
### Enable Print Statements
```
Right-click → Print String → Type message
```
Add these throughout your blueprints to track execution flow.
### Check Output Log
- **Window → Developer Tools → Output Log**
- Shows blueprint errors, warnings, and print statements
### Use Breakpoints
- Right-click any blueprint node → **Add Breakpoint**
- Execution pauses at that point during Play mode
- Step through with F10/F11
### Visualize Collisions
- In viewport: **Show → Collision**
- Shows collision shapes in play mode
### Console Commands
- Press `~` (tilde) to open console
- `stat fps` - Show frame rate
- `show collision` - Toggle collision visualization

View File

@ -7,10 +7,11 @@
## Scale Conversion
| Unity | Unreal | Notes |
|-------|--------|-------|
| ---------------- | --------------------- | ---------------------------- |
| 1 unit = 1 meter | 1 unit = 1 centimeter | Multiply Unity values by 100 |
**Example:**
- Unity speed: `7.5` → Unreal speed: `750`
- Unity position: `(5, 10, 0)` → Unreal position: `(500, 1000, 0)`
@ -19,12 +20,13 @@
## Coordinate System
| Axis | Unity | Unreal |
|------|-------|--------|
| ------- | ----- | ------ |
| Up | Y | Z |
| Forward | Z | X |
| Right | X | Y |
**For 2D top-down games:**
- Both use X for horizontal
- Y/Z swap for vertical
@ -35,12 +37,14 @@
### Input
**Unity:**
```csharp
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
```
**Unreal (Enhanced Input):**
1. Create Input Action (IA_Move) with Axis2D type
2. Create Input Mapping Context with key bindings
3. Add Mapping Context in BeginPlay
@ -51,11 +55,13 @@ float v = Input.GetAxisRaw("Vertical");
### Instantiate / Spawn
**Unity:**
```csharp
Instantiate(prefab, position, rotation);
```
**Unreal:**
```
Spawn Actor from Class
├── Class: YourBlueprintClass
@ -68,11 +74,13 @@ Spawn Actor from Class
### Destroy
**Unity:**
```csharp
Destroy(gameObject);
```
**Unreal:**
```
Destroy Actor
└── Target: Self (or reference)
@ -83,11 +91,13 @@ Destroy Actor
### Delta Time
**Unity:**
```csharp
Time.deltaTime
```
**Unreal:**
```
Get World Delta Seconds
```
@ -97,12 +107,14 @@ Get World Delta Seconds
### Find Objects
**Unity:**
```csharp
FindObjectOfType<PlayerController>();
FindObjectsOfType<Enemy>();
```
**Unreal:**
```
Get All Actors of Class
├── Actor Class: BP_Player
@ -116,6 +128,7 @@ Then: Get (a ref) → index 0
### Singleton Pattern
**Unity:**
```csharp
public static GameManager Instance { get; private set; }
@ -125,6 +138,7 @@ void Awake() {
```
**Unreal Options:**
1. **Game Instance** - Persists across levels
2. **Subsystem** - Engine-managed singleton
3. **Get All Actors of Class** - Find at runtime
@ -134,6 +148,7 @@ void Awake() {
### Coroutines vs Timers
**Unity:**
```csharp
StartCoroutine(DelayedAction());
@ -144,6 +159,7 @@ IEnumerator DelayedAction() {
```
**Unreal:**
```
Set Timer by Function Name
├── Function Name: "DelayedAction"
@ -158,10 +174,12 @@ Or use **Delay** node in blueprints.
### Physics Layers vs Collision Channels
**Unity:**
- Layer-based collision matrix
- `Physics.IgnoreLayerCollision()`
**Unreal:**
- Collision Channels (Object Types)
- Collision Presets
- Per-component collision settings
@ -171,11 +189,13 @@ Or use **Delay** node in blueprints.
### Tags
**Unity:**
```csharp
if (other.CompareTag("Enemy")) { }
```
**Unreal:**
```
Actor Has Tag
├── Target: Other Actor
@ -189,7 +209,7 @@ Or use **Cast To** for type checking (preferred).
### Vector Math
| Operation | Unity | Unreal |
|-----------|-------|--------|
| ------------- | --------------------- | -------------------- |
| Normalize | `vector.normalized` | `Normalize` node |
| Magnitude | `vector.magnitude` | `Vector Length` node |
| Dot Product | `Vector3.Dot(a, b)` | `Dot Product` node |
@ -200,12 +220,14 @@ Or use **Cast To** for type checking (preferred).
### Random
**Unity:**
```csharp
Random.Range(0f, 1f);
Random.Range(0, 10); // int, exclusive max
```
**Unreal:**
```
Random Float in Range
├── Min: 0.0
@ -223,12 +245,14 @@ Random Integer in Range
### Debug
**Unity:**
```csharp
Debug.Log("Message");
Debug.DrawLine(start, end, Color.red);
```
**Unreal:**
```
Print String
└── In String: "Message"
@ -244,7 +268,7 @@ Draw Debug Line
## Quick Reference Table
| Concept | Unity | Unreal |
|---------|-------|--------|
| --------- | ---------------- | --------------------- |
| Script | C# MonoBehaviour | Blueprint / C++ Actor |
| Prefab | .prefab asset | Blueprint Class |
| Scene | .unity scene | Level (.umap) |

View File

@ -0,0 +1,155 @@
#!/bin/bash
# Markdown Linter and Formatter Script
# Usage: ./lint-markdown.sh [--fix]
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FIX_MODE=false
# Parse arguments
if [[ "$1" == "--fix" ]]; then
FIX_MODE=true
fi
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo "========================================="
echo "Markdown Linter and Formatter"
echo "========================================="
# Check for required tools
check_tool() {
if ! command -v "$1" &> /dev/null; then
echo -e "${YELLOW}Warning: $1 not found. Installing...${NC}"
return 1
fi
return 0
}
# Install markdownlint-cli if not present
if ! check_tool "markdownlint"; then
echo "Installing markdownlint-cli..."
npm install -g markdownlint-cli
fi
# Install prettier if not present
if ! check_tool "prettier"; then
echo "Installing prettier..."
npm install -g prettier
fi
# Create markdownlint config if it doesn't exist
CONFIG_FILE="$SCRIPT_DIR/.markdownlint.json"
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "Creating markdownlint configuration..."
cat > "$CONFIG_FILE" << 'EOF'
{
"default": true,
"MD013": {
"line_length": 300,
"code_blocks": false,
"tables": false
},
"MD024": {
"siblings_only": true
},
"MD033": {
"allowed_elements": ["br", "details", "summary", "kbd"]
},
"MD041": false,
"MD036": false,
"MD040": false,
"MD046": false
}
EOF
fi
# Create prettier config if it doesn't exist
PRETTIER_CONFIG="$SCRIPT_DIR/.prettierrc"
if [[ ! -f "$PRETTIER_CONFIG" ]]; then
echo "Creating prettier configuration..."
cat > "$PRETTIER_CONFIG" << 'EOF'
{
"proseWrap": "preserve",
"tabWidth": 2,
"useTabs": false,
"printWidth": 300
}
EOF
fi
echo ""
echo "========================================="
echo "Running Linters on Markdown Files"
echo "========================================="
# Find all markdown files
MD_FILES=$(find "$SCRIPT_DIR" -maxdepth 1 -name "*.md" -type f | sort)
TOTAL_ISSUES=0
if [[ "$FIX_MODE" == true ]]; then
echo -e "${GREEN}Running in FIX mode - will auto-fix issues${NC}"
echo ""
# Run prettier to format
echo "Step 1: Running Prettier (formatting)..."
for file in $MD_FILES; do
echo " Formatting: $(basename "$file")"
prettier --write "$file" 2>/dev/null || true
done
echo ""
echo "Step 2: Running markdownlint with --fix..."
for file in $MD_FILES; do
echo " Linting: $(basename "$file")"
markdownlint --fix --config "$CONFIG_FILE" "$file" 2>&1 || true
done
echo ""
echo "Step 3: Final check for remaining issues..."
for file in $MD_FILES; do
OUTPUT=$(markdownlint --config "$CONFIG_FILE" "$file" 2>&1 || true)
if [[ -n "$OUTPUT" ]]; then
echo -e "${YELLOW}$(basename "$file"):${NC}"
echo "$OUTPUT"
TOTAL_ISSUES=$((TOTAL_ISSUES + $(echo "$OUTPUT" | wc -l)))
fi
done
else
echo -e "${YELLOW}Running in CHECK mode - use --fix to auto-fix${NC}"
echo ""
for file in $MD_FILES; do
echo "Checking: $(basename "$file")"
OUTPUT=$(markdownlint --config "$CONFIG_FILE" "$file" 2>&1 || true)
if [[ -n "$OUTPUT" ]]; then
echo -e "${RED}Issues found:${NC}"
echo "$OUTPUT"
TOTAL_ISSUES=$((TOTAL_ISSUES + $(echo "$OUTPUT" | wc -l)))
else
echo -e "${GREEN} ✓ No issues${NC}"
fi
echo ""
done
fi
echo ""
echo "========================================="
echo "Summary"
echo "========================================="
if [[ $TOTAL_ISSUES -eq 0 ]]; then
echo -e "${GREEN}✓ All markdown files are clean!${NC}"
else
echo -e "${YELLOW}Found $TOTAL_ISSUES remaining issue(s)${NC}"
echo "Some issues may require manual fixing."
fi
echo ""
echo "Done!"

View File

@ -14,7 +14,6 @@
5. In the "Unreal Project Browser" window that appears:
- At the top, select "Games" category (should be selected by default)
- Click "Blank" template (empty square icon)
6. On the right side panel, configure:
- **Project Defaults:** Blueprint (not C++)
- **Target Platform:** Desktop
@ -25,17 +24,18 @@
7. At the bottom:
- Choose folder location where you want to save
- Name the project: `BulletHellGame`
8. Click "Create" button (bottom right, yellow)
### Expected Result
Unreal Editor opens with an empty level. You should see:
- Main 3D viewport in the center
- Outliner panel on the right (showing "Untitled" level)
- Details panel on the right side
> **NOTE:** The Content Drawer is NOT open by default. To open it:
>
> - Click "Content Drawer" button at the bottom of the screen, OR
> - Press `Ctrl+Space`, OR
> - Go to Window → Content Drawer
@ -54,7 +54,6 @@ Since bullet-hell games are typically 2D, we'll set up a top-down orthographic v
- Go to menu bar (the top bar): **Edit** (Second from left, next to "File" and "Window") → **Project Settings**
- In the left sidebar, search for "Maps & Modes" (Project → 4th from the top, next to GameplayTags and Movies)
- Click on "Maps & Modes"
5. Under "Default Modes":
- Find "Default GameMode" dropdown
- We'll create our own later, leave it for now

View File

@ -21,6 +21,7 @@
### 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
@ -39,6 +40,7 @@ A new tab opens showing the Blueprint Editor with:
- 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
@ -112,7 +114,7 @@ DefaultSceneRoot
## Step 2.3: Create Player Variables
*(Continue in the same BP_Player Blueprint Editor tab that was opened in Step 2.1)*
_(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
@ -125,7 +127,7 @@ DefaultSceneRoot
> 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 |
@ -137,10 +139,10 @@ DefaultSceneRoot
| 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.
| 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)
@ -149,6 +151,7 @@ DefaultSceneRoot
### 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
@ -227,7 +230,7 @@ Before creating any input assets, you **MUST** configure the project to use Enha
**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 |
@ -254,6 +257,7 @@ Before creating any input assets, you **MUST** configure the project to use Enha
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
@ -276,7 +280,6 @@ From "Event BeginPlay" node:
- 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
@ -303,6 +306,7 @@ Your graph should look like this:
```
**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)
@ -310,19 +314,21 @@ Your graph should look like this:
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):
#### 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:
#### 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:
#### c) Build the debug logic from DEBUG_PrintInput
1. **Get the current input value:**
- Right-click → "Get Player Controller"
@ -342,7 +348,8 @@ Before creating movement, let's verify input is working with a separate debug se
- "Set DEBUG_LastMoveInput" = IA_Move value
- Then → "Print String" with IA_Move value
#### d) Call the debug event from Event Tick:
#### 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
@ -374,18 +381,22 @@ AREA 2 - Debug logic (completely separate):
```
#### 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!
#### 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 Steps (if you see "X=0.0 Y=0.0" always)
<details>
<summary><b>DEBUGGING STEP 1</b> - Verify the pawn is possessed</summary>
@ -394,6 +405,7 @@ AREA 2 - Debug logic (completely separate):
- 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
</details>
<details>
@ -403,6 +415,7 @@ AREA 2 - Debug logic (completely separate):
- The viewport must have focus to receive keyboard input
- Try pressing `Shift+F1` to release mouse, then click viewport again
- Then press WASD again
</details>
<details>
@ -413,6 +426,7 @@ AREA 2 - Debug logic (completely separate):
- 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
</details>
<details>
@ -423,6 +437,7 @@ AREA 2 - Debug logic (completely separate):
- 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?)
</details>
<details>
@ -432,6 +447,7 @@ AREA 2 - Debug logic (completely separate):
- Delete ALL of them
- Drag in exactly ONE fresh BP_Player from Content Drawer
- Try again
</details>
<details>
@ -443,6 +459,7 @@ AREA 2 - Debug logic (completely separate):
- 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
</details>
**IF THIS TEST WORKS:** Continue to step 5. You can optionally delete the DEBUG_PrintInput call from Event Tick to stop the debug prints.
@ -452,7 +469,8 @@ AREA 2 - Debug logic (completely separate):
> **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:
#### 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)
@ -468,92 +486,110 @@ Event Tick → Sequence ─┬─ Then 0 → DEBUG_PrintInput
If you skipped debug, just connect Event Tick directly to movement.
#### b) Starting point for 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).
#### 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)
#### 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)
#### 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
- 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:
#### 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:
#### 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):
#### 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
@ -561,6 +597,7 @@ Event Tick → Sequence ─┬─ Then 0 → DEBUG_PrintInput
```
**Final Structure (without debug):**
```
Event Tick → [movement logic] → Set Actor Location
```
@ -569,18 +606,21 @@ Event Tick → [movement logic] → Set Actor Location
### 6. Click Compile and Save
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark (no errors)
- No warnings about unconnected pins
### How to Test at This Stage:
### 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:
### 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)
@ -588,7 +628,7 @@ Event Tick → [movement logic] → Set Actor Location
> **NOTE:** Full game testing will be possible after completing [Part 8 (Level Setup)](part-8-game-mode-level.md).
### Visual Diagram of Movement Nodes:
### Visual Diagram of Movement Nodes
```
┌─────────────┐ ┌──────────┐
@ -624,18 +664,20 @@ Event Tick → [movement logic] → Set Actor Location
2. After the movement logic, add firing check using Enhanced Input:
#### a) Right-click → search `IA_Fire`
### 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:
#### b) From the "Triggered" execution pin, build the fire rate limiter
1. **Get current timer value:**
- Right-click → `Get FireTimer` (your variable)
@ -701,7 +743,8 @@ Leave the execution wire open after the "Set FireTimer = FireInterval" node. We'
### 4. Compile and Save
### Expected Result after Compile:
### 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
@ -717,17 +760,18 @@ Leave the execution wire open after the "Set FireTimer = FireInterval" node. We'
---
### 1. CREATE "HandleDeath" FUNCTION (Placeholder):
### 1. CREATE "HandleDeath" FUNCTION (Placeholder)
We create this FIRST because `TakeHit` will call it. For now, it's a placeholder.
#### a) Create the function:
#### 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):
#### b) Add placeholder logic (will be completed in Part 6)
1. The function graph opens with a purple "HandleDeath" entry node
@ -742,6 +786,7 @@ We create this FIRST because `TakeHit` will call it. For now, it's a placeholder
- Position it near the end of the function
4. **Connect execution:**
```
HandleDeath (entry) ──► Set Actor Hidden in Game
@ -755,16 +800,18 @@ We create this FIRST because `TakeHit` will call it. For now, it's a placeholder
---
### 2. CREATE "TakeHit" FUNCTION:
### 2. CREATE "TakeHit" FUNCTION
Now we can create TakeHit, which calls HandleDeath.
#### a) Create the function:
#### 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:
#### 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
@ -772,19 +819,22 @@ Now we can create TakeHit, which calls HandleDeath.
5. Type: `Integer`
6. The purple entry node now shows a "Damage" output pin
#### c) Build the function logic:
#### 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)
@ -792,11 +842,13 @@ Now we can create TakeHit, which calls HandleDeath.
- 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
@ -804,13 +856,15 @@ Now we can create TakeHit, which calls HandleDeath.
- 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:
#### d) Visual diagram of TakeHit function
```
┌────────────────────────┐
@ -840,7 +894,7 @@ Now we can create TakeHit, which calls HandleDeath.
---
### 3. SPECIAL ABILITY (Screen Clear) - Input Setup Only:
### 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.
@ -849,21 +903,24 @@ Now we can create TakeHit, which calls HandleDeath.
Using Enhanced Input (IA_Special was already set up with X and Right Mouse in Step 2.4):
#### a) Create the event node:
#### 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:
#### 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:
#### 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)
@ -877,10 +934,11 @@ Using Enhanced Input (IA_Special was already set up with X and Right Mouse in St
- Type: "TODO: Add enemy/bullet destruction (Part 4)"
- Position it after the Print String
#### d) On FALSE (special already used):
#### d) On FALSE (special already used)
- Leave unconnected (nothing happens on second press)
#### e) Visual diagram (placeholder version):
#### e) Visual diagram (placeholder version)
```
┌─────────────────────────────┐
@ -912,7 +970,8 @@ Using Enhanced Input (IA_Special was already set up with X and Right Mouse in St
#### f) Compile and Save
#### g) Test the placeholder:
#### g) Test the placeholder
1. Press Play
2. Press X key (or Right Mouse Button)
3. You should see "SPECIAL ABILITY ACTIVATED!" appear once
@ -924,21 +983,24 @@ Using Enhanced Input (IA_Special was already set up with X and Right Mouse in St
### 4. Compile and Save
### Expected Result after Compile:
### 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):
### 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.
---

View File

@ -24,19 +24,21 @@
6. **Create Variables:**
| Variable Name | Type | Default Value |
|---------------|------|---------------|
| ------------------- | ------- | ------------- |
| `TravelDirection` | Vector | (0, 1, 0) |
| `TravelSpeed` | Float | 1200.0 |
| `RemainingLifetime` | Float | 4.0 |
| `IsEnemyProjectile` | Boolean | false |
| `Damage` | Integer | 1 |
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- Components panel shows: DefaultSceneRoot → BulletSprite, BulletCollision, TempVisual
- Variables panel shows all 5 variables with correct types
### Expected Result in Viewport (Blueprint Editor):
### Expected Result in Viewport (Blueprint Editor)
- Small cube visible (the TempVisual placeholder)
- Sphere collision visible (radius 8)
@ -46,7 +48,7 @@
1. In Event Graph, from **Event Tick:**
#### a) Calculate movement:
### a) Calculate movement
1. Right-click → `Get TravelDirection`
2. Right-click → `Get TravelSpeed`
@ -70,7 +72,7 @@
- Connect the Add result to "New Location"
- Connect execution wire from Event Tick to Set Actor Location
#### b) Check lifetime and destroy when expired:
#### b) Check lifetime and destroy when expired
9. Right-click → `Get RemainingLifetime`
@ -115,21 +117,23 @@ Event Tick ──► Set Actor Location ──► Set RemainingLifetime ──
Destroy Actor (nothing)
```
### 2. CREATE "Initialize" FUNCTION:
### 2. CREATE "Initialize" FUNCTION
#### a) Create the function
#### a) Create the function:
1. In "My Blueprint" panel (left side), find "Functions" section
2. Click the **"+"** button next to Functions
3. Name the new function `Initialize`
4. Double-click to open the function graph
#### b) Add input parameters:
#### b) Add input parameters
1. In the function graph, you should see a purple "Initialize" entry node
2. With the entry node selected, look at the Details panel (right side)
3. Find "Inputs" section and click "+" to add parameters:
| Parameter | Type | Default |
|-----------|------|---------|
| ------------- | ------- | ------- |
| `Direction` | Vector | - |
| `Speed` | Float | - |
| `bIsEnemy` | Boolean | - |
@ -138,7 +142,7 @@ Event Tick ──► Set Actor Location ──► Set RemainingLifetime ──
The entry node should now show 5 input pins.
#### c) Build the function logic:
#### c) Build the function logic
1. **Normalize and set direction:**
- Drag from **Direction** parameter (yellow pin) → search `Normalize` → add it
@ -161,7 +165,7 @@ The entry node should now show 5 input pins.
- Right-click → `Set Damage`
- Drag from **DamageValue** parameter → Set Damage input
#### d) CRITICAL - Connect execution wires:
#### d) CRITICAL - Connect execution wires
> **IMPORTANT:** Without execution wires, the SET nodes will NEVER run! You must chain them together.
@ -192,7 +196,8 @@ The entry node should now show 5 input pins.
### 3. Compile and Save
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- "Initialize" function appears under Functions with 5 input parameters
@ -202,7 +207,7 @@ The entry node should now show 5 input pins.
## Step 3.3: Bullet Collision Logic
### 1. ADD THE OVERLAP EVENT:
### 1. ADD THE OVERLAP EVENT
1. In the Components panel (top-left), click on **"BulletCollision"** to select it
@ -214,7 +219,7 @@ The entry node should now show 5 input pins.
3. The Event Graph now shows a red node: **"On Component Begin Overlap (BulletCollision)"**
- This fires whenever another actor overlaps with the bullet's collision sphere
### 2. CHECK IF THIS IS AN ENEMY PROJECTILE:
### 2. CHECK IF THIS IS AN ENEMY PROJECTILE
4. Right-click → `Get IsEnemyProjectile`
- This gets your boolean variable
@ -224,7 +229,7 @@ The entry node should now show 5 input pins.
- TRUE = this is an enemy bullet (should damage player)
- FALSE = this is a player bullet (should damage enemies)
### 3. TRUE BRANCH - Enemy Bullet Hits Player:
### 3. TRUE BRANCH - Enemy Bullet Hits Player
6. From the **"On Component Begin Overlap"** node, look for the **"Other Actor"** output pin
- This is the actor that overlapped with the bullet
@ -254,7 +259,7 @@ The entry node should now show 5 input pins.
12. Connect execution wire:
- Cast to BP_Player → TakeHit → Destroy Actor
### 4. FALSE BRANCH - Player Bullet Hits Enemy (PLACEHOLDER):
### 4. FALSE BRANCH - Player Bullet Hits Enemy (PLACEHOLDER)
> **NOTE:** BP_Enemy doesn't exist yet, so we'll add a placeholder. You'll complete this logic in [Part 4, Step 4.7](part-4-create-enemy.md#step-47-complete-bullet-collision-logic-bp_bullet).
@ -293,7 +298,8 @@ The entry node should now show 5 input pins.
### 4. Compile and Save
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- Event Graph shows "On Component Begin Overlap" event connected to Branch
@ -305,11 +311,12 @@ The entry node should now show 5 input pins.
Now that BP_Bullet exists, we can complete the player's firing functions.
### 1. Open BP_Player Blueprint:
### 1. Open BP_Player Blueprint
- In Content Drawer, navigate to Content → Blueprints
- Double-click "BP_Player" to open the Blueprint Editor
### 2. SET THE BULLET CLASS VARIABLE:
### 2. SET THE BULLET CLASS VARIABLE
1. In My Blueprint panel, find the `BulletClass` variable
2. Click on it to select it
@ -317,25 +324,28 @@ Now that BP_Bullet exists, we can complete the player's firing functions.
4. Click the dropdown and select `BP_Bullet`
5. Compile to save the change
### 3. CREATE SPAWN BULLET FUNCTION:
### 3. CREATE SPAWN BULLET FUNCTION
#### a) In "My Blueprint" panel, under "Functions", click "+"
#### b) Name the function `SpawnBullet`
#### c) Double-click to open function graph
#### d) Add input parameters to the function:
#### d) Add input parameters to the function
- In the function graph, look at the purple "SpawnBullet" entry node
- In Details panel (right side), find "Inputs" section
- Click "+" to add a new input parameter
| Parameter | Type |
|-----------|------|
| --------------- | ------ |
| `SpawnLocation` | Vector |
| `Direction` | Vector |
The entry node should now show two input pins: SpawnLocation and Direction
#### e) Build the spawning logic:
#### e) Build the spawning logic
1. Right-click → search `Spawn Actor from Class` → add it
@ -352,7 +362,7 @@ The entry node should now show two input pins: SpawnLocation and Direction
4. Connect execution wire:
- Drag from SpawnBullet entry node (white triangle) → SpawnActor (white triangle)
#### f) Initialize the spawned bullet:
#### f) Initialize the spawned bullet
5. Add the Cast node:
- From SpawnActor's **"Return Value"** output (blue pin on right side), drag → search `Cast to BP_Bullet`
@ -408,13 +418,15 @@ The entry node should now show two input pins: SpawnLocation and Direction
#### g) Compile (should have no errors now)
### 4. CREATE FIRE VOLLEY FUNCTION:
### 4. CREATE FIRE VOLLEY FUNCTION
#### a) In "My Blueprint" panel, under "Functions", click "+"
#### b) Name the function `FireVolley`
#### c) Double-click to open function graph
#### d) Inside FireVolley function graph, build this logic step by step:
#### d) Inside FireVolley function graph, build this logic step by step
---
@ -489,18 +501,14 @@ The entry node should now show two input pins: SpawnLocation and Direction
- Right-click → `Get VolleySize`
- Right-click → `Subtract (Integer)` → connect VolleySize, type `1`
- Result: (VolleySize - 1) = 2 for default
- Right-click → `Get VolleySpread`
- Right-click → `Multiply (float)` → connect VolleySpread and (VolleySize-1)
- NOTE: The integer will auto-convert to float
- Result: VolleySpread * (VolleySize - 1) = 12 * 2 = 24
- Result: VolleySpread _(VolleySize - 1) = 12_ 2 = 24
- Right-click → `Divide (float)` → connect the multiply result, type `2`
- Result: 24 / 2 = 12
- Right-click → `Negate (float)` or "Multiply by -1"
- Result: -12 (this is StartAngle)
- Right-click → `Set` → create a LOCAL variable "StartAngle" (Float)
- OR just keep the wire connected (we'll use it in the loop)
@ -519,12 +527,11 @@ The entry node should now show two input pins: SpawnLocation and Direction
- Right-click → `Multiply (float)`
- Connect loop "Index" to first input (auto-converts int to float)
- Connect VolleySpread to second input
- Result: Index * VolleySpread (0, 12, 24 for indices 0, 1, 2)
- Result: Index \* VolleySpread (0, 12, 24 for indices 0, 1, 2)
- Right-click → `Add (float)`
- Connect StartAngle (the -12 from step 10) to first input
- Connect (Index * VolleySpread) to second input
- Result: StartAngle + (Index * VolleySpread) = -12, 0, +12 degrees
- Connect (Index \* VolleySpread) to second input
- Result: StartAngle + (Index \* VolleySpread) = -12, 0, +12 degrees
This is the angle in DEGREES. Store or continue with this value.
@ -606,12 +613,13 @@ The entry node should now show two input pins: SpawnLocation and Direction
```
**Test Values (VolleySize=3, VolleySpread=12):**
- StartAngle = -(12 * 2) / 2 = -12°
- Bullet 0: -12 + (0 * 12) = -12° → slightly left
- Bullet 1: -12 + (1 * 12) = 0° → straight up
- Bullet 2: -12 + (2 * 12) = +12° → slightly right
### 5. CONNECT FIREVOLLEY TO THE FIRE RATE LIMITER:
- StartAngle = -(12 \* 2) / 2 = -12°
- Bullet 0: -12 + (0 \* 12) = -12° → slightly left
- Bullet 1: -12 + (1 \* 12) = 0° → straight up
- Bullet 2: -12 + (2 \* 12) = +12° → slightly right
### 5. CONNECT FIREVOLLEY TO THE FIRE RATE LIMITER
1. Go back to the Event Graph tab
2. Find your IA_Fire logic from [Step 2.5](part-2-create-player.md#step-25-create-player-firing-logic)
@ -629,11 +637,13 @@ The entry node should now show two input pins: SpawnLocation and Direction
### 6. Compile and Save
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- "FireVolley" and "SpawnBullet" functions appear under Functions in My Blueprint panel
### Expected Result in Play mode:
### Expected Result in Play mode
- Pressing Z or Left Mouse Button spawns 3 bullets in a spread pattern
- Bullets travel upward from player position
- Rapid fire when holding the button (every 0.08 seconds)

View File

@ -23,7 +23,7 @@
6. **Create Variables:**
| Variable Name | Type | Default Value |
|---------------|------|---------------|
| --------------------- | ------------------------ | ------------- |
| `MaxHealth` | Integer | 12 |
| `CurrentHealth` | Integer | 12 |
| `ScoreValue` | Integer | 50 |
@ -41,12 +41,14 @@
| `FireTimer` | Float | 0.0 |
| `BulletClass` | Class Reference to Actor | (set later) |
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- Components panel shows: DefaultSceneRoot → EnemySprite, EnemyCollision, TempVisual
- Variables panel shows all 16 variables with correct types and defaults
### Expected Result in Viewport (Blueprint Editor):
### Expected Result in Viewport (Blueprint Editor)
- Cube visible (the TempVisual placeholder, larger than player)
- Box collision visible (30x30x10)
@ -56,20 +58,23 @@
1. In Event Graph, from **"Event BeginPlay":**
#### a) Set CurrentHealth = MaxHealth
### a) Set CurrentHealth = MaxHealth
#### b) Get Actor Location → Break Vector → Set BaseX = X value
### b) Get Actor Location → Break Vector → Set BaseX = X value
### c) Random Float in Range (0, 6.28) → Set WaveSeed
#### c) Random Float in Range (0, 6.28) → Set WaveSeed
(6.28 ≈ 2π for wave randomization)
#### d) Random Float in Range (0, FireInterval) → Set FireTimer
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- BeginPlay event connected to variable setters
### Expected Result in Play mode:
### Expected Result in Play mode
- Each enemy spawns with randomized WaveSeed (0 to 6.28)
- Each enemy starts with different FireTimer offset
- This creates varied, non-synchronized enemy behavior
@ -80,11 +85,13 @@
1. In Event Graph, from **"Event Tick":**
#### a) VERTICAL MOVEMENT:
- Get Actor Location → Break into X, Y, Z
- Subtract (VerticalSpeed * DeltaSeconds) from Y
### a) VERTICAL MOVEMENT
- Get Actor Location → Break into X, Y, Z
- Subtract (VerticalSpeed \* DeltaSeconds) from Y
### b) HORIZONTAL SINE WAVE
#### b) HORIZONTAL SINE WAVE:
- Get Game Time in Seconds
- Add WaveSeed
- Multiply by HorizontalFrequency
@ -95,7 +102,8 @@
#### c) Set Actor Location with new X, Y (Z stays 0)
#### d) DESPAWN CHECK:
#### d) DESPAWN CHECK
- If Y < DespawnY: Destroy Actor
- If Abs(X) > 1400: Destroy Actor
@ -115,11 +123,13 @@
└──────────────────────────────────────────────┘
```
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- Event Tick connected to movement and despawn logic
### Expected Result in Play mode:
### Expected Result in Play mode
- Enemies drift downward at VerticalSpeed (220 units/sec)
- Enemies oscillate horizontally in sine wave pattern
- Each enemy has different horizontal phase (due to WaveSeed)
@ -129,24 +139,27 @@
## Step 4.4: Enemy Firing Logic
### 1. Continue in Event Tick (after movement):
### 1. Continue in Event Tick (after movement)
#### a) Decrease FireTimer by DeltaSeconds
#### b) Branch: if FireTimer <= 0:
#### b) Branch: if FireTimer <= 0
- Reset FireTimer to FireInterval
- Call "FireBurst" function
### 2. CREATE "FireBurst" FUNCTION:
### 2. CREATE "FireBurst" FUNCTION
#### a) Add function "FireBurst"
#### b) Inside:
#### b) Inside
- Get BulletsPerBurst
- Calculate angle step: `360 / BulletsPerBurst` (for full circle)
- OR: `BurstSpread / (BulletsPerBurst - 1)` (for partial arc)
#### c) For Loop from 0 to BulletsPerBurst - 1:
#### c) For Loop from 0 to BulletsPerBurst - 1
- Calculate angle: `i * AngleStep`
- Convert to direction vector:
- X = Sin(angle in radians)
@ -171,11 +184,13 @@
```
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- "FireBurst" function appears under Functions
### Expected Result in Play mode:
### Expected Result in Play mode
- Every 0.35 seconds (FireInterval), enemy fires bullet burst
- 20 bullets spawn in a full 360° circle pattern
- Bullets travel outward from enemy position
@ -185,37 +200,42 @@
## Step 4.5: Enemy Damage and Death
### 1. CREATE "ApplyDamage" FUNCTION:
### 1. CREATE "ApplyDamage" FUNCTION
#### a) Add input: `DamageAmount` (Integer)
#### b) Inside:
#### b) Inside
- Subtract DamageAmount from CurrentHealth
- If CurrentHealth <= 0:
- Call HandleDeath
### 2. CREATE "HandleDeath" FUNCTION:
### 2. CREATE "HandleDeath" FUNCTION
#### a) Inside
#### a) Inside:
- Get reference to ScoreManager (we'll create in [Part 7](part-7-score-manager-ui.md))
- Call AddScore, passing ScoreValue
- Spawn death effect (optional)
- Destroy Actor
### 3. COLLISION WITH PLAYER:
### 3. COLLISION WITH PLAYER
#### a) On the EnemyCollision component overlap event
#### a) On the EnemyCollision component overlap event:
- Cast Other Actor to BP_Player
- If successful:
- Call TakeHit(1) on player
- Call HandleDeath() on self
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- "ApplyDamage" and "HandleDeath" functions appear under Functions
- EnemyCollision has overlap event in Event Graph
### Expected Result in Play mode:
### Expected Result in Play mode
- Player bullets hitting enemy: Enemy health decreases
- After 12 hits (MaxHealth): Enemy disappears, score increases by 50
- Player colliding with enemy: Player takes 1 damage, enemy dies
@ -226,47 +246,54 @@
Now that BP_Enemy exists (and BP_Bullet from Part 3), we can complete the special ability that was set up as a placeholder in [Part 2, Step 2.6](part-2-create-player.md#step-26-create-player-damage-and-special-ability).
### 1. Open BP_Player Blueprint:
### 1. Open BP_Player Blueprint
1. Content Browser → Blueprints → double-click `BP_Player`
2. Go to **Event Graph** tab
3. Find the `EnhancedInputAction IA_Special` event node (created in Step 2.6)
4. Locate the `Print String "SPECIAL ABILITY ACTIVATED!"` node
### 2. Replace Print String with destruction logic:
### 2. Replace Print String with destruction logic
#### a) Delete the placeholder
#### a) Delete the placeholder:
- Select the `Print String` node
- Press Delete
- (Keep the TODO comment if you want, or delete it too)
#### b) Destroy all enemies:
#### b) Destroy all enemies
1. From `Set SpecialUsed` output execution pin, drag → search `Get All Actors of Class` → add it
2. Click the "Actor Class" dropdown → search and select `BP_Enemy`
3. The output "Out Actors" is an array of all enemies currently in the level
#### c) Loop through enemies and destroy them:
#### c) Loop through enemies and destroy them
1. From `Get All Actors of Class`, drag execution → search `For Each Loop` → add it
2. Connect the "Out Actors" array (blue pin) to the loop's "Array" input (blue pin)
3. From "Loop Body" execution pin, drag → search `Destroy Actor` → add it
4. Connect "Array Element" (blue pin - the current enemy in the loop) to Destroy Actor's "Target" input
#### d) Now destroy enemy bullets (after enemies are done):
#### d) Now destroy enemy bullets (after enemies are done)
1. From `For Each Loop`'s **"Completed"** execution pin (NOT "Loop Body"), drag → search `Get All Actors of Class` → add it
2. Click "Actor Class" dropdown → select `BP_Bullet`
3. From this Get All Actors, drag execution → search `For Each Loop` → add another loop
#### e) Filter to only destroy ENEMY bullets (keep player bullets):
#### e) Filter to only destroy ENEMY bullets (keep player bullets)
1. From the second loop's "Loop Body" pin, drag → search `Branch` → add it
2. From "Array Element" (the current bullet), drag → search `Get IsEnemyProjectile` → add it
- This reads the IsEnemyProjectile variable from BP_Bullet
3. Connect `IsEnemyProjectile` output (boolean) to Branch's "Condition" input
#### f) Destroy only if it's an enemy bullet:
#### f) Destroy only if it's an enemy bullet
1. From Branch's **"True"** pin, drag → search `Destroy Actor` → add it
2. Connect "Array Element" (the bullet) to Destroy Actor's "Target"
3. Leave Branch's "False" pin unconnected (player bullets are preserved)
### 3. Visual diagram of completed special ability:
### 3. Visual diagram of completed special ability
```
┌─────────────────────────────┐
@ -332,11 +359,13 @@ Now that BP_Enemy exists (and BP_Bullet from Part 3), we can complete the specia
### 4. Compile and Save
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- No warnings about missing classes (BP_Enemy and BP_Bullet now exist)
### Expected Result in Play mode:
### Expected Result in Play mode
- Press X (or Right Mouse Button) once:
- ALL enemies on screen instantly disappear
- ALL enemy bullets (red) instantly disappear
@ -350,19 +379,22 @@ Now that BP_Enemy exists (and BP_Bullet from Part 3), we can complete the specia
Now that BP_Enemy exists (and the `ApplyDamage` function from Step 4.3), we can complete the collision logic that was set up as a placeholder in [Part 3, Step 3.3](part-3-create-bullet.md#step-33-bullet-collision-logic).
### 1. Open BP_Bullet Blueprint:
### 1. Open BP_Bullet Blueprint
1. Content Browser → Blueprints → double-click `BP_Bullet`
2. Go to **Event Graph** tab
3. Find the `On Component Begin Overlap (BulletCollision)` event node
4. Locate the `Print String "TODO: Damage enemy"` node on the FALSE branch
### 2. Replace Print String with enemy damage logic:
### 2. Replace Print String with enemy damage logic
#### a) Delete the placeholder
#### a) Delete the placeholder:
- Select the `Print String` node
- Press Delete
#### b) Add Cast to BP_Enemy:
#### b) Add Cast to BP_Enemy
1. Right-click → search `Cast to BP_Enemy` → add it
2. Connect execution wire:
@ -372,7 +404,8 @@ Now that BP_Enemy exists (and the `ApplyDamage` function from Step 4.3), we can
- From the **"On Component Begin Overlap"** node, drag from **"Other Actor"** to the Cast's "Object" input
- (You can Ctrl+drag to create a second wire without removing the existing one to BP_Player)
#### c) Call ApplyDamage on the enemy:
#### c) Call ApplyDamage on the enemy
4. From "Cast Succeeded" on the BP_Enemy cast:
- From the cast's **"As BP Enemy"** output pin, drag → search `ApplyDamage`
- This calls the ApplyDamage function you created in Step 4.3
@ -381,7 +414,8 @@ Now that BP_Enemy exists (and the `ApplyDamage` function from Step 4.3), we can
- Right-click → `Get Damage` (the bullet's damage variable)
- Connect to ApplyDamage's "DamageAmount" input
#### d) Destroy the bullet after damaging:
#### d) Destroy the bullet after damaging
6. From ApplyDamage, drag execution → `Destroy Actor`
- Leave "Target" as "Self" (destroys this bullet)
@ -412,11 +446,13 @@ Now that BP_Enemy exists (and the `ApplyDamage` function from Step 4.3), we can
### 3. Compile and Save
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- No warnings - both BP_Player and BP_Enemy casts are valid now
### Expected Result in Play mode:
### Expected Result in Play mode
- Player bullets (IsEnemyProjectile=false) hitting enemies:
- Enemy takes damage, bullet disappears
- After enough hits, enemy dies and awards score

View File

@ -14,7 +14,7 @@
5. **Create Variables:**
| Variable Name | Type | Default Value |
|---------------|------|---------------|
| ------------------------ | --------------- | ------------------------- |
| `EnemyClass` | Class Reference | (will reference BP_Enemy) |
| `SpawnAreaHalfWidth` | Float | 900.0 |
| `GameDuration` | Float | 300.0 (5 minutes) |
@ -23,7 +23,8 @@
| `SpawnTimer` | Float | 0.0 |
| `SpawningActive` | Boolean | true |
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- Variables panel shows all 7 variables with correct types
- No components needed (spawner is invisible logic actor)
@ -32,23 +33,24 @@
## Step 5.2: Spawn Rate Curve
### 1. Create Variable:
### 1. Create Variable
- `SpawnCurve` (Curve Float)
### 2. To create the curve asset:
### 2. To create the curve asset
1. In Content Browser, right-click → **Miscellaneous → Curve**
2. Select "CurveFloat"
3. Name it `SpawnRateCurve`
4. Double-click to open Curve Editor
### 3. In Curve Editor:
### 3. In Curve Editor
- Right-click on the curve → Add Key
- Create these keyframes:
| Time | Value | Description |
|------|-------|-------------|
| ---- | ----- | -------------------- |
| 0.0 | 0.4 | slow spawn at start |
| 0.5 | 2.0 | medium spawn halfway |
| 1.0 | 4.5 | fast spawn at end |
@ -58,11 +60,13 @@
### 4. In BP_EnemySpawner, set SpawnCurve default to this curve asset
### Expected Result in Curve Editor:
### Expected Result in Curve Editor
- Curve line visible starting at (0, 0.4), rising through (0.5, 2.0), ending at (1.0, 4.5)
- Smooth interpolation between keyframes
### Expected Result in Play mode:
### Expected Result in Play mode
- Game start: ~0.4 enemies spawn per second (slow)
- At 2.5 minutes: ~2 enemies spawn per second (medium)
- At 5 minutes: ~4.5 enemies spawn per second (intense)
@ -71,67 +75,81 @@
## Step 5.3: Spawning Logic
### 1. In Event Graph, from Event Tick:
### 1. In Event Graph, from Event Tick
#### a) Check if SpawningActive is true
- If false, do nothing
#### b) Update ElapsedTime:
#### b) Update ElapsedTime
- Add DeltaSeconds to ElapsedTime
#### c) Calculate normalized time:
#### c) Calculate normalized time
- Divide ElapsedTime by GameDuration
- Clamp between 0 and 1
#### d) Get spawn rate from curve:
#### d) Get spawn rate from curve
- Get SpawnCurve
- Call `Get Float Value` with normalized time
- This returns spawns per second
#### e) Update SpawnTimer:
#### e) Update SpawnTimer
- Subtract DeltaSeconds from SpawnTimer
- If SpawnTimer <= 0:
- Reset SpawnTimer to `(1.0 / SpawnsPerSecond)`
- Call SpawnWave function
### 2. CREATE "SpawnWave" FUNCTION:
### 2. CREATE "SpawnWave" FUNCTION
#### a) Inside
#### a) Inside:
- Calculate burst size based on time:
```
BaseCount = 1 + (NormalizedTime * 6)
BurstSize = BaseCount + Random(0, 2)
Clamp between 1 and 12
```
#### b) For Loop from 0 to BurstSize - 1:
#### b) For Loop from 0 to BurstSize - 1
- Call SpawnEnemy function
### 3. CREATE "SpawnEnemy" FUNCTION:
### 3. CREATE "SpawnEnemy" FUNCTION
#### a) Inside
#### a) Inside:
- Check: Get All Actors of Class (BP_Enemy)
- Get array length
- If >= MaxSimultaneousEnemies: Return (don't spawn)
#### b) Calculate spawn position:
#### b) Calculate spawn position
- X = Random Float in Range (-SpawnAreaHalfWidth, SpawnAreaHalfWidth)
- Y = Get this actor's Y position (top of screen)
- Z = 0
#### c) Spawn Actor from Class:
#### c) Spawn Actor from Class
- Class: EnemyClass
- Location: calculated position
- Rotation: (0, 0, 0)
### 4. CREATE "StopSpawning" FUNCTION:
### 4. CREATE "StopSpawning" FUNCTION
- Set SpawningActive = false
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- "SpawnWave", "SpawnEnemy", "StopSpawning" functions appear under Functions
### Expected Result in Play mode:
### Expected Result in Play mode
- Enemies spawn at top of screen (Y=500) at random X positions
- Spawn rate increases over time following the curve
- Maximum 120 enemies on screen at once

View File

@ -14,14 +14,15 @@
5. **Create Variables:**
| Variable Name | Type | Default Value |
|---------------|------|---------------|
| ------------------ | ----------------------------------- | ------------- |
| `PlayerReference` | Object Reference to BP_Player | - |
| `SpawnerReference` | Object Reference to BP_EnemySpawner | - |
| `GameDuration` | Float | 300.0 |
| `ElapsedTime` | Float | 0.0 |
| `GameActive` | Boolean | true |
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- Variables panel shows all 5 variables with correct types
- No components needed (director is invisible logic actor)
@ -30,27 +31,32 @@
## Step 6.2: Game Director Initialization
### 1. From Event BeginPlay:
### 1. From Event BeginPlay
#### a) Find player in scene
#### a) Find player in scene:
- Get All Actors of Class → BP_Player
- Get first element (index 0)
- Set PlayerReference
#### b) Find spawner in scene:
#### b) Find spawner in scene
- Get All Actors of Class → BP_EnemySpawner
- Get first element
- Set SpawnerReference
#### c) Initialize ScoreManager:
#### c) Initialize ScoreManager
- Get ScoreManager reference
- Call RegisterGameStart with initial lives and duration
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- BeginPlay event connected to "Get All Actors of Class" nodes
### Expected Result in Play mode:
### Expected Result in Play mode
- Game Director automatically finds Player, Spawner, and ScoreManager
- UI initializes with correct starting values (Lives: 3, Time: 05:00)
@ -58,41 +64,49 @@
## Step 6.3: Game Director Update Logic
### 1. From Event Tick:
### 1. From Event Tick
#### a) Check if GameActive
- If false, skip everything
#### b) Update elapsed time:
#### b) Update elapsed time
- Add DeltaSeconds to ElapsedTime
#### c) Calculate remaining time:
#### c) Calculate remaining time
- Subtract ElapsedTime from GameDuration
- Max with 0 (don't go negative)
#### d) Update UI timer:
#### d) Update UI timer
- Get ScoreManager
- Call UpdateTimer with remaining time
#### e) Check for victory:
#### e) Check for victory
- If ElapsedTime >= GameDuration:
- Set GameActive = false
- Get SpawnerReference → Call StopSpawning
- Get ScoreManager → Call HandleGameClear
### 2. CREATE "HandlePlayerDeath" FUNCTION:
### 2. CREATE "HandlePlayerDeath" FUNCTION
#### a) Inside
#### a) Inside:
- Set GameActive = false
- Get SpawnerReference → Call StopSpawning
- Get ScoreManager → Call HandleGameOver
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- "HandlePlayerDeath" function appears under Functions
- Event Tick connected to timer update and victory check
### Expected Result in Play mode:
### Expected Result in Play mode
- Timer counts down from 05:00 to 00:00
- At 00:00: "Mission Complete" appears, enemies stop spawning
- When player dies: "Game Over" appears, enemies stop spawning

View File

@ -11,8 +11,10 @@
3. Name: `WBP_HUD`
4. Double-click to open Widget Designer
### Expected Result:
### Expected Result
Widget Designer opens with:
- Hierarchy panel on left
- Canvas preview in center
- Details panel on right
@ -22,9 +24,10 @@ Widget Designer opens with:
## Step 7.2: Design HUD Layout
### 1. In the Palette panel (left side), search for "Canvas Panel"
- Drag Canvas Panel to the Hierarchy (if not already there)
### 2. Add Score Text:
### 2. Add Score Text
1. In Palette, search for "Text"
2. Drag "Text" widget onto Canvas Panel in hierarchy
@ -41,7 +44,7 @@ Widget Designer opens with:
- Font Size: `24`
- Color: White
### 3. Add Lives Text:
### 3. Add Lives Text
1. Drag another "Text" widget
2. Rename to `LivesText`
@ -49,7 +52,7 @@ Widget Designer opens with:
4. Text: `Lives: 3`
5. Same font settings as Score
### 4. Add Timer Text:
### 4. Add Timer Text
1. Drag another "Text" widget
2. Rename to `TimerText`
@ -59,9 +62,10 @@ Widget Designer opens with:
### 5. Click "Compile" and "Save" (top buttons)
### Expected Result:
### Expected Result
Preview shows:
```
┌────────────────────────────────────┐
│ Score: 0 │
@ -84,13 +88,14 @@ Preview shows:
5. **Create Variables:**
| Variable Name | Type | Default Value |
|---------------|------|---------------|
| ---------------- | --------------------------- | ---------------- |
| `Score` | Integer | 0 |
| `CurrentLives` | Integer | 3 |
| `HUDWidget` | Object Reference to WBP_HUD | - |
| `HUDWidgetClass` | Class Reference | (set to WBP_HUD) |
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- Variables panel shows all 4 variables with correct types
@ -98,25 +103,30 @@ Preview shows:
## Step 7.4: Score Manager Initialization
### 1. From Event BeginPlay:
### 1. From Event BeginPlay
#### a) Create HUD Widget
#### a) Create HUD Widget:
- Right-click → `Create Widget`
- Class: Select WBP_HUD (or use HUDWidgetClass variable)
- Owning Player: Get Player Controller (index 0)
#### b) Store widget reference:
#### b) Store widget reference
- Set HUDWidget to the created widget
#### c) Add to viewport:
#### c) Add to viewport
- Right-click → `Add to Viewport`
- Connect widget reference as target
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- BeginPlay event connected to Create Widget → Add to Viewport
### Expected Result in Play mode:
### Expected Result in Play mode
- HUD appears immediately when game starts
- HUD displays in top-left corner of screen
- Text is visible and readable (white on game background)
@ -125,22 +135,25 @@ Preview shows:
## Step 7.5: Score Manager Functions
### 1. CREATE "RegisterGameStart" FUNCTION:
### 1. CREATE "RegisterGameStart" FUNCTION
**Inputs:**
- `InitialLives` (Integer)
- `Duration` (Float)
**Inside:**
- Set Score = 0
- Set CurrentLives = InitialLives
- Update all UI labels
### 2. CREATE "AddScore" FUNCTION:
### 2. CREATE "AddScore" FUNCTION
**Input:** `Amount` (Integer)
**Inside:**
- Add Amount to Score
- Update Score label in HUD:
- Get HUDWidget
@ -148,42 +161,50 @@ Preview shows:
- Get "ScoreText" widget
- Set Text to "Score: " + Score
### 3. CREATE "SetLives" FUNCTION:
### 3. CREATE "SetLives" FUNCTION
**Input:** `Lives` (Integer)
**Inside:**
- Set CurrentLives = Lives
- Update Lives label in HUD
### 4. CREATE "UpdateTimer" FUNCTION:
### 4. CREATE "UpdateTimer" FUNCTION
**Input:** `TimeRemaining` (Float)
**Inside:**
- Convert to minutes:seconds format:
```
Minutes = Floor(TimeRemaining / 60)
Seconds = Floor(TimeRemaining mod 60)
```
- Format string: "Time: MM:SS"
- Update Timer label
### 5. CREATE "HandleGameOver" FUNCTION:
### 5. CREATE "HandleGameOver" FUNCTION
**Inside:**
- Set Timer text to "Game Over"
### 6. CREATE "HandleGameClear" FUNCTION:
### 6. CREATE "HandleGameClear" FUNCTION
**Inside:**
- Set Timer text to "Mission Complete"
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- All 6 functions appear under Functions panel
### Expected Result in Play mode:
### Expected Result in Play mode
- Score updates instantly when enemies are killed (+50 each)
- Lives display updates when player is hit
- Timer counts down in MM:SS format

View File

@ -19,7 +19,8 @@
- Default Pawn Class: Select `BP_Player`
- Player Controller Class: Keep default
### Expected Result after Compile:
### Expected Result after Compile
- Compile button shows GREEN checkmark
- Details panel shows "Default Pawn Class" set to BP_Player
@ -33,7 +34,8 @@
- Default GameMode: Select `BP_BulletHellGameMode`
4. Close Project Settings
### Expected Result:
### Expected Result
- Project Settings shows BP_BulletHellGameMode as Default GameMode
- This means the game will automatically spawn BP_Player when Play is pressed
@ -48,7 +50,8 @@
5. Name: `BulletHellLevel`
6. Click Save
### Expected Result:
### Expected Result
- New level file "BulletHellLevel" appears in Content folder
- Level is completely empty (black viewport)
- Outliner shows only default actors (if any)
@ -59,7 +62,7 @@
In the level (main viewport), we need to add game actors:
### 1. ADD CAMERA:
### 1. ADD CAMERA
1. In Place Actors panel (left side, or Window → Place Actors)
2. Search for "Camera Actor"
@ -73,40 +76,43 @@ In the level (main viewport), we need to add game actors:
- Check the box, set Player Index to `0`
**For Orthographic View:**
- Click on Camera component
- In Details, set "Projection Mode" to `Orthographic`
- Set "Ortho Width" to `1920` (or your screen width)
### 2. ADD PLAYER:
### 2. ADD PLAYER
1. From Content Browser, drag `BP_Player` into level
2. Position: `X=0, Y=-300, Z=0` (bottom center)
### 3. ADD ENEMY SPAWNER:
### 3. ADD ENEMY SPAWNER
1. Drag `BP_EnemySpawner` into level
2. Position: `X=0, Y=500, Z=0` (top of screen)
3. In Details panel, set EnemyClass to `BP_Enemy`
### 4. ADD GAME DIRECTOR:
### 4. ADD GAME DIRECTOR
1. Drag `BP_GameDirector` into level
2. Position doesn't matter (it's invisible)
### 5. ADD SCORE MANAGER:
### 5. ADD SCORE MANAGER
1. Drag `BP_ScoreManager` into level
2. Position doesn't matter
### 6. Save the level (`Ctrl+S`)
### Expected Result in Level Viewport:
### Expected Result in Level Viewport
- Camera actor visible at top of scene (Z=1000)
- BP_Player visible at bottom center (Y=-300)
- BP_EnemySpawner visible at top (Y=500)
- BP_GameDirector and BP_ScoreManager in Outliner (invisible actors)
### Expected Result in Outliner:
### Expected Result in Outliner
```
- CameraActor
- BP_Player
@ -125,7 +131,8 @@ In the level (main viewport), we need to add game actors:
- Editor Startup Map: Select `BulletHellLevel`
- Game Default Map: Select `BulletHellLevel`
### Expected Result:
### Expected Result
- Project Settings shows BulletHellLevel as both startup and default map
- Launching the game (standalone or in editor) loads this level automatically

View File

@ -6,26 +6,31 @@
## Step 9.1: Assign Blueprint References
### 1. Open BP_Player:
### 1. Open BP_Player
- In Details panel (with blueprint open)
- Set BulletClass: `BP_Bullet`
### 2. Open BP_EnemySpawner:
### 2. Open BP_EnemySpawner
- Set EnemyClass: `BP_Enemy`
### 3. Open BP_Enemy:
### 3. Open BP_Enemy
- Set BulletClass: `BP_Bullet`
### 4. Compile and Save all blueprints
### Expected Result after Compile (all blueprints):
### Expected Result after Compile (all blueprints)
- All blueprints compile with GREEN checkmark
- No "None" or missing references in variable defaults
- BP_Player.BulletClass → BP_Bullet
- BP_Enemy.BulletClass → BP_Bullet
- BP_EnemySpawner.EnemyClass → BP_Enemy
### Expected Result in Play mode:
### Expected Result in Play mode
- Player can shoot bullets (BP_Bullet spawns)
- Enemies spawn and shoot bullets
- All collision/damage systems functional
@ -36,7 +41,7 @@
Now replace the temporary cube visuals with proper colored materials:
### 1. REMOVE TEMPORARY COMPONENTS:
### 1. REMOVE TEMPORARY COMPONENTS
1. Open BP_Player blueprint
2. In Components panel, select "TempVisual" (the cube added in [Step 2.2](part-2-create-player.md#step-22-add-player-visual-components))
@ -46,7 +51,7 @@ Now replace the temporary cube visuals with proper colored materials:
### 2. Content Browser → Materials folder
### 3. PLAYER MATERIAL:
### 3. PLAYER MATERIAL
1. Right-click → Material
2. Name: `M_Player`
@ -56,19 +61,21 @@ Now replace the temporary cube visuals with proper colored materials:
6. Connect to Base Color
7. Save and Close
### 4. BULLET MATERIALS:
### 4. BULLET MATERIALS
- Create `M_PlayerBullet` - Yellow `(1, 1, 0)`
- Create `M_EnemyBullet` - Red `(1, 0, 0)`
### 5. ENEMY MATERIAL:
### 5. ENEMY MATERIAL
- Create `M_Enemy` - Magenta `(1, 0, 1)`
### 6. Apply materials to sprite components in each Blueprint
(Or use Sprite assets if you have 2D images)
### Expected Result in Play mode:
### Expected Result in Play mode
- Player visible as blue shape
- Player bullets visible as yellow shapes
- Enemy bullets visible as red shapes
@ -79,15 +86,18 @@ Now replace the temporary cube visuals with proper colored materials:
## Step 9.3: Add Background (Optional)
### 1. In level, add a Plane mesh:
### 1. In level, add a Plane mesh
- Place Actors → Basic → Plane
- Scale: `X=20, Y=30, Z=1`
- Position: `X=0, Y=0, Z=-100` (behind everything)
### 2. Create dark space material:
### 2. Create dark space material
- `M_Background` - Dark blue/black
### Expected Result in Play mode:
### Expected Result in Play mode
- Dark background visible behind all game elements
- Game elements (player, enemies, bullets) clearly visible against background
- Background doesn't interfere with gameplay (Z=-100, behind everything)
@ -97,12 +107,13 @@ Now replace the temporary cube visuals with proper colored materials:
## Step 9.4: Test the Game
### 1. Click "Play" button (green arrow in main toolbar)
OR press `Alt+P`
### 2. TEST CHECKLIST:
### 2. TEST CHECKLIST
| # | Test | Pass? |
|---|------|-------|
| --- | --------------------------------------------- | ----- |
| 1 | Player moves with WASD or Arrow keys | ☐ |
| 2 | Player stays within screen bounds | ☐ |
| 3 | Player shoots with Z key or Left Mouse | ☐ |
@ -121,7 +132,8 @@ OR press `Alt+P`
### 3. To stop playing: Press `ESC` or click "Stop" button
### Expected Result - Complete Game Test:
### Expected Result - Complete Game Test
- All checklist items above should pass
- Frame rate stable (60+ FPS recommended)
- No crashes or Blueprint errors in Output Log
@ -137,7 +149,8 @@ OR press `Alt+P`
4. Navigate to output folder → WindowsNoEditor → [ProjectName].exe
5. Run the executable to play standalone
### Expected Result:
### Expected Result
- Build completes without errors (check Output Log)
- Executable file created in output folder
- Running .exe launches the game in fullscreen
@ -146,11 +159,12 @@ OR press `Alt+P`
---
## 🎉 Congratulations!
## 🎉 Congratulations
You have completed the Unreal Engine Bullet Hell tutorial!
Your game includes:
- ✅ Player with 3 lives, WASD movement, Z/mouse shooting
- ✅ Volley shooting (3 bullets in spread pattern)
- ✅ Screen-clear special ability (X key, one use)
@ -163,7 +177,7 @@ Your game includes:
---
### Additional Resources:
### Additional Resources
- [Appendix A: Complete Variable Reference](appendix-a-variables.md)
- [Appendix B: Troubleshooting](appendix-b-troubleshooting.md)