mirror of
https://github.com/kuhyx/praca_magisterska.git
synced 2026-07-04 13:23:05 +02:00
chore: markdown linter and fixes
This commit is contained in:
parent
9bf78a2dc1
commit
472f6dc19d
18
games/unreal/tutorial/.markdownlint.json
Normal file
18
games/unreal/tutorial/.markdownlint.json
Normal 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
|
||||||
|
}
|
||||||
6
games/unreal/tutorial/.prettierrc
Normal file
6
games/unreal/tutorial/.prettierrc
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"proseWrap": "preserve",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"printWidth": 300
|
||||||
|
}
|
||||||
@ -17,11 +17,13 @@ This tutorial recreates the Unity "magisterka_1" bullet-hell shooter in Unreal E
|
|||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
### Part 1: Project Setup
|
### Part 1: Project Setup
|
||||||
|
|
||||||
- [Step 1.1: Create New Project](part-1-project-setup.md#step-11-create-new-project)
|
- [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.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)
|
- [Step 1.3: Create Folder Structure](part-1-project-setup.md#step-13-create-folder-structure)
|
||||||
|
|
||||||
### Part 2: Create the Player
|
### Part 2: Create the Player
|
||||||
|
|
||||||
- [Step 2.1: Create Player Blueprint](part-2-create-player.md#step-21-create-player-blueprint)
|
- [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.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)
|
- [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)
|
- [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
|
### Part 3: Create the Bullet
|
||||||
|
|
||||||
- [Step 3.1: Create Bullet Blueprint](part-3-create-bullet.md#step-31-create-bullet-blueprint)
|
- [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.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.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)
|
- [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
|
### Part 4: Create the Enemy
|
||||||
|
|
||||||
- [Step 4.1: Create Enemy Blueprint](part-4-create-enemy.md#step-41-create-enemy-blueprint)
|
- [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.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)
|
- [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)
|
- [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
|
### Part 5: Create Enemy Spawner
|
||||||
|
|
||||||
- [Step 5.1: Create Spawner Blueprint](part-5-create-spawner.md#step-51-create-spawner-blueprint)
|
- [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.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)
|
- [Step 5.3: Spawning Logic](part-5-create-spawner.md#step-53-spawning-logic)
|
||||||
|
|
||||||
### Part 6: Create Game Director
|
### 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.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.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)
|
- [Step 6.3: Game Director Update Logic](part-6-game-director.md#step-63-game-director-update-logic)
|
||||||
|
|
||||||
### Part 7: Create Score Manager / UI
|
### 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.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.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)
|
- [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)
|
- [Step 7.5: Score Manager Functions](part-7-score-manager-ui.md#step-75-score-manager-functions)
|
||||||
|
|
||||||
### Part 8: Create Game Mode and Level
|
### 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.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.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)
|
- [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)
|
- [Step 8.5: Set Default Level](part-8-game-mode-level.md#step-85-set-default-level)
|
||||||
|
|
||||||
### Part 9: Final Setup and Testing
|
### Part 9: Final Setup and Testing
|
||||||
|
|
||||||
- [Step 9.1: Assign Blueprint References](part-9-final-setup.md#step-91-assign-blueprint-references)
|
- [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.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)
|
- [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)
|
- [Step 9.5: Build Standalone Game](part-9-final-setup.md#step-95-build-standalone-game)
|
||||||
|
|
||||||
### Appendices
|
### Appendices
|
||||||
|
|
||||||
- [Appendix A: Complete Variable Reference](appendix-a-variables.md)
|
- [Appendix A: Complete Variable Reference](appendix-a-variables.md)
|
||||||
- [Appendix B: Troubleshooting](appendix-b-troubleshooting.md)
|
- [Appendix B: Troubleshooting](appendix-b-troubleshooting.md)
|
||||||
- [Appendix C: Unity to Unreal Conversion Notes](appendix-c-unity-conversion.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
|
## Navigation
|
||||||
|
|
||||||
Each page includes:
|
Each page includes:
|
||||||
|
|
||||||
- **← Previous** and **Next →** links at the top and bottom
|
- **← Previous** and **Next →** links at the top and bottom
|
||||||
- Links back to this index
|
- Links back to this index
|
||||||
- Cross-references to related sections
|
- 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._
|
||||||
|
|||||||
@ -6,93 +6,93 @@
|
|||||||
|
|
||||||
## BP_Player Variables
|
## BP_Player Variables
|
||||||
|
|
||||||
| Variable Name | Type | Default Value |
|
| Variable Name | Type | Default Value |
|
||||||
|---------------|------|---------------|
|
| -------------- | --------- | ------------- |
|
||||||
| `MoveSpeed` | Float | 750.0 |
|
| `MoveSpeed` | Float | 750.0 |
|
||||||
| `BoundsMin` | Vector 2D | (-850, -450) |
|
| `BoundsMin` | Vector 2D | (-850, -450) |
|
||||||
| `BoundsMax` | Vector 2D | (850, 450) |
|
| `BoundsMax` | Vector 2D | (850, 450) |
|
||||||
| `FireInterval` | Float | 0.08 |
|
| `FireInterval` | Float | 0.08 |
|
||||||
| `FireTimer` | Float | 0.0 |
|
| `FireTimer` | Float | 0.0 |
|
||||||
| `BulletSpeed` | Float | 2200.0 |
|
| `BulletSpeed` | Float | 2200.0 |
|
||||||
| `MaxLives` | Integer | 3 |
|
| `MaxLives` | Integer | 3 |
|
||||||
| `CurrentLives` | Integer | 3 |
|
| `CurrentLives` | Integer | 3 |
|
||||||
| `VolleySize` | Integer | 3 |
|
| `VolleySize` | Integer | 3 |
|
||||||
| `VolleySpread` | Float | 12.0 |
|
| `VolleySpread` | Float | 12.0 |
|
||||||
| `SpecialUsed` | Boolean | false |
|
| `SpecialUsed` | Boolean | false |
|
||||||
| `BulletClass` | Class Ref | BP_Bullet |
|
| `BulletClass` | Class Ref | BP_Bullet |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## BP_Bullet Variables
|
## BP_Bullet Variables
|
||||||
|
|
||||||
| Variable Name | Type | Default Value |
|
| Variable Name | Type | Default Value |
|
||||||
|---------------|------|---------------|
|
| ------------------- | ------- | ------------- |
|
||||||
| `TravelDirection` | Vector | (0, 1, 0) |
|
| `TravelDirection` | Vector | (0, 1, 0) |
|
||||||
| `TravelSpeed` | Float | 1200.0 |
|
| `TravelSpeed` | Float | 1200.0 |
|
||||||
| `RemainingLifetime` | Float | 4.0 |
|
| `RemainingLifetime` | Float | 4.0 |
|
||||||
| `IsEnemyProjectile` | Boolean | false |
|
| `IsEnemyProjectile` | Boolean | false |
|
||||||
| `Damage` | Integer | 1 |
|
| `Damage` | Integer | 1 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## BP_Enemy Variables
|
## BP_Enemy Variables
|
||||||
|
|
||||||
| Variable Name | Type | Default Value |
|
| Variable Name | Type | Default Value |
|
||||||
|---------------|------|---------------|
|
| --------------------- | --------- | ------------- |
|
||||||
| `MaxHealth` | Integer | 12 |
|
| `MaxHealth` | Integer | 12 |
|
||||||
| `CurrentHealth` | Integer | 12 |
|
| `CurrentHealth` | Integer | 12 |
|
||||||
| `ScoreValue` | Integer | 50 |
|
| `ScoreValue` | Integer | 50 |
|
||||||
| `VerticalSpeed` | Float | 220.0 |
|
| `VerticalSpeed` | Float | 220.0 |
|
||||||
| `HorizontalAmplitude` | Float | 250.0 |
|
| `HorizontalAmplitude` | Float | 250.0 |
|
||||||
| `HorizontalFrequency` | Float | 1.8 |
|
| `HorizontalFrequency` | Float | 1.8 |
|
||||||
| `DespawnY` | Float | -750.0 |
|
| `DespawnY` | Float | -750.0 |
|
||||||
| `FireInterval` | Float | 0.35 |
|
| `FireInterval` | Float | 0.35 |
|
||||||
| `BulletsPerBurst` | Integer | 20 |
|
| `BulletsPerBurst` | Integer | 20 |
|
||||||
| `BurstSpread` | Float | 360.0 |
|
| `BurstSpread` | Float | 360.0 |
|
||||||
| `EnemyBulletSpeed` | Float | 1000.0 |
|
| `EnemyBulletSpeed` | Float | 1000.0 |
|
||||||
| `EnemyBulletLifetime` | Float | 6.0 |
|
| `EnemyBulletLifetime` | Float | 6.0 |
|
||||||
| `BaseX` | Float | 0.0 |
|
| `BaseX` | Float | 0.0 |
|
||||||
| `WaveSeed` | Float | 0.0 |
|
| `WaveSeed` | Float | 0.0 |
|
||||||
| `FireTimer` | Float | 0.0 |
|
| `FireTimer` | Float | 0.0 |
|
||||||
| `BulletClass` | Class Ref | BP_Bullet |
|
| `BulletClass` | Class Ref | BP_Bullet |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## BP_EnemySpawner Variables
|
## BP_EnemySpawner Variables
|
||||||
|
|
||||||
| Variable Name | Type | Default Value |
|
| Variable Name | Type | Default Value |
|
||||||
|---------------|------|---------------|
|
| ------------------------ | ----------- | -------------------- |
|
||||||
| `EnemyClass` | Class Ref | BP_Enemy |
|
| `EnemyClass` | Class Ref | BP_Enemy |
|
||||||
| `SpawnAreaHalfWidth` | Float | 900.0 |
|
| `SpawnAreaHalfWidth` | Float | 900.0 |
|
||||||
| `GameDuration` | Float | 300.0 |
|
| `GameDuration` | Float | 300.0 |
|
||||||
| `MaxSimultaneousEnemies` | Integer | 120 |
|
| `MaxSimultaneousEnemies` | Integer | 120 |
|
||||||
| `ElapsedTime` | Float | 0.0 |
|
| `ElapsedTime` | Float | 0.0 |
|
||||||
| `SpawnTimer` | Float | 0.0 |
|
| `SpawnTimer` | Float | 0.0 |
|
||||||
| `SpawningActive` | Boolean | true |
|
| `SpawningActive` | Boolean | true |
|
||||||
| `SpawnCurve` | Curve Float | SpawnRateCurve asset |
|
| `SpawnCurve` | Curve Float | SpawnRateCurve asset |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## BP_GameDirector Variables
|
## BP_GameDirector Variables
|
||||||
|
|
||||||
| Variable Name | Type | Default Value |
|
| Variable Name | Type | Default Value |
|
||||||
|---------------|------|---------------|
|
| ------------------ | ---------- | ---------------- |
|
||||||
| `PlayerReference` | Object Ref | (set at runtime) |
|
| `PlayerReference` | Object Ref | (set at runtime) |
|
||||||
| `SpawnerReference` | Object Ref | (set at runtime) |
|
| `SpawnerReference` | Object Ref | (set at runtime) |
|
||||||
| `GameDuration` | Float | 300.0 |
|
| `GameDuration` | Float | 300.0 |
|
||||||
| `ElapsedTime` | Float | 0.0 |
|
| `ElapsedTime` | Float | 0.0 |
|
||||||
| `GameActive` | Boolean | true |
|
| `GameActive` | Boolean | true |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## BP_ScoreManager Variables
|
## BP_ScoreManager Variables
|
||||||
|
|
||||||
| Variable Name | Type | Default Value |
|
| Variable Name | Type | Default Value |
|
||||||
|---------------|------|---------------|
|
| ---------------- | ---------- | ---------------- |
|
||||||
| `Score` | Integer | 0 |
|
| `Score` | Integer | 0 |
|
||||||
| `CurrentLives` | Integer | 3 |
|
| `CurrentLives` | Integer | 3 |
|
||||||
| `HUDWidget` | Object Ref | (set at runtime) |
|
| `HUDWidget` | Object Ref | (set at runtime) |
|
||||||
| `HUDWidgetClass` | Class Ref | WBP_HUD |
|
| `HUDWidgetClass` | Class Ref | WBP_HUD |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -143,25 +143,31 @@
|
|||||||
## General Debugging Tips
|
## General Debugging Tips
|
||||||
|
|
||||||
### Enable Print Statements
|
### Enable Print Statements
|
||||||
|
|
||||||
```
|
```
|
||||||
Right-click → Print String → Type message
|
Right-click → Print String → Type message
|
||||||
```
|
```
|
||||||
|
|
||||||
Add these throughout your blueprints to track execution flow.
|
Add these throughout your blueprints to track execution flow.
|
||||||
|
|
||||||
### Check Output Log
|
### Check Output Log
|
||||||
|
|
||||||
- **Window → Developer Tools → Output Log**
|
- **Window → Developer Tools → Output Log**
|
||||||
- Shows blueprint errors, warnings, and print statements
|
- Shows blueprint errors, warnings, and print statements
|
||||||
|
|
||||||
### Use Breakpoints
|
### Use Breakpoints
|
||||||
|
|
||||||
- Right-click any blueprint node → **Add Breakpoint**
|
- Right-click any blueprint node → **Add Breakpoint**
|
||||||
- Execution pauses at that point during Play mode
|
- Execution pauses at that point during Play mode
|
||||||
- Step through with F10/F11
|
- Step through with F10/F11
|
||||||
|
|
||||||
### Visualize Collisions
|
### Visualize Collisions
|
||||||
|
|
||||||
- In viewport: **Show → Collision**
|
- In viewport: **Show → Collision**
|
||||||
- Shows collision shapes in play mode
|
- Shows collision shapes in play mode
|
||||||
|
|
||||||
### Console Commands
|
### Console Commands
|
||||||
|
|
||||||
- Press `~` (tilde) to open console
|
- Press `~` (tilde) to open console
|
||||||
- `stat fps` - Show frame rate
|
- `stat fps` - Show frame rate
|
||||||
- `show collision` - Toggle collision visualization
|
- `show collision` - Toggle collision visualization
|
||||||
|
|||||||
@ -6,11 +6,12 @@
|
|||||||
|
|
||||||
## Scale Conversion
|
## Scale Conversion
|
||||||
|
|
||||||
| Unity | Unreal | Notes |
|
| Unity | Unreal | Notes |
|
||||||
|-------|--------|-------|
|
| ---------------- | --------------------- | ---------------------------- |
|
||||||
| 1 unit = 1 meter | 1 unit = 1 centimeter | Multiply Unity values by 100 |
|
| 1 unit = 1 meter | 1 unit = 1 centimeter | Multiply Unity values by 100 |
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
- Unity speed: `7.5` → Unreal speed: `750`
|
- Unity speed: `7.5` → Unreal speed: `750`
|
||||||
- Unity position: `(5, 10, 0)` → Unreal position: `(500, 1000, 0)`
|
- Unity position: `(5, 10, 0)` → Unreal position: `(500, 1000, 0)`
|
||||||
|
|
||||||
@ -18,13 +19,14 @@
|
|||||||
|
|
||||||
## Coordinate System
|
## Coordinate System
|
||||||
|
|
||||||
| Axis | Unity | Unreal |
|
| Axis | Unity | Unreal |
|
||||||
|------|-------|--------|
|
| ------- | ----- | ------ |
|
||||||
| Up | Y | Z |
|
| Up | Y | Z |
|
||||||
| Forward | Z | X |
|
| Forward | Z | X |
|
||||||
| Right | X | Y |
|
| Right | X | Y |
|
||||||
|
|
||||||
**For 2D top-down games:**
|
**For 2D top-down games:**
|
||||||
|
|
||||||
- Both use X for horizontal
|
- Both use X for horizontal
|
||||||
- Y/Z swap for vertical
|
- Y/Z swap for vertical
|
||||||
|
|
||||||
@ -35,12 +37,14 @@
|
|||||||
### Input
|
### Input
|
||||||
|
|
||||||
**Unity:**
|
**Unity:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
float h = Input.GetAxisRaw("Horizontal");
|
float h = Input.GetAxisRaw("Horizontal");
|
||||||
float v = Input.GetAxisRaw("Vertical");
|
float v = Input.GetAxisRaw("Vertical");
|
||||||
```
|
```
|
||||||
|
|
||||||
**Unreal (Enhanced Input):**
|
**Unreal (Enhanced Input):**
|
||||||
|
|
||||||
1. Create Input Action (IA_Move) with Axis2D type
|
1. Create Input Action (IA_Move) with Axis2D type
|
||||||
2. Create Input Mapping Context with key bindings
|
2. Create Input Mapping Context with key bindings
|
||||||
3. Add Mapping Context in BeginPlay
|
3. Add Mapping Context in BeginPlay
|
||||||
@ -51,11 +55,13 @@ float v = Input.GetAxisRaw("Vertical");
|
|||||||
### Instantiate / Spawn
|
### Instantiate / Spawn
|
||||||
|
|
||||||
**Unity:**
|
**Unity:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
Instantiate(prefab, position, rotation);
|
Instantiate(prefab, position, rotation);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Unreal:**
|
**Unreal:**
|
||||||
|
|
||||||
```
|
```
|
||||||
Spawn Actor from Class
|
Spawn Actor from Class
|
||||||
├── Class: YourBlueprintClass
|
├── Class: YourBlueprintClass
|
||||||
@ -68,11 +74,13 @@ Spawn Actor from Class
|
|||||||
### Destroy
|
### Destroy
|
||||||
|
|
||||||
**Unity:**
|
**Unity:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
Destroy(gameObject);
|
Destroy(gameObject);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Unreal:**
|
**Unreal:**
|
||||||
|
|
||||||
```
|
```
|
||||||
Destroy Actor
|
Destroy Actor
|
||||||
└── Target: Self (or reference)
|
└── Target: Self (or reference)
|
||||||
@ -83,11 +91,13 @@ Destroy Actor
|
|||||||
### Delta Time
|
### Delta Time
|
||||||
|
|
||||||
**Unity:**
|
**Unity:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
Time.deltaTime
|
Time.deltaTime
|
||||||
```
|
```
|
||||||
|
|
||||||
**Unreal:**
|
**Unreal:**
|
||||||
|
|
||||||
```
|
```
|
||||||
Get World Delta Seconds
|
Get World Delta Seconds
|
||||||
```
|
```
|
||||||
@ -97,12 +107,14 @@ Get World Delta Seconds
|
|||||||
### Find Objects
|
### Find Objects
|
||||||
|
|
||||||
**Unity:**
|
**Unity:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
FindObjectOfType<PlayerController>();
|
FindObjectOfType<PlayerController>();
|
||||||
FindObjectsOfType<Enemy>();
|
FindObjectsOfType<Enemy>();
|
||||||
```
|
```
|
||||||
|
|
||||||
**Unreal:**
|
**Unreal:**
|
||||||
|
|
||||||
```
|
```
|
||||||
Get All Actors of Class
|
Get All Actors of Class
|
||||||
├── Actor Class: BP_Player
|
├── Actor Class: BP_Player
|
||||||
@ -116,6 +128,7 @@ Then: Get (a ref) → index 0
|
|||||||
### Singleton Pattern
|
### Singleton Pattern
|
||||||
|
|
||||||
**Unity:**
|
**Unity:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public static GameManager Instance { get; private set; }
|
public static GameManager Instance { get; private set; }
|
||||||
|
|
||||||
@ -125,6 +138,7 @@ void Awake() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Unreal Options:**
|
**Unreal Options:**
|
||||||
|
|
||||||
1. **Game Instance** - Persists across levels
|
1. **Game Instance** - Persists across levels
|
||||||
2. **Subsystem** - Engine-managed singleton
|
2. **Subsystem** - Engine-managed singleton
|
||||||
3. **Get All Actors of Class** - Find at runtime
|
3. **Get All Actors of Class** - Find at runtime
|
||||||
@ -134,6 +148,7 @@ void Awake() {
|
|||||||
### Coroutines vs Timers
|
### Coroutines vs Timers
|
||||||
|
|
||||||
**Unity:**
|
**Unity:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
StartCoroutine(DelayedAction());
|
StartCoroutine(DelayedAction());
|
||||||
|
|
||||||
@ -144,6 +159,7 @@ IEnumerator DelayedAction() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Unreal:**
|
**Unreal:**
|
||||||
|
|
||||||
```
|
```
|
||||||
Set Timer by Function Name
|
Set Timer by Function Name
|
||||||
├── Function Name: "DelayedAction"
|
├── Function Name: "DelayedAction"
|
||||||
@ -158,10 +174,12 @@ Or use **Delay** node in blueprints.
|
|||||||
### Physics Layers vs Collision Channels
|
### Physics Layers vs Collision Channels
|
||||||
|
|
||||||
**Unity:**
|
**Unity:**
|
||||||
|
|
||||||
- Layer-based collision matrix
|
- Layer-based collision matrix
|
||||||
- `Physics.IgnoreLayerCollision()`
|
- `Physics.IgnoreLayerCollision()`
|
||||||
|
|
||||||
**Unreal:**
|
**Unreal:**
|
||||||
|
|
||||||
- Collision Channels (Object Types)
|
- Collision Channels (Object Types)
|
||||||
- Collision Presets
|
- Collision Presets
|
||||||
- Per-component collision settings
|
- Per-component collision settings
|
||||||
@ -171,11 +189,13 @@ Or use **Delay** node in blueprints.
|
|||||||
### Tags
|
### Tags
|
||||||
|
|
||||||
**Unity:**
|
**Unity:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
if (other.CompareTag("Enemy")) { }
|
if (other.CompareTag("Enemy")) { }
|
||||||
```
|
```
|
||||||
|
|
||||||
**Unreal:**
|
**Unreal:**
|
||||||
|
|
||||||
```
|
```
|
||||||
Actor Has Tag
|
Actor Has Tag
|
||||||
├── Target: Other Actor
|
├── Target: Other Actor
|
||||||
@ -188,11 +208,11 @@ Or use **Cast To** for type checking (preferred).
|
|||||||
|
|
||||||
### Vector Math
|
### Vector Math
|
||||||
|
|
||||||
| Operation | Unity | Unreal |
|
| Operation | Unity | Unreal |
|
||||||
|-----------|-------|--------|
|
| ------------- | --------------------- | -------------------- |
|
||||||
| Normalize | `vector.normalized` | `Normalize` node |
|
| Normalize | `vector.normalized` | `Normalize` node |
|
||||||
| Magnitude | `vector.magnitude` | `Vector Length` node |
|
| Magnitude | `vector.magnitude` | `Vector Length` node |
|
||||||
| Dot Product | `Vector3.Dot(a, b)` | `Dot Product` node |
|
| Dot Product | `Vector3.Dot(a, b)` | `Dot Product` node |
|
||||||
| Cross Product | `Vector3.Cross(a, b)` | `Cross Product` node |
|
| Cross Product | `Vector3.Cross(a, b)` | `Cross Product` node |
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -200,12 +220,14 @@ Or use **Cast To** for type checking (preferred).
|
|||||||
### Random
|
### Random
|
||||||
|
|
||||||
**Unity:**
|
**Unity:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
Random.Range(0f, 1f);
|
Random.Range(0f, 1f);
|
||||||
Random.Range(0, 10); // int, exclusive max
|
Random.Range(0, 10); // int, exclusive max
|
||||||
```
|
```
|
||||||
|
|
||||||
**Unreal:**
|
**Unreal:**
|
||||||
|
|
||||||
```
|
```
|
||||||
Random Float in Range
|
Random Float in Range
|
||||||
├── Min: 0.0
|
├── Min: 0.0
|
||||||
@ -223,12 +245,14 @@ Random Integer in Range
|
|||||||
### Debug
|
### Debug
|
||||||
|
|
||||||
**Unity:**
|
**Unity:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
Debug.Log("Message");
|
Debug.Log("Message");
|
||||||
Debug.DrawLine(start, end, Color.red);
|
Debug.DrawLine(start, end, Color.red);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Unreal:**
|
**Unreal:**
|
||||||
|
|
||||||
```
|
```
|
||||||
Print String
|
Print String
|
||||||
└── In String: "Message"
|
└── In String: "Message"
|
||||||
@ -243,16 +267,16 @@ Draw Debug Line
|
|||||||
|
|
||||||
## Quick Reference Table
|
## Quick Reference Table
|
||||||
|
|
||||||
| Concept | Unity | Unreal |
|
| Concept | Unity | Unreal |
|
||||||
|---------|-------|--------|
|
| --------- | ---------------- | --------------------- |
|
||||||
| Script | C# MonoBehaviour | Blueprint / C++ Actor |
|
| Script | C# MonoBehaviour | Blueprint / C++ Actor |
|
||||||
| Prefab | .prefab asset | Blueprint Class |
|
| Prefab | .prefab asset | Blueprint Class |
|
||||||
| Scene | .unity scene | Level (.umap) |
|
| Scene | .unity scene | Level (.umap) |
|
||||||
| Inspector | Inspector window | Details panel |
|
| Inspector | Inspector window | Details panel |
|
||||||
| Hierarchy | Hierarchy window | Outliner |
|
| Hierarchy | Hierarchy window | Outliner |
|
||||||
| Project | Project window | Content Browser |
|
| Project | Project window | Content Browser |
|
||||||
| Console | Console window | Output Log |
|
| Console | Console window | Output Log |
|
||||||
| Play | Play button | Play (Alt+P) |
|
| Play | Play button | Play (Alt+P) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
155
games/unreal/tutorial/lint-markdown.sh
Executable file
155
games/unreal/tutorial/lint-markdown.sh
Executable 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!"
|
||||||
@ -14,7 +14,6 @@
|
|||||||
5. In the "Unreal Project Browser" window that appears:
|
5. In the "Unreal Project Browser" window that appears:
|
||||||
- At the top, select "Games" category (should be selected by default)
|
- At the top, select "Games" category (should be selected by default)
|
||||||
- Click "Blank" template (empty square icon)
|
- Click "Blank" template (empty square icon)
|
||||||
|
|
||||||
6. On the right side panel, configure:
|
6. On the right side panel, configure:
|
||||||
- **Project Defaults:** Blueprint (not C++)
|
- **Project Defaults:** Blueprint (not C++)
|
||||||
- **Target Platform:** Desktop
|
- **Target Platform:** Desktop
|
||||||
@ -25,17 +24,18 @@
|
|||||||
7. At the bottom:
|
7. At the bottom:
|
||||||
- Choose folder location where you want to save
|
- Choose folder location where you want to save
|
||||||
- Name the project: `BulletHellGame`
|
- Name the project: `BulletHellGame`
|
||||||
|
|
||||||
8. Click "Create" button (bottom right, yellow)
|
8. Click "Create" button (bottom right, yellow)
|
||||||
|
|
||||||
### Expected Result
|
### Expected Result
|
||||||
|
|
||||||
Unreal Editor opens with an empty level. You should see:
|
Unreal Editor opens with an empty level. You should see:
|
||||||
|
|
||||||
- Main 3D viewport in the center
|
- Main 3D viewport in the center
|
||||||
- Outliner panel on the right (showing "Untitled" level)
|
- Outliner panel on the right (showing "Untitled" level)
|
||||||
- Details panel on the right side
|
- Details panel on the right side
|
||||||
|
|
||||||
> **NOTE:** The Content Drawer is NOT open by default. To open it:
|
> **NOTE:** The Content Drawer is NOT open by default. To open it:
|
||||||
|
>
|
||||||
> - Click "Content Drawer" button at the bottom of the screen, OR
|
> - Click "Content Drawer" button at the bottom of the screen, OR
|
||||||
> - Press `Ctrl+Space`, OR
|
> - Press `Ctrl+Space`, OR
|
||||||
> - Go to Window → Content Drawer
|
> - 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**
|
- 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)
|
- In the left sidebar, search for "Maps & Modes" (Project → 4th from the top, next to GameplayTags and Movies)
|
||||||
- Click on "Maps & Modes"
|
- Click on "Maps & Modes"
|
||||||
|
|
||||||
5. Under "Default Modes":
|
5. Under "Default Modes":
|
||||||
- Find "Default GameMode" dropdown
|
- Find "Default GameMode" dropdown
|
||||||
- We'll create our own later, leave it for now
|
- We'll create our own later, leave it for now
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
### Expected Result
|
### Expected Result
|
||||||
|
|
||||||
A new tab opens showing the Blueprint Editor with:
|
A new tab opens showing the Blueprint Editor with:
|
||||||
|
|
||||||
- Components panel on the left
|
- Components panel on the left
|
||||||
- Viewport in the center
|
- Viewport in the center
|
||||||
- Details panel on the right
|
- 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)
|
- Rename it to `PlayerSprite` (click on it, then press F2)
|
||||||
|
|
||||||
> **NOTE:** If Paper Sprite is not available:
|
> **NOTE:** If Paper Sprite is not available:
|
||||||
|
>
|
||||||
> - Go to Edit → Plugins (from main menu bar)
|
> - Go to Edit → Plugins (from main menu bar)
|
||||||
> - Search for "Paper2D"
|
> - Search for "Paper2D"
|
||||||
> - Make sure "Paper2D" plugin is ENABLED
|
> - Make sure "Paper2D" plugin is ENABLED
|
||||||
@ -81,7 +83,7 @@ A new tab opens showing the Blueprint Editor with:
|
|||||||
- This should already be ENABLED by default
|
- This should already be ENABLED by default
|
||||||
- If not, check/enable it - this tells Unreal to use THIS camera when playing
|
- If not, check/enable it - this tells Unreal to use THIS camera when playing
|
||||||
- This camera will follow the player, making testing easy
|
- This camera will follow the player, making testing easy
|
||||||
|
|
||||||
> **NOTE:** This temporary camera ensures you can see the player during testing.
|
> **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.
|
> In [Part 8](part-8-game-mode-level.md), we'll set up a proper fixed camera for the final game.
|
||||||
|
|
||||||
@ -104,7 +106,7 @@ DefaultSceneRoot
|
|||||||
- Find "Auto Possess Player" dropdown (currently set to "Disabled")
|
- Find "Auto Possess Player" dropdown (currently set to "Disabled")
|
||||||
- Change it to **"Player 0"**
|
- Change it to **"Player 0"**
|
||||||
- This makes the engine automatically possess this pawn when playing, which activates the PlayerCamera
|
- 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
|
> **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.
|
> in [Part 8](part-8-game-mode-level.md), which handles possession automatically.
|
||||||
|
|
||||||
@ -112,7 +114,7 @@ DefaultSceneRoot
|
|||||||
|
|
||||||
## Step 2.3: Create Player Variables
|
## 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)
|
1. In the Blueprint Editor, look at the left panel (below the Components panel)
|
||||||
2. Find "My Blueprint" section
|
2. Find "My Blueprint" section
|
||||||
@ -120,27 +122,27 @@ DefaultSceneRoot
|
|||||||
|
|
||||||
4. Create these variables one by one (click +, then set properties in Details):
|
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
|
> **NOTE:** Variables are NOT added to the Components hierarchy. They appear in a
|
||||||
> separate "Variables" list within the "My Blueprint" panel. Variables store
|
> separate "Variables" list within the "My Blueprint" panel. Variables store
|
||||||
> data values, while Components are physical/visual parts of the actor.
|
> data values, while Components are physical/visual parts of the actor.
|
||||||
|
|
||||||
| # | Variable Name | Type | Default Value |
|
| # | Variable Name | Type | Default Value |
|
||||||
|---|---------------|------|---------------|
|
| --- | -------------- | ----------------- | ----------------- |
|
||||||
| 1 | `MoveSpeed` | Float | 750.0 |
|
| 1 | `MoveSpeed` | Float | 750.0 |
|
||||||
| 2 | `BoundsMin` | Vector 2D | X=-850, Y=-450 |
|
| 2 | `BoundsMin` | Vector 2D | X=-850, Y=-450 |
|
||||||
| 3 | `BoundsMax` | Vector 2D | X=850, Y=450 |
|
| 3 | `BoundsMax` | Vector 2D | X=850, Y=450 |
|
||||||
| 4 | `FireInterval` | Float | 0.08 |
|
| 4 | `FireInterval` | Float | 0.08 |
|
||||||
| 5 | `FireTimer` | Float | 0.0 |
|
| 5 | `FireTimer` | Float | 0.0 |
|
||||||
| 6 | `BulletSpeed` | Float | 2200.0 |
|
| 6 | `BulletSpeed` | Float | 2200.0 |
|
||||||
| 7 | `MaxLives` | Integer | 3 |
|
| 7 | `MaxLives` | Integer | 3 |
|
||||||
| 8 | `CurrentLives` | Integer | 3 |
|
| 8 | `CurrentLives` | Integer | 3 |
|
||||||
| 9 | `VolleySize` | Integer | 3 |
|
| 9 | `VolleySize` | Integer | 3 |
|
||||||
| 10 | `VolleySpread` | Float | 12.0 |
|
| 10 | `VolleySpread` | Float | 12.0 |
|
||||||
| 11 | `SpecialUsed` | Boolean | false (unchecked) |
|
| 11 | `SpecialUsed` | Boolean | false (unchecked) |
|
||||||
| 12 | `BulletClass` | Class Reference* | (set later) |
|
| 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.
|
|
||||||
|
|
||||||
|
> \*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.
|
> **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)
|
5. Click **"Compile"** button (top left, blue checkmark icon)
|
||||||
@ -149,6 +151,7 @@ DefaultSceneRoot
|
|||||||
### Expected Result
|
### Expected Result
|
||||||
|
|
||||||
My Blueprint panel shows:
|
My Blueprint panel shows:
|
||||||
|
|
||||||
- All 12 variables you created listed under "Variables"
|
- 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
|
- A "Components" category with 4 component references (DefaultSceneRoot, PlayerSprite, PlayerCollision, Arrow) - these are automatically created from the components you added in Step 2.2
|
||||||
|
|
||||||
@ -200,38 +203,38 @@ Before creating any input assets, you **MUST** configure the project to use Enha
|
|||||||
2. Click **"+"** next to "Mappings" to add IA_Move:
|
2. Click **"+"** next to "Mappings" to add IA_Move:
|
||||||
- Select "IA_Move" from dropdown
|
- Select "IA_Move" from dropdown
|
||||||
- Click "+" under IA_Move to add key bindings:
|
- Click "+" under IA_Move to add key bindings:
|
||||||
|
|
||||||
> **NOTE:** In Unreal's top-down view, X axis = up/down, Y axis = left/right
|
> **NOTE:** In Unreal's top-down view, X axis = up/down, Y axis = left/right
|
||||||
|
|
||||||
**For W key (move UP on screen):**
|
**For W key (move UP on screen):**
|
||||||
- Click "+", select "W"
|
- Click "+", select "W"
|
||||||
- No modifiers needed (outputs X positive = up)
|
- No modifiers needed (outputs X positive = up)
|
||||||
- TOTAL MODIFIERS FOR W: **0**
|
- TOTAL MODIFIERS FOR W: **0**
|
||||||
|
|
||||||
**For S key (move DOWN on screen):**
|
**For S key (move DOWN on screen):**
|
||||||
- Click "+", select "S"
|
- Click "+", select "S"
|
||||||
- Click "+" next to "Modifiers" → Add "Negate"
|
- Click "+" next to "Modifiers" → Add "Negate"
|
||||||
- TOTAL MODIFIERS FOR S: **1** (Negate only)
|
- TOTAL MODIFIERS FOR S: **1** (Negate only)
|
||||||
|
|
||||||
**For A key (move LEFT on screen):**
|
**For A key (move LEFT on screen):**
|
||||||
- Click "+", select "A"
|
- Click "+", select "A"
|
||||||
- Click "+" next to "Modifiers" → Add "Swizzle Input Axis Values" → Order: "YXZ"
|
- Click "+" next to "Modifiers" → Add "Swizzle Input Axis Values" → Order: "YXZ"
|
||||||
- Click "+" next to "Modifiers" AGAIN → Add "Negate"
|
- Click "+" next to "Modifiers" AGAIN → Add "Negate"
|
||||||
- TOTAL MODIFIERS FOR A: **2** (Swizzle AND Negate)
|
- TOTAL MODIFIERS FOR A: **2** (Swizzle AND Negate)
|
||||||
|
|
||||||
**For D key (move RIGHT on screen):**
|
**For D key (move RIGHT on screen):**
|
||||||
- Click "+", select "D"
|
- Click "+", select "D"
|
||||||
- Click "+" next to "Modifiers" → Add "Swizzle Input Axis Values" → Order: "YXZ"
|
- Click "+" next to "Modifiers" → Add "Swizzle Input Axis Values" → Order: "YXZ"
|
||||||
- TOTAL MODIFIERS FOR D: **1** (Swizzle only)
|
- TOTAL MODIFIERS FOR D: **1** (Swizzle only)
|
||||||
|
|
||||||
**Summary Table - verify your IMC_Default matches this:**
|
**Summary Table - verify your IMC_Default matches this:**
|
||||||
|
|
||||||
| Key | Modifiers | Output Vector |
|
| Key | Modifiers | Output Vector |
|
||||||
|-----|-----------|---------------|
|
| --- | ---------------------------------------- | -------------- |
|
||||||
| W | (none) | (1, 0) = UP |
|
| W | (none) | (1, 0) = UP |
|
||||||
| S | Negate | (-1, 0) = DOWN |
|
| S | Negate | (-1, 0) = DOWN |
|
||||||
| A | Swizzle Input Axis Values (YXZ) + Negate | (0, -1) = LEFT |
|
| A | Swizzle Input Axis Values (YXZ) + Negate | (0, -1) = LEFT |
|
||||||
| D | Swizzle Input Axis Values (YXZ) | (0, 1) = RIGHT |
|
| D | Swizzle Input Axis Values (YXZ) | (0, 1) = RIGHT |
|
||||||
|
|
||||||
After adding all keys, press `Ctrl+S` to SAVE IMC_Default!
|
After adding all keys, press `Ctrl+S` to SAVE IMC_Default!
|
||||||
|
|
||||||
@ -254,8 +257,9 @@ 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)
|
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):
|
You should see three default events (red nodes):
|
||||||
|
|
||||||
- Event BeginPlay
|
- Event BeginPlay
|
||||||
- Event ActorBeginOverlap
|
- Event ActorBeginOverlap
|
||||||
- Event Tick
|
- Event Tick
|
||||||
|
|
||||||
### 3. ADD MAPPING CONTEXT IN BEGINPLAY
|
### 3. ADD MAPPING CONTEXT IN BEGINPLAY
|
||||||
@ -267,7 +271,7 @@ From "Event BeginPlay" node:
|
|||||||
- It has a "Player Index" input (default 0) and "Return Value" output
|
- 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`
|
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`
|
3. From that blue output, drag and search `Add Mapping Context`
|
||||||
4. For "Mapping Context" input:
|
4. For "Mapping Context" input:
|
||||||
- Click dropdown and select `IMC_Default`
|
- Click dropdown and select `IMC_Default`
|
||||||
- Or drag IMC_Default from Content Drawer
|
- Or drag IMC_Default from Content Drawer
|
||||||
5. Set "Priority" to `0`
|
5. Set "Priority" to `0`
|
||||||
@ -276,11 +280,10 @@ From "Event BeginPlay" node:
|
|||||||
- The WHITE TRIANGLE pins are different from the BLUE CIRCLE pins!
|
- The WHITE TRIANGLE pins are different from the BLUE CIRCLE pins!
|
||||||
- Blue circles = DATA (what values to use)
|
- Blue circles = DATA (what values to use)
|
||||||
- White triangles = EXECUTION (when to run)
|
- White triangles = EXECUTION (when to run)
|
||||||
|
|
||||||
- On "Event BeginPlay", find the WHITE TRIANGLE on the RIGHT side
|
- On "Event BeginPlay", find the WHITE TRIANGLE on the RIGHT side
|
||||||
- On "Add Mapping Context", find the WHITE TRIANGLE on the LEFT 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
|
- Click and DRAG from BeginPlay's white triangle to Add Mapping Context's white triangle
|
||||||
|
|
||||||
**WITHOUT THIS WHITE WIRE, THE CODE NEVER RUNS!**
|
**WITHOUT THIS WHITE WIRE, THE CODE NEVER RUNS!**
|
||||||
|
|
||||||
Your graph should look like this:
|
Your graph should look like this:
|
||||||
@ -303,6 +306,7 @@ Your graph should look like this:
|
|||||||
```
|
```
|
||||||
|
|
||||||
**VERIFY:** You must have BOTH:
|
**VERIFY:** You must have BOTH:
|
||||||
|
|
||||||
- Blue data wires connecting the nodes (passes the subsystem reference)
|
- Blue data wires connecting the nodes (passes the subsystem reference)
|
||||||
- White execution wire from Event BeginPlay to Add Mapping Context (makes it run)
|
- 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.
|
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 "+"
|
- In My Blueprint panel → Variables → click "+"
|
||||||
- Name: `DEBUG_LastMoveInput`
|
- Name: `DEBUG_LastMoveInput`
|
||||||
- Type: Vector 2D
|
- Type: Vector 2D
|
||||||
- Default Value: (0, 0)
|
- Default Value: (0, 0)
|
||||||
- Compile to save
|
- 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
|
- Right-click in empty space → search `Custom Event` → add it
|
||||||
- Name it `DEBUG_PrintInput`
|
- Name it `DEBUG_PrintInput`
|
||||||
- This keeps debug logic completely isolated
|
- 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:**
|
1. **Get the current input value:**
|
||||||
- Right-click → "Get Player Controller"
|
- 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
|
- "Set DEBUG_LastMoveInput" = IA_Move value
|
||||||
- Then → "Print String" with 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`
|
- From "Event Tick" WHITE pin → search `DEBUG_PrintInput`
|
||||||
- This adds a node that calls your custom event
|
- This adds a node that calls your custom event
|
||||||
|
|
||||||
@ -374,18 +381,22 @@ AREA 2 - Debug logic (completely separate):
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### e) Compile and Save
|
#### e) Compile and Save
|
||||||
|
|
||||||
#### f) Drag BP_Player into level, Press Play (`Alt+P`)
|
#### 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
|
#### h) Press WASD keys and look at top-left of screen
|
||||||
|
|
||||||
**Expected:** Message appears ONLY when you press or release a key:
|
**Expected:** Message appears ONLY when you press or release a key:
|
||||||
|
|
||||||
- Press W: prints "X=1.0 Y=0.0"
|
- Press W: prints "X=1.0 Y=0.0"
|
||||||
- Release W: prints "X=0.0 Y=0.0"
|
- Release W: prints "X=0.0 Y=0.0"
|
||||||
- Log is NOT spammed every frame
|
- 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.
|
**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>
|
<details>
|
||||||
<summary><b>DEBUGGING STEP 1</b> - Verify the pawn is possessed</summary>
|
<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
|
- Find BP_Player in the list
|
||||||
- If it shows a small controller icon next to it, it's possessed
|
- 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
|
- If NOT possessed: Check that "Auto Possess Player" = "Player 0" in BP_Player
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -403,6 +415,7 @@ AREA 2 - Debug logic (completely separate):
|
|||||||
- The viewport must have focus to receive keyboard input
|
- The viewport must have focus to receive keyboard input
|
||||||
- Try pressing `Shift+F1` to release mouse, then click viewport again
|
- Try pressing `Shift+F1` to release mouse, then click viewport again
|
||||||
- Then press WASD again
|
- Then press WASD again
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -413,6 +426,7 @@ AREA 2 - Debug logic (completely separate):
|
|||||||
- Type "Tick is running" in the In String field (just plain text)
|
- 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
|
- 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
|
- If you DON'T see this: The blueprint isn't running at all
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -423,6 +437,7 @@ AREA 2 - Debug logic (completely separate):
|
|||||||
- Connect the white wire: BeginPlay → Add Mapping Context → Print String
|
- Connect the white wire: BeginPlay → Add Mapping Context → Print String
|
||||||
- Play the game - you should see "BeginPlay executed" once at start
|
- Play the game - you should see "BeginPlay executed" once at start
|
||||||
- If you DON'T see this: BeginPlay isn't running (pawn not spawned?)
|
- If you DON'T see this: BeginPlay isn't running (pawn not spawned?)
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -432,6 +447,7 @@ AREA 2 - Debug logic (completely separate):
|
|||||||
- Delete ALL of them
|
- Delete ALL of them
|
||||||
- Drag in exactly ONE fresh BP_Player from Content Drawer
|
- Drag in exactly ONE fresh BP_Player from Content Drawer
|
||||||
- Try again
|
- Try again
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -443,6 +459,7 @@ AREA 2 - Debug logic (completely separate):
|
|||||||
- In Event Graph: Event Tick → Print String (connected to Get IA_Move)
|
- In Event Graph: Event Tick → Print String (connected to Get IA_Move)
|
||||||
- Drag this minimal test blueprint into level
|
- Drag this minimal test blueprint into level
|
||||||
- If THIS works, something is wrong with your BP_Player
|
- If THIS works, something is wrong with your BP_Player
|
||||||
|
|
||||||
</details>
|
</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.
|
**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).
|
> **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
|
- Delete the wire from Event Tick to DEBUG_PrintInput
|
||||||
- Right-click → search `Sequence` → add it
|
- Right-click → search `Sequence` → add it
|
||||||
- Connect Event Tick → Sequence (input)
|
- 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.
|
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 using Sequence: drag from "Then 1" output
|
||||||
- If no debug: drag from Event Tick directly
|
- If no debug: drag from Event Tick directly
|
||||||
|
|
||||||
#### c) Right-click → search `Get Player Controller` and add it
|
#### c) Right-click → search `Get Player Controller` and add it
|
||||||
|
|
||||||
(This is a NEW Get Player Controller - separate from the one in debug)
|
(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`
|
#### d) From Player Controller output, drag → `Get Enhanced Input Local Player Subsystem`
|
||||||
|
|
||||||
#### e) From subsystem output, drag and right-click → search `IA_Move`
|
#### e) From subsystem output, drag and right-click → search `IA_Move`
|
||||||
|
|
||||||
- Look under "Input" → "Enhanced Action Values" → select "IA_Move"
|
- Look under "Input" → "Enhanced Action Values" → select "IA_Move"
|
||||||
- This node returns the current value of the IA_Move input action
|
- 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,
|
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
|
OR drag from output and search "To Vector 2D" to convert it
|
||||||
|
|
||||||
#### g) Right-click → `Make Vector`
|
#### g) Right-click → `Make Vector`
|
||||||
|
|
||||||
- Connect the X from movement input to X
|
- Connect the X from movement input to X
|
||||||
- Connect the Y from movement input to Y
|
- Connect the Y from movement input to Y
|
||||||
- Set Z to 0
|
- Set Z to 0
|
||||||
|
|
||||||
#### h) Right-click → `Normalize`
|
#### h) Right-click → `Normalize`
|
||||||
|
|
||||||
- Connect the vector output to Normalize input
|
- Connect the vector output to Normalize input
|
||||||
|
|
||||||
#### i) Right-click → `Get World Delta Seconds`
|
#### i) Right-click → `Get World Delta Seconds`
|
||||||
|
|
||||||
#### j) Right-click → `Get MoveSpeed` (your variable)
|
#### 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 Delta Seconds output to first input
|
||||||
- Connect MoveSpeed output to second 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 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
|
- This output is the "movement delta" - how far to move this frame
|
||||||
|
|
||||||
#### m) Right-click → `Get Actor Location`
|
#### m) Right-click → `Get Actor Location`
|
||||||
|
|
||||||
#### n) Right-click → Add (vector + vector)
|
#### n) Right-click → Add (vector + vector)
|
||||||
|
|
||||||
- Connect current location (from step m) to first input
|
- Connect current location (from step m) to first input
|
||||||
- Connect movement delta (from step l) to second 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:
|
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"
|
- 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
|
- This splits the vector into three separate pins: X, Y, Z
|
||||||
|
|
||||||
Now clamp the X value:
|
Now clamp the X value:
|
||||||
|
|
||||||
- Right-click in empty space → search `Clamp (float)` → add it
|
- Right-click in empty space → search `Clamp (float)` → add it
|
||||||
- Connect the "X" output (from the split Add node) to "Value" input of Clamp
|
- Connect the "X" output (from the split Add node) to "Value" input of Clamp
|
||||||
|
|
||||||
Get the min bound:
|
Get the min bound:
|
||||||
|
|
||||||
- Right-click → search `Get BoundsMin` (your variable) → add it
|
- Right-click → search `Get BoundsMin` (your variable) → add it
|
||||||
- Right-click on BoundsMin output pin → "Split Struct Pin" (splits into X, Y)
|
- Right-click on BoundsMin output pin → "Split Struct Pin" (splits into X, Y)
|
||||||
- Connect BoundsMin's "X" to Clamp's "Min" input
|
- Connect BoundsMin's "X" to Clamp's "Min" input
|
||||||
|
|
||||||
Get the max bound:
|
Get the max bound:
|
||||||
|
|
||||||
- Right-click → search `Get BoundsMax` (your variable) → add it
|
- Right-click → search `Get BoundsMax` (your variable) → add it
|
||||||
- Right-click on BoundsMax output pin → "Split Struct Pin"
|
- Right-click on BoundsMax output pin → "Split Struct Pin"
|
||||||
- Connect BoundsMax's "X" to Clamp's "Max" input
|
- Connect BoundsMax's "X" to Clamp's "Max" input
|
||||||
|
|
||||||
The Clamp node now outputs the X position clamped within bounds.
|
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
|
- Right-click → add another `Clamp (float)` node
|
||||||
- Connect the "Y" output (from the split Add node in step o) to "Value"
|
- Connect the "Y" output (from the split Add node in step o) to "Value"
|
||||||
- Connect BoundsMin's "Y" (already split) to "Min"
|
- Connect BoundsMin's "Y" (already split) to "Min"
|
||||||
- Connect BoundsMax's "Y" (already split) to "Max"
|
- Connect BoundsMax's "Y" (already split) to "Max"
|
||||||
|
|
||||||
#### q) Right-click → `Make Vector`
|
#### q) Right-click → `Make Vector`
|
||||||
|
|
||||||
- Connect clamped X
|
- Connect clamped X
|
||||||
- Connect clamped Y
|
- Connect clamped Y
|
||||||
- Set Z to 0
|
- Set Z to 0
|
||||||
|
|
||||||
#### r) Right-click → `Set Actor Location`
|
#### r) Right-click → `Set Actor Location`
|
||||||
|
|
||||||
- Connect the clamped vector to "New 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 using Sequence (from step a): drag from "Then 1" to Set Actor Location
|
||||||
- If no debug: drag from Event Tick to Set Actor Location
|
- If no debug: drag from Event Tick to Set Actor Location
|
||||||
- Without this execution wire, the movement code NEVER runs!
|
- Without this execution wire, the movement code NEVER runs!
|
||||||
|
|
||||||
**Final Structure (with debug - using Sequence node):**
|
**Final Structure (with debug - using Sequence node):**
|
||||||
|
|
||||||
```
|
```
|
||||||
Event Tick → Sequence ─┬─ Then 0 → DEBUG_PrintInput
|
Event Tick → Sequence ─┬─ Then 0 → DEBUG_PrintInput
|
||||||
│
|
│
|
||||||
@ -561,6 +597,7 @@ Event Tick → Sequence ─┬─ Then 0 → DEBUG_PrintInput
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Final Structure (without debug):**
|
**Final Structure (without debug):**
|
||||||
|
|
||||||
```
|
```
|
||||||
Event Tick → [movement logic] → Set Actor Location
|
Event Tick → [movement logic] → Set Actor Location
|
||||||
```
|
```
|
||||||
@ -569,18 +606,21 @@ Event Tick → [movement logic] → Set Actor Location
|
|||||||
|
|
||||||
### 6. Click Compile and Save
|
### 6. Click Compile and Save
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark (no errors)
|
- Compile button shows GREEN checkmark (no errors)
|
||||||
- No warnings about unconnected pins
|
- 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)
|
1. Open any level (or the default "Untitled" level)
|
||||||
2. From Content Drawer, drag BP_Player into the viewport
|
2. From Content Drawer, drag BP_Player into the viewport
|
||||||
3. Press Play (`Alt+P`)
|
3. Press Play (`Alt+P`)
|
||||||
4. Click inside the game viewport to give it keyboard focus
|
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
|
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
|
- Player pawn moves smoothly when pressing WASD keys
|
||||||
- Movement is frame-rate independent (consistent speed)
|
- Movement is frame-rate independent (consistent speed)
|
||||||
- Player cannot move outside the screen bounds (stops at edges)
|
- 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).
|
> **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
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────┐ ┌──────────┐
|
┌─────────────┐ ┌──────────┐
|
||||||
@ -597,7 +637,7 @@ Event Tick → [movement logic] → Set Actor Location
|
|||||||
└─ Then 1 ──► Movement logic ──► Set Actor Location
|
└─ Then 1 ──► Movement logic ──► Set Actor Location
|
||||||
|
|
||||||
(If no debug, skip Sequence and connect Event Tick directly to movement)
|
(If no debug, skip Sequence and connect Event Tick directly to movement)
|
||||||
|
|
||||||
┌──────────────────────────────────┐ ┌────────────────────┐
|
┌──────────────────────────────────┐ ┌────────────────────┐
|
||||||
│ Get Enhanced Input Subsystem │ │ Set Actor Location │
|
│ Get Enhanced Input Subsystem │ │ Set Actor Location │
|
||||||
│ → Get Action Value (IA_Move) │ └────────────────────┘
|
│ → Get Action Value (IA_Move) │ └────────────────────┘
|
||||||
@ -624,18 +664,20 @@ Event Tick → [movement logic] → Set Actor Location
|
|||||||
|
|
||||||
2. After the movement logic, add firing check using Enhanced Input:
|
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)
|
- Look under "Input" → "Enhanced Action Events" category (diamond ◇ icons)
|
||||||
- Select "IA_Fire" (the one with diamond icon, NOT the square icon)
|
- 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
|
- This creates an EVENT node (red, like Event Tick) that fires when the button is pressed
|
||||||
|
|
||||||
> **NOTE:**
|
> **NOTE:**
|
||||||
|
>
|
||||||
> - "Enhanced Action Events" (diamond ◇) = triggers when button pressed
|
> - "Enhanced Action Events" (diamond ◇) = triggers when button pressed
|
||||||
> - "Enhanced Action Values" (square □) = reads current value continuously
|
> - "Enhanced Action Values" (square □) = reads current value continuously
|
||||||
>
|
>
|
||||||
> For firing, we want the EVENT.
|
> 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:**
|
1. **Get current timer value:**
|
||||||
- Right-click → `Get FireTimer` (your variable)
|
- 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
|
### 4. Compile and Save
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- Fire rate limiter logic works (Print String spams when holding Z)
|
- 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
|
- 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.
|
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
|
1. In My Blueprint panel (left side), find "Functions" section
|
||||||
2. Click the **"+"** button next to "Functions"
|
2. Click the **"+"** button next to "Functions"
|
||||||
3. Name it `HandleDeath`
|
3. Name it `HandleDeath`
|
||||||
4. Double-click to open the function graph
|
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
|
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
|
- Position it near the end of the function
|
||||||
|
|
||||||
4. **Connect execution:**
|
4. **Connect execution:**
|
||||||
|
|
||||||
```
|
```
|
||||||
HandleDeath (entry) ──► Set Actor Hidden in Game
|
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.
|
Now we can create TakeHit, which calls HandleDeath.
|
||||||
|
|
||||||
#### a) Create the function:
|
#### a) Create the function
|
||||||
|
|
||||||
1. In My Blueprint panel → Functions → click **"+"**
|
1. In My Blueprint panel → Functions → click **"+"**
|
||||||
2. Name it `TakeHit`
|
2. Name it `TakeHit`
|
||||||
3. Double-click to open the function graph
|
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)
|
1. With the function graph open, look at the Details panel (right side)
|
||||||
2. Find "Inputs" section
|
2. Find "Inputs" section
|
||||||
3. Click **"+"** to add a new input
|
3. Click **"+"** to add a new input
|
||||||
@ -772,19 +819,22 @@ Now we can create TakeHit, which calls HandleDeath.
|
|||||||
5. Type: `Integer`
|
5. Type: `Integer`
|
||||||
6. The purple entry node now shows a "Damage" output pin
|
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:**
|
**Step 1 - Get current lives:**
|
||||||
|
|
||||||
- Right-click in empty space → search `Get CurrentLives` → add it
|
- Right-click in empty space → search `Get CurrentLives` → add it
|
||||||
- This reads the CurrentLives variable value
|
- This reads the CurrentLives variable value
|
||||||
|
|
||||||
**Step 2 - Subtract damage from lives:**
|
**Step 2 - Subtract damage from lives:**
|
||||||
|
|
||||||
- Right-click → search `integer - integer` or `Subtract (int)` → add it
|
- Right-click → search `integer - integer` or `Subtract (int)` → add it
|
||||||
- Connect `CurrentLives` (from Step 1) to the TOP input (A)
|
- Connect `CurrentLives` (from Step 1) to the TOP input (A)
|
||||||
- Connect `Damage` (from the purple entry node) to the BOTTOM input (B)
|
- Connect `Damage` (from the purple entry node) to the BOTTOM input (B)
|
||||||
- Result = CurrentLives minus Damage
|
- Result = CurrentLives minus Damage
|
||||||
|
|
||||||
**Step 3 - Clamp the result (prevent negative lives):**
|
**Step 3 - Clamp the result (prevent negative lives):**
|
||||||
|
|
||||||
- Right-click → search `Clamp (int)` → add the "Clamp (integer)" node
|
- Right-click → search `Clamp (int)` → add the "Clamp (integer)" node
|
||||||
- Connect the Subtract result (from Step 2) to the "Value" input
|
- Connect the Subtract result (from Step 2) to the "Value" input
|
||||||
- Set "Min" to `0` (type directly in the field)
|
- 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)
|
- The output is the clamped value (0 or higher)
|
||||||
|
|
||||||
**Step 4 - Store the new lives value:**
|
**Step 4 - Store the new lives value:**
|
||||||
|
|
||||||
- Right-click → search `Set CurrentLives` → add it
|
- Right-click → search `Set CurrentLives` → add it
|
||||||
- Connect the Clamp output (from Step 3) to the input
|
- Connect the Clamp output (from Step 3) to the input
|
||||||
- **IMPORTANT:** Connect execution wire from entry node → Set CurrentLives
|
- **IMPORTANT:** Connect execution wire from entry node → Set CurrentLives
|
||||||
|
|
||||||
**Step 5 - Check if player should die:**
|
**Step 5 - Check if player should die:**
|
||||||
|
|
||||||
- From `Set CurrentLives`, drag execution → search `Branch` → add it
|
- From `Set CurrentLives`, drag execution → search `Branch` → add it
|
||||||
- Right-click → search `<= (integer)` (less than or equal) → 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
|
- 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
|
- Connect the `<=` result (boolean) to Branch's "Condition" input
|
||||||
|
|
||||||
**Step 6 - On TRUE (player is dead):**
|
**Step 6 - On TRUE (player is dead):**
|
||||||
|
|
||||||
- From Branch's "True" execution pin, drag → search `HandleDeath` → add it
|
- From Branch's "True" execution pin, drag → search `HandleDeath` → add it
|
||||||
- This calls the function we created in step 1
|
- This calls the function we created in step 1
|
||||||
|
|
||||||
**Step 7 - On FALSE (player still alive):**
|
**Step 7 - On FALSE (player still alive):**
|
||||||
|
|
||||||
- Leave the "False" pin unconnected (do nothing, player survives)
|
- 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:**
|
> **IMPORTANT - Dependency Note:**
|
||||||
> The special ability needs to destroy BP_Enemy and BP_Bullet actors, which don't exist yet.
|
> 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):
|
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)
|
1. Go back to the **Event Graph** tab (not inside a function)
|
||||||
2. Right-click in empty space → search `EnhancedInputAction IA_Special`
|
2. Right-click in empty space → search `EnhancedInputAction IA_Special`
|
||||||
- Look under "Input" → "Enhanced Action Events" category
|
- Look under "Input" → "Enhanced Action Events" category
|
||||||
- Select "IA_Special" (the EVENT with diamond ◇ icon)
|
- Select "IA_Special" (the EVENT with diamond ◇ icon)
|
||||||
3. This creates a red event node that fires when X or Right Mouse is pressed
|
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
|
1. From the "Triggered" execution pin, drag → search `Branch` → add it
|
||||||
2. Right-click → search `Get SpecialUsed` → add it
|
2. Right-click → search `Get SpecialUsed` → add it
|
||||||
3. Right-click → search `NOT Boolean` → add it
|
3. Right-click → search `NOT Boolean` → add it
|
||||||
4. Connect SpecialUsed → NOT → Branch Condition
|
4. Connect SpecialUsed → NOT → Branch Condition
|
||||||
- We branch on "NOT SpecialUsed" (true = hasn't been used yet)
|
- 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
|
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)
|
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)"
|
- Type: "TODO: Add enemy/bullet destruction (Part 4)"
|
||||||
- Position it after the Print String
|
- 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)
|
- 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
|
#### f) Compile and Save
|
||||||
|
|
||||||
#### g) Test the placeholder:
|
#### g) Test the placeholder
|
||||||
|
|
||||||
1. Press Play
|
1. Press Play
|
||||||
2. Press X key (or Right Mouse Button)
|
2. Press X key (or Right Mouse Button)
|
||||||
3. You should see "SPECIAL ABILITY ACTIVATED!" appear once
|
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
|
### 4. Compile and Save
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- "TakeHit" and "HandleDeath" functions appear under Functions in My Blueprint panel
|
- "TakeHit" and "HandleDeath" functions appear under Functions in My Blueprint panel
|
||||||
- Event Graph shows IA_Special event with branching logic
|
- 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 key: "SPECIAL ABILITY ACTIVATED!" appears once in top-left
|
||||||
- Press X again: Nothing happens (SpecialUsed is now true)
|
- Press X again: Nothing happens (SpecialUsed is now true)
|
||||||
- TakeHit and HandleDeath functions exist but cannot be tested yet (nothing calls them)
|
- TakeHit and HandleDeath functions exist but cannot be tested yet (nothing calls them)
|
||||||
|
|
||||||
> **NOTE:** Full testing of damage/death systems requires:
|
> **NOTE:** Full testing of damage/death systems requires:
|
||||||
|
>
|
||||||
> - BP_Bullet collision (Part 3) to call TakeHit
|
> - BP_Bullet collision (Part 3) to call TakeHit
|
||||||
> - BP_GameDirector (Part 6) for HandleDeath to notify
|
> - BP_GameDirector (Part 6) for HandleDeath to notify
|
||||||
> - BP_Enemy (Part 4) for special ability to destroy
|
> - 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.
|
> **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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@ -23,20 +23,22 @@
|
|||||||
|
|
||||||
6. **Create Variables:**
|
6. **Create Variables:**
|
||||||
|
|
||||||
| Variable Name | Type | Default Value |
|
| Variable Name | Type | Default Value |
|
||||||
|---------------|------|---------------|
|
| ------------------- | ------- | ------------- |
|
||||||
| `TravelDirection` | Vector | (0, 1, 0) |
|
| `TravelDirection` | Vector | (0, 1, 0) |
|
||||||
| `TravelSpeed` | Float | 1200.0 |
|
| `TravelSpeed` | Float | 1200.0 |
|
||||||
| `RemainingLifetime` | Float | 4.0 |
|
| `RemainingLifetime` | Float | 4.0 |
|
||||||
| `IsEnemyProjectile` | Boolean | false |
|
| `IsEnemyProjectile` | Boolean | false |
|
||||||
| `Damage` | Integer | 1 |
|
| `Damage` | Integer | 1 |
|
||||||
|
|
||||||
|
### Expected Result after Compile
|
||||||
|
|
||||||
### Expected Result after Compile:
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- Components panel shows: DefaultSceneRoot → BulletSprite, BulletCollision, TempVisual
|
- Components panel shows: DefaultSceneRoot → BulletSprite, BulletCollision, TempVisual
|
||||||
- Variables panel shows all 5 variables with correct types
|
- 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)
|
- Small cube visible (the TempVisual placeholder)
|
||||||
- Sphere collision visible (radius 8)
|
- Sphere collision visible (radius 8)
|
||||||
|
|
||||||
@ -46,7 +48,7 @@
|
|||||||
|
|
||||||
1. In Event Graph, from **Event Tick:**
|
1. In Event Graph, from **Event Tick:**
|
||||||
|
|
||||||
#### a) Calculate movement:
|
### a) Calculate movement
|
||||||
|
|
||||||
1. Right-click → `Get TravelDirection`
|
1. Right-click → `Get TravelDirection`
|
||||||
2. Right-click → `Get TravelSpeed`
|
2. Right-click → `Get TravelSpeed`
|
||||||
@ -70,7 +72,7 @@
|
|||||||
- Connect the Add result to "New Location"
|
- Connect the Add result to "New Location"
|
||||||
- Connect execution wire from Event Tick to Set Actor 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`
|
9. Right-click → `Get RemainingLifetime`
|
||||||
|
|
||||||
@ -115,30 +117,32 @@ Event Tick ──► Set Actor Location ──► Set RemainingLifetime ──
|
|||||||
Destroy Actor (nothing)
|
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
|
1. In "My Blueprint" panel (left side), find "Functions" section
|
||||||
2. Click the **"+"** button next to Functions
|
2. Click the **"+"** button next to Functions
|
||||||
3. Name the new function `Initialize`
|
3. Name the new function `Initialize`
|
||||||
4. Double-click to open the function graph
|
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
|
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)
|
2. With the entry node selected, look at the Details panel (right side)
|
||||||
3. Find "Inputs" section and click "+" to add parameters:
|
3. Find "Inputs" section and click "+" to add parameters:
|
||||||
|
|
||||||
| Parameter | Type | Default |
|
| Parameter | Type | Default |
|
||||||
|-----------|------|---------|
|
| ------------- | ------- | ------- |
|
||||||
| `Direction` | Vector | - |
|
| `Direction` | Vector | - |
|
||||||
| `Speed` | Float | - |
|
| `Speed` | Float | - |
|
||||||
| `bIsEnemy` | Boolean | - |
|
| `bIsEnemy` | Boolean | - |
|
||||||
| `Lifetime` | Float | - |
|
| `Lifetime` | Float | - |
|
||||||
| `DamageValue` | Integer | 1 |
|
| `DamageValue` | Integer | 1 |
|
||||||
|
|
||||||
The entry node should now show 5 input pins.
|
The entry node should now show 5 input pins.
|
||||||
|
|
||||||
#### c) Build the function logic:
|
#### c) Build the function logic
|
||||||
|
|
||||||
1. **Normalize and set direction:**
|
1. **Normalize and set direction:**
|
||||||
- Drag from **Direction** parameter (yellow pin) → search `Normalize` → add it
|
- 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`
|
- Right-click → `Set Damage`
|
||||||
- Drag from **DamageValue** parameter → Set Damage input
|
- 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.
|
> **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
|
### 3. Compile and Save
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- "Initialize" function appears under Functions with 5 input parameters
|
- "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
|
## 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
|
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)"**
|
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
|
- 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`
|
4. Right-click → `Get IsEnemyProjectile`
|
||||||
- This gets your boolean variable
|
- 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)
|
- TRUE = this is an enemy bullet (should damage player)
|
||||||
- FALSE = this is a player bullet (should damage enemies)
|
- 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
|
6. From the **"On Component Begin Overlap"** node, look for the **"Other Actor"** output pin
|
||||||
- This is the actor that overlapped with the bullet
|
- 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:
|
12. Connect execution wire:
|
||||||
- Cast to BP_Player → TakeHit → Destroy Actor
|
- 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).
|
> **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
|
### 4. Compile and Save
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- Event Graph shows "On Component Begin Overlap" event connected to Branch
|
- 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.
|
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
|
- In Content Drawer, navigate to Content → Blueprints
|
||||||
- Double-click "BP_Player" to open the Blueprint Editor
|
- 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
|
1. In My Blueprint panel, find the `BulletClass` variable
|
||||||
2. Click on it to select it
|
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`
|
4. Click the dropdown and select `BP_Bullet`
|
||||||
5. Compile to save the change
|
5. Compile to save the change
|
||||||
|
|
||||||
### 3. CREATE SPAWN BULLET FUNCTION:
|
### 3. CREATE SPAWN BULLET FUNCTION
|
||||||
|
|
||||||
#### a) In "My Blueprint" panel, under "Functions", click "+"
|
#### a) In "My Blueprint" panel, under "Functions", click "+"
|
||||||
|
|
||||||
#### b) Name the function `SpawnBullet`
|
#### b) Name the function `SpawnBullet`
|
||||||
|
|
||||||
#### c) Double-click to open function graph
|
#### 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 the function graph, look at the purple "SpawnBullet" entry node
|
||||||
- In Details panel (right side), find "Inputs" section
|
- In Details panel (right side), find "Inputs" section
|
||||||
- Click "+" to add a new input parameter
|
- Click "+" to add a new input parameter
|
||||||
|
|
||||||
| Parameter | Type |
|
| Parameter | Type |
|
||||||
|-----------|------|
|
| --------------- | ------ |
|
||||||
| `SpawnLocation` | Vector |
|
| `SpawnLocation` | Vector |
|
||||||
| `Direction` | Vector |
|
| `Direction` | Vector |
|
||||||
|
|
||||||
The entry node should now show two input pins: SpawnLocation and Direction
|
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
|
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:
|
4. Connect execution wire:
|
||||||
- Drag from SpawnBullet entry node (white triangle) → SpawnActor (white triangle)
|
- 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:
|
5. Add the Cast node:
|
||||||
- From SpawnActor's **"Return Value"** output (blue pin on right side), drag → search `Cast to BP_Bullet`
|
- 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)
|
#### 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 "+"
|
#### a) In "My Blueprint" panel, under "Functions", click "+"
|
||||||
|
|
||||||
#### b) Name the function `FireVolley`
|
#### b) Name the function `FireVolley`
|
||||||
|
|
||||||
#### c) Double-click to open function graph
|
#### 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
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -477,73 +489,68 @@ The entry node should now show two input pins: SpawnLocation and Direction
|
|||||||
|
|
||||||
9. From Branch FALSE pin, we need to loop through each bullet.
|
9. From Branch FALSE pin, we need to loop through each bullet.
|
||||||
First, calculate the starting angle:
|
First, calculate the starting angle:
|
||||||
|
|
||||||
The spread is CENTERED on "straight up". For 3 bullets with 12° spread:
|
The spread is CENTERED on "straight up". For 3 bullets with 12° spread:
|
||||||
- Bullet 0: -12° (left of center)
|
- Bullet 0: -12° (left of center)
|
||||||
- Bullet 1: 0° (center, straight up)
|
- Bullet 1: 0° (center, straight up)
|
||||||
- Bullet 2: +12° (right of center)
|
- Bullet 2: +12° (right of center)
|
||||||
|
|
||||||
**Formula:** `StartAngle = -(VolleySpread * (VolleySize - 1)) / 2`
|
**Formula:** `StartAngle = -(VolleySpread * (VolleySize - 1)) / 2`
|
||||||
|
|
||||||
10. **Calculate StartAngle:**
|
10. **Calculate StartAngle:**
|
||||||
- Right-click → `Get VolleySize`
|
- Right-click → `Get VolleySize`
|
||||||
- Right-click → `Subtract (Integer)` → connect VolleySize, type `1`
|
- Right-click → `Subtract (Integer)` → connect VolleySize, type `1`
|
||||||
- Result: (VolleySize - 1) = 2 for default
|
- Result: (VolleySize - 1) = 2 for default
|
||||||
|
|
||||||
- Right-click → `Get VolleySpread`
|
- Right-click → `Get VolleySpread`
|
||||||
- Right-click → `Multiply (float)` → connect VolleySpread and (VolleySize-1)
|
- Right-click → `Multiply (float)` → connect VolleySpread and (VolleySize-1)
|
||||||
- NOTE: The integer will auto-convert to float
|
- 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`
|
- Right-click → `Divide (float)` → connect the multiply result, type `2`
|
||||||
- Result: 24 / 2 = 12
|
- Result: 24 / 2 = 12
|
||||||
|
|
||||||
- Right-click → `Negate (float)` or "Multiply by -1"
|
- Right-click → `Negate (float)` or "Multiply by -1"
|
||||||
- Result: -12 (this is StartAngle)
|
- Result: -12 (this is StartAngle)
|
||||||
|
|
||||||
- Right-click → `Set` → create a LOCAL variable "StartAngle" (Float)
|
- Right-click → `Set` → create a LOCAL variable "StartAngle" (Float)
|
||||||
- OR just keep the wire connected (we'll use it in the loop)
|
- OR just keep the wire connected (we'll use it in the loop)
|
||||||
|
|
||||||
11. **Set up the FOR loop:**
|
11. **Set up the FOR loop:**
|
||||||
- From Branch FALSE pin, drag → right-click → search `For Loop`
|
- From Branch FALSE pin, drag → right-click → search `For Loop`
|
||||||
- "First Index": type `0`
|
- "First Index": type `0`
|
||||||
- "Last Index": connect (VolleySize - 1)
|
- "Last Index": connect (VolleySize - 1)
|
||||||
- You already calculated this in step 10, reuse it or calculate again:
|
- You already calculated this in step 10, reuse it or calculate again:
|
||||||
- Get VolleySize → Subtract 1 → connect to Last Index
|
- Get VolleySize → Subtract 1 → connect to Last Index
|
||||||
|
|
||||||
The loop will run with Index = 0, 1, 2 for VolleySize=3
|
The loop will run with Index = 0, 1, 2 for VolleySize=3
|
||||||
|
|
||||||
12. **Inside the loop (from "Loop Body" execution pin):**
|
12. **Inside the loop (from "Loop Body" execution pin):**
|
||||||
|
|
||||||
Calculate angle for THIS bullet:
|
Calculate angle for THIS bullet:
|
||||||
- Right-click → `Multiply (float)`
|
- Right-click → `Multiply (float)`
|
||||||
- Connect loop "Index" to first input (auto-converts int to float)
|
- Connect loop "Index" to first input (auto-converts int to float)
|
||||||
- Connect VolleySpread to second input
|
- 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)`
|
- Right-click → `Add (float)`
|
||||||
- Connect StartAngle (the -12 from step 10) to first input
|
- Connect StartAngle (the -12 from step 10) to first input
|
||||||
- Connect (Index * VolleySpread) to second input
|
- Connect (Index \* VolleySpread) to second input
|
||||||
- Result: StartAngle + (Index * VolleySpread) = -12, 0, +12 degrees
|
- Result: StartAngle + (Index \* VolleySpread) = -12, 0, +12 degrees
|
||||||
|
|
||||||
This is the angle in DEGREES. Store or continue with this value.
|
This is the angle in DEGREES. Store or continue with this value.
|
||||||
|
|
||||||
13. **Convert angle from degrees to a direction vector:**
|
13. **Convert angle from degrees to a direction vector:**
|
||||||
|
|
||||||
In Unreal's top-down view (looking down Z axis):
|
In Unreal's top-down view (looking down Z axis):
|
||||||
- X axis = up/down on screen (vertical)
|
- X axis = up/down on screen (vertical)
|
||||||
- Y axis = left/right on screen (horizontal)
|
- Y axis = left/right on screen (horizontal)
|
||||||
- Angle 0° = straight up = direction (1, 0, 0)
|
- Angle 0° = straight up = direction (1, 0, 0)
|
||||||
- Angle 90° = right = direction (0, 1, 0)
|
- Angle 90° = right = direction (0, 1, 0)
|
||||||
|
|
||||||
```
|
```
|
||||||
Direction.X = Cos(angle) // Cos for vertical (forward/up)
|
Direction.X = Cos(angle) // Cos for vertical (forward/up)
|
||||||
Direction.Y = Sin(angle) // Sin for horizontal offset
|
Direction.Y = Sin(angle) // Sin for horizontal offset
|
||||||
Direction.Z = 0
|
Direction.Z = 0
|
||||||
```
|
```
|
||||||
|
|
||||||
BUT Unreal's Sin/Cos use RADIANS, not degrees!
|
BUT Unreal's Sin/Cos use RADIANS, not degrees!
|
||||||
|
|
||||||
**a) Convert degrees to radians:**
|
**a) Convert degrees to radians:**
|
||||||
- Right-click → search `Degrees To Radians` → add it
|
- Right-click → search `Degrees To Radians` → add it
|
||||||
- Connect your angle (from step 12) to the input
|
- Connect your angle (from step 12) to the input
|
||||||
@ -552,14 +559,14 @@ The entry node should now show two input pins: SpawnLocation and Direction
|
|||||||
**b) Calculate X component (Cos - forward/up direction):**
|
**b) Calculate X component (Cos - forward/up direction):**
|
||||||
- Right-click → `Cos (Radians)` → add it
|
- Right-click → `Cos (Radians)` → add it
|
||||||
- Connect the Degrees To Radians output to Cos input
|
- Connect the Degrees To Radians output to Cos input
|
||||||
|
|
||||||
**c) Calculate Y component (Sin - horizontal offset):**
|
**c) Calculate Y component (Sin - horizontal offset):**
|
||||||
- Right-click → `Sin (Radians)` → add it
|
- Right-click → `Sin (Radians)` → add it
|
||||||
- To connect the SAME radians value to Sin (without losing the Cos connection):
|
- To connect the SAME radians value to Sin (without losing the Cos connection):
|
||||||
- **Option 1:** Ctrl+drag from "Degrees To Radians" output to Sin input (creates second wire)
|
- **Option 1:** Ctrl+drag from "Degrees To Radians" output to Sin input (creates second wire)
|
||||||
- **Option 2:** Drag directly from "Degrees To Radians" output again - UE5 allows multiple wires from one output pin
|
- **Option 2:** Drag directly from "Degrees To Radians" output again - UE5 allows multiple wires from one output pin
|
||||||
- Both Cos and Sin should now be connected to the same radians value
|
- Both Cos and Sin should now be connected to the same radians value
|
||||||
|
|
||||||
**d) Make the direction vector:**
|
**d) Make the direction vector:**
|
||||||
- Right-click → `Make Vector`
|
- Right-click → `Make Vector`
|
||||||
- Connect Cos result to X (forward/up direction)
|
- Connect Cos result to X (forward/up direction)
|
||||||
@ -606,12 +613,13 @@ The entry node should now show two input pins: SpawnLocation and Direction
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Test Values (VolleySize=3, VolleySpread=12):**
|
**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
|
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)
|
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
|
### 6. Compile and Save
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- "FireVolley" and "SpawnBullet" functions appear under Functions in My Blueprint panel
|
- "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
|
- Pressing Z or Left Mouse Button spawns 3 bullets in a spread pattern
|
||||||
- Bullets travel upward from player position
|
- Bullets travel upward from player position
|
||||||
- Rapid fire when holding the button (every 0.08 seconds)
|
- Rapid fire when holding the button (every 0.08 seconds)
|
||||||
|
|||||||
@ -22,31 +22,33 @@
|
|||||||
|
|
||||||
6. **Create Variables:**
|
6. **Create Variables:**
|
||||||
|
|
||||||
| Variable Name | Type | Default Value |
|
| Variable Name | Type | Default Value |
|
||||||
|---------------|------|---------------|
|
| --------------------- | ------------------------ | ------------- |
|
||||||
| `MaxHealth` | Integer | 12 |
|
| `MaxHealth` | Integer | 12 |
|
||||||
| `CurrentHealth` | Integer | 12 |
|
| `CurrentHealth` | Integer | 12 |
|
||||||
| `ScoreValue` | Integer | 50 |
|
| `ScoreValue` | Integer | 50 |
|
||||||
| `VerticalSpeed` | Float | 220.0 |
|
| `VerticalSpeed` | Float | 220.0 |
|
||||||
| `HorizontalAmplitude` | Float | 250.0 |
|
| `HorizontalAmplitude` | Float | 250.0 |
|
||||||
| `HorizontalFrequency` | Float | 1.8 |
|
| `HorizontalFrequency` | Float | 1.8 |
|
||||||
| `DespawnY` | Float | -750.0 |
|
| `DespawnY` | Float | -750.0 |
|
||||||
| `FireInterval` | Float | 0.35 |
|
| `FireInterval` | Float | 0.35 |
|
||||||
| `BulletsPerBurst` | Integer | 20 |
|
| `BulletsPerBurst` | Integer | 20 |
|
||||||
| `BurstSpread` | Float | 360.0 |
|
| `BurstSpread` | Float | 360.0 |
|
||||||
| `EnemyBulletSpeed` | Float | 1000.0 |
|
| `EnemyBulletSpeed` | Float | 1000.0 |
|
||||||
| `EnemyBulletLifetime` | Float | 6.0 |
|
| `EnemyBulletLifetime` | Float | 6.0 |
|
||||||
| `BaseX` | Float | 0.0 |
|
| `BaseX` | Float | 0.0 |
|
||||||
| `WaveSeed` | Float | 0.0 |
|
| `WaveSeed` | Float | 0.0 |
|
||||||
| `FireTimer` | Float | 0.0 |
|
| `FireTimer` | Float | 0.0 |
|
||||||
| `BulletClass` | Class Reference to Actor | (set later) |
|
| `BulletClass` | Class Reference to Actor | (set later) |
|
||||||
|
|
||||||
|
### Expected Result after Compile
|
||||||
|
|
||||||
### Expected Result after Compile:
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- Components panel shows: DefaultSceneRoot → EnemySprite, EnemyCollision, TempVisual
|
- Components panel shows: DefaultSceneRoot → EnemySprite, EnemyCollision, TempVisual
|
||||||
- Variables panel shows all 16 variables with correct types and defaults
|
- 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)
|
- Cube visible (the TempVisual placeholder, larger than player)
|
||||||
- Box collision visible (30x30x10)
|
- Box collision visible (30x30x10)
|
||||||
|
|
||||||
@ -56,20 +58,23 @@
|
|||||||
|
|
||||||
1. In Event Graph, from **"Event BeginPlay":**
|
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)
|
(6.28 ≈ 2π for wave randomization)
|
||||||
|
|
||||||
#### d) Random Float in Range (0, FireInterval) → Set FireTimer
|
#### d) Random Float in Range (0, FireInterval) → Set FireTimer
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- BeginPlay event connected to variable setters
|
- 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 spawns with randomized WaveSeed (0 to 6.28)
|
||||||
- Each enemy starts with different FireTimer offset
|
- Each enemy starts with different FireTimer offset
|
||||||
- This creates varied, non-synchronized enemy behavior
|
- This creates varied, non-synchronized enemy behavior
|
||||||
@ -80,11 +85,13 @@
|
|||||||
|
|
||||||
1. In Event Graph, from **"Event Tick":**
|
1. In Event Graph, from **"Event Tick":**
|
||||||
|
|
||||||
#### a) VERTICAL MOVEMENT:
|
### a) VERTICAL MOVEMENT
|
||||||
- Get Actor Location → Break into X, Y, Z
|
|
||||||
- Subtract (VerticalSpeed * DeltaSeconds) from Y
|
- 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
|
- Get Game Time in Seconds
|
||||||
- Add WaveSeed
|
- Add WaveSeed
|
||||||
- Multiply by HorizontalFrequency
|
- Multiply by HorizontalFrequency
|
||||||
@ -95,7 +102,8 @@
|
|||||||
|
|
||||||
#### c) Set Actor Location with new X, Y (Z stays 0)
|
#### c) Set Actor Location with new X, Y (Z stays 0)
|
||||||
|
|
||||||
#### d) DESPAWN CHECK:
|
#### d) DESPAWN CHECK
|
||||||
|
|
||||||
- If Y < DespawnY: Destroy Actor
|
- If Y < DespawnY: Destroy Actor
|
||||||
- If Abs(X) > 1400: 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
|
- Compile button shows GREEN checkmark
|
||||||
- Event Tick connected to movement and despawn logic
|
- 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 drift downward at VerticalSpeed (220 units/sec)
|
||||||
- Enemies oscillate horizontally in sine wave pattern
|
- Enemies oscillate horizontally in sine wave pattern
|
||||||
- Each enemy has different horizontal phase (due to WaveSeed)
|
- Each enemy has different horizontal phase (due to WaveSeed)
|
||||||
@ -129,28 +139,31 @@
|
|||||||
|
|
||||||
## Step 4.4: Enemy Firing Logic
|
## 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
|
#### a) Decrease FireTimer by DeltaSeconds
|
||||||
|
|
||||||
#### b) Branch: if FireTimer <= 0:
|
#### b) Branch: if FireTimer <= 0
|
||||||
|
|
||||||
- Reset FireTimer to FireInterval
|
- Reset FireTimer to FireInterval
|
||||||
- Call "FireBurst" function
|
- Call "FireBurst" function
|
||||||
|
|
||||||
### 2. CREATE "FireBurst" FUNCTION:
|
### 2. CREATE "FireBurst" FUNCTION
|
||||||
|
|
||||||
#### a) Add function "FireBurst"
|
#### a) Add function "FireBurst"
|
||||||
|
|
||||||
#### b) Inside:
|
#### b) Inside
|
||||||
|
|
||||||
- Get BulletsPerBurst
|
- Get BulletsPerBurst
|
||||||
- Calculate angle step: `360 / BulletsPerBurst` (for full circle)
|
- Calculate angle step: `360 / BulletsPerBurst` (for full circle)
|
||||||
- OR: `BurstSpread / (BulletsPerBurst - 1)` (for partial arc)
|
- 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`
|
- Calculate angle: `i * AngleStep`
|
||||||
- Convert to direction vector:
|
- Convert to direction vector:
|
||||||
- X = Sin(angle in radians)
|
- X = Sin(angle in radians)
|
||||||
- Y = -Cos(angle in radians) ← negative because enemies fire DOWN
|
- Y = -Cos(angle in radians) ← negative because enemies fire DOWN
|
||||||
- Spawn bullet:
|
- Spawn bullet:
|
||||||
- Spawn Actor from Class (BP_Bullet)
|
- Spawn Actor from Class (BP_Bullet)
|
||||||
- Get spawned bullet, call Initialize:
|
- Get spawned bullet, call Initialize:
|
||||||
@ -171,11 +184,13 @@
|
|||||||
↓
|
↓
|
||||||
```
|
```
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- "FireBurst" function appears under Functions
|
- "FireBurst" function appears under Functions
|
||||||
|
|
||||||
### Expected Result in Play mode:
|
### Expected Result in Play mode
|
||||||
|
|
||||||
- Every 0.35 seconds (FireInterval), enemy fires bullet burst
|
- Every 0.35 seconds (FireInterval), enemy fires bullet burst
|
||||||
- 20 bullets spawn in a full 360° circle pattern
|
- 20 bullets spawn in a full 360° circle pattern
|
||||||
- Bullets travel outward from enemy position
|
- Bullets travel outward from enemy position
|
||||||
@ -185,37 +200,42 @@
|
|||||||
|
|
||||||
## Step 4.5: Enemy Damage and Death
|
## Step 4.5: Enemy Damage and Death
|
||||||
|
|
||||||
### 1. CREATE "ApplyDamage" FUNCTION:
|
### 1. CREATE "ApplyDamage" FUNCTION
|
||||||
|
|
||||||
#### a) Add input: `DamageAmount` (Integer)
|
#### a) Add input: `DamageAmount` (Integer)
|
||||||
|
|
||||||
#### b) Inside:
|
#### b) Inside
|
||||||
|
|
||||||
- Subtract DamageAmount from CurrentHealth
|
- Subtract DamageAmount from CurrentHealth
|
||||||
- If CurrentHealth <= 0:
|
- If CurrentHealth <= 0:
|
||||||
- Call HandleDeath
|
- 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))
|
- Get reference to ScoreManager (we'll create in [Part 7](part-7-score-manager-ui.md))
|
||||||
- Call AddScore, passing ScoreValue
|
- Call AddScore, passing ScoreValue
|
||||||
- Spawn death effect (optional)
|
- Spawn death effect (optional)
|
||||||
- Destroy Actor
|
- 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
|
- Cast Other Actor to BP_Player
|
||||||
- If successful:
|
- If successful:
|
||||||
- Call TakeHit(1) on player
|
- Call TakeHit(1) on player
|
||||||
- Call HandleDeath() on self
|
- Call HandleDeath() on self
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- "ApplyDamage" and "HandleDeath" functions appear under Functions
|
- "ApplyDamage" and "HandleDeath" functions appear under Functions
|
||||||
- EnemyCollision has overlap event in Event Graph
|
- EnemyCollision has overlap event in Event Graph
|
||||||
|
|
||||||
### Expected Result in Play mode:
|
### Expected Result in Play mode
|
||||||
|
|
||||||
- Player bullets hitting enemy: Enemy health decreases
|
- Player bullets hitting enemy: Enemy health decreases
|
||||||
- After 12 hits (MaxHealth): Enemy disappears, score increases by 50
|
- After 12 hits (MaxHealth): Enemy disappears, score increases by 50
|
||||||
- Player colliding with enemy: Player takes 1 damage, enemy dies
|
- 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).
|
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`
|
1. Content Browser → Blueprints → double-click `BP_Player`
|
||||||
2. Go to **Event Graph** tab
|
2. Go to **Event Graph** tab
|
||||||
3. Find the `EnhancedInputAction IA_Special` event node (created in Step 2.6)
|
3. Find the `EnhancedInputAction IA_Special` event node (created in Step 2.6)
|
||||||
4. Locate the `Print String "SPECIAL ABILITY ACTIVATED!"` node
|
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
|
- Select the `Print String` node
|
||||||
- Press Delete
|
- Press Delete
|
||||||
- (Keep the TODO comment if you want, or delete it too)
|
- (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
|
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`
|
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
|
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
|
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)
|
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
|
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
|
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
|
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`
|
2. Click "Actor Class" dropdown → select `BP_Bullet`
|
||||||
3. From this Get All Actors, drag execution → search `For Each Loop` → add another loop
|
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
|
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
|
2. From "Array Element" (the current bullet), drag → search `Get IsEnemyProjectile` → add it
|
||||||
- This reads the IsEnemyProjectile variable from BP_Bullet
|
- This reads the IsEnemyProjectile variable from BP_Bullet
|
||||||
3. Connect `IsEnemyProjectile` output (boolean) to Branch's "Condition" input
|
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
|
1. From Branch's **"True"** pin, drag → search `Destroy Actor` → add it
|
||||||
2. Connect "Array Element" (the bullet) to Destroy Actor's "Target"
|
2. Connect "Array Element" (the bullet) to Destroy Actor's "Target"
|
||||||
3. Leave Branch's "False" pin unconnected (player bullets are preserved)
|
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
|
### 4. Compile and Save
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- No warnings about missing classes (BP_Enemy and BP_Bullet now exist)
|
- 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:
|
- Press X (or Right Mouse Button) once:
|
||||||
- ALL enemies on screen instantly disappear
|
- ALL enemies on screen instantly disappear
|
||||||
- ALL enemy bullets (red) 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).
|
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`
|
1. Content Browser → Blueprints → double-click `BP_Bullet`
|
||||||
2. Go to **Event Graph** tab
|
2. Go to **Event Graph** tab
|
||||||
3. Find the `On Component Begin Overlap (BulletCollision)` event node
|
3. Find the `On Component Begin Overlap (BulletCollision)` event node
|
||||||
4. Locate the `Print String "TODO: Damage enemy"` node on the FALSE branch
|
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
|
- Select the `Print String` node
|
||||||
- Press Delete
|
- Press Delete
|
||||||
|
|
||||||
#### b) Add Cast to BP_Enemy:
|
#### b) Add Cast to BP_Enemy
|
||||||
|
|
||||||
1. Right-click → search `Cast to BP_Enemy` → add it
|
1. Right-click → search `Cast to BP_Enemy` → add it
|
||||||
|
|
||||||
2. Connect execution wire:
|
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
|
- 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)
|
- (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:
|
4. From "Cast Succeeded" on the BP_Enemy cast:
|
||||||
- From the cast's **"As BP Enemy"** output pin, drag → search `ApplyDamage`
|
- From the cast's **"As BP Enemy"** output pin, drag → search `ApplyDamage`
|
||||||
- This calls the ApplyDamage function you created in Step 4.3
|
- 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)
|
- Right-click → `Get Damage` (the bullet's damage variable)
|
||||||
- Connect to ApplyDamage's "DamageAmount" input
|
- 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`
|
6. From ApplyDamage, drag execution → `Destroy Actor`
|
||||||
- Leave "Target" as "Self" (destroys this bullet)
|
- 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
|
### 3. Compile and Save
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- No warnings - both BP_Player and BP_Enemy casts are valid now
|
- 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:
|
- Player bullets (IsEnemyProjectile=false) hitting enemies:
|
||||||
- Enemy takes damage, bullet disappears
|
- Enemy takes damage, bullet disappears
|
||||||
- After enough hits, enemy dies and awards score
|
- After enough hits, enemy dies and awards score
|
||||||
|
|||||||
@ -13,17 +13,18 @@
|
|||||||
|
|
||||||
5. **Create Variables:**
|
5. **Create Variables:**
|
||||||
|
|
||||||
| Variable Name | Type | Default Value |
|
| Variable Name | Type | Default Value |
|
||||||
|---------------|------|---------------|
|
| ------------------------ | --------------- | ------------------------- |
|
||||||
| `EnemyClass` | Class Reference | (will reference BP_Enemy) |
|
| `EnemyClass` | Class Reference | (will reference BP_Enemy) |
|
||||||
| `SpawnAreaHalfWidth` | Float | 900.0 |
|
| `SpawnAreaHalfWidth` | Float | 900.0 |
|
||||||
| `GameDuration` | Float | 300.0 (5 minutes) |
|
| `GameDuration` | Float | 300.0 (5 minutes) |
|
||||||
| `MaxSimultaneousEnemies` | Integer | 120 |
|
| `MaxSimultaneousEnemies` | Integer | 120 |
|
||||||
| `ElapsedTime` | Float | 0.0 |
|
| `ElapsedTime` | Float | 0.0 |
|
||||||
| `SpawnTimer` | Float | 0.0 |
|
| `SpawnTimer` | Float | 0.0 |
|
||||||
| `SpawningActive` | Boolean | true |
|
| `SpawningActive` | Boolean | true |
|
||||||
|
|
||||||
|
### Expected Result after Compile
|
||||||
|
|
||||||
### Expected Result after Compile:
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- Variables panel shows all 7 variables with correct types
|
- Variables panel shows all 7 variables with correct types
|
||||||
- No components needed (spawner is invisible logic actor)
|
- No components needed (spawner is invisible logic actor)
|
||||||
@ -32,37 +33,40 @@
|
|||||||
|
|
||||||
## Step 5.2: Spawn Rate Curve
|
## Step 5.2: Spawn Rate Curve
|
||||||
|
|
||||||
### 1. Create Variable:
|
### 1. Create Variable
|
||||||
|
|
||||||
- `SpawnCurve` (Curve Float)
|
- `SpawnCurve` (Curve Float)
|
||||||
|
|
||||||
### 2. To create the curve asset:
|
### 2. To create the curve asset
|
||||||
|
|
||||||
1. In Content Browser, right-click → **Miscellaneous → Curve**
|
1. In Content Browser, right-click → **Miscellaneous → Curve**
|
||||||
2. Select "CurveFloat"
|
2. Select "CurveFloat"
|
||||||
3. Name it `SpawnRateCurve`
|
3. Name it `SpawnRateCurve`
|
||||||
4. Double-click to open Curve Editor
|
4. Double-click to open Curve Editor
|
||||||
|
|
||||||
### 3. In Curve Editor:
|
### 3. In Curve Editor
|
||||||
|
|
||||||
- Right-click on the curve → Add Key
|
- Right-click on the curve → Add Key
|
||||||
- Create these keyframes:
|
- Create these keyframes:
|
||||||
|
|
||||||
| Time | Value | Description |
|
| Time | Value | Description |
|
||||||
|------|-------|-------------|
|
| ---- | ----- | -------------------- |
|
||||||
| 0.0 | 0.4 | slow spawn at start |
|
| 0.0 | 0.4 | slow spawn at start |
|
||||||
| 0.5 | 2.0 | medium spawn halfway |
|
| 0.5 | 2.0 | medium spawn halfway |
|
||||||
| 1.0 | 4.5 | fast spawn at end |
|
| 1.0 | 4.5 | fast spawn at end |
|
||||||
|
|
||||||
- The X axis is normalized time (0-1)
|
- The X axis is normalized time (0-1)
|
||||||
- The Y axis is spawns per second
|
- The Y axis is spawns per second
|
||||||
|
|
||||||
### 4. In BP_EnemySpawner, set SpawnCurve default to this curve asset
|
### 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)
|
- Curve line visible starting at (0, 0.4), rising through (0.5, 2.0), ending at (1.0, 4.5)
|
||||||
- Smooth interpolation between keyframes
|
- Smooth interpolation between keyframes
|
||||||
|
|
||||||
### Expected Result in Play mode:
|
### Expected Result in Play mode
|
||||||
|
|
||||||
- Game start: ~0.4 enemies spawn per second (slow)
|
- Game start: ~0.4 enemies spawn per second (slow)
|
||||||
- At 2.5 minutes: ~2 enemies spawn per second (medium)
|
- At 2.5 minutes: ~2 enemies spawn per second (medium)
|
||||||
- At 5 minutes: ~4.5 enemies spawn per second (intense)
|
- At 5 minutes: ~4.5 enemies spawn per second (intense)
|
||||||
@ -71,67 +75,81 @@
|
|||||||
|
|
||||||
## Step 5.3: Spawning Logic
|
## 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
|
#### a) Check if SpawningActive is true
|
||||||
|
|
||||||
- If false, do nothing
|
- If false, do nothing
|
||||||
|
|
||||||
#### b) Update ElapsedTime:
|
#### b) Update ElapsedTime
|
||||||
|
|
||||||
- Add DeltaSeconds to ElapsedTime
|
- Add DeltaSeconds to ElapsedTime
|
||||||
|
|
||||||
#### c) Calculate normalized time:
|
#### c) Calculate normalized time
|
||||||
|
|
||||||
- Divide ElapsedTime by GameDuration
|
- Divide ElapsedTime by GameDuration
|
||||||
- Clamp between 0 and 1
|
- Clamp between 0 and 1
|
||||||
|
|
||||||
#### d) Get spawn rate from curve:
|
#### d) Get spawn rate from curve
|
||||||
|
|
||||||
- Get SpawnCurve
|
- Get SpawnCurve
|
||||||
- Call `Get Float Value` with normalized time
|
- Call `Get Float Value` with normalized time
|
||||||
- This returns spawns per second
|
- This returns spawns per second
|
||||||
|
|
||||||
#### e) Update SpawnTimer:
|
#### e) Update SpawnTimer
|
||||||
|
|
||||||
- Subtract DeltaSeconds from SpawnTimer
|
- Subtract DeltaSeconds from SpawnTimer
|
||||||
- If SpawnTimer <= 0:
|
- If SpawnTimer <= 0:
|
||||||
- Reset SpawnTimer to `(1.0 / SpawnsPerSecond)`
|
- Reset SpawnTimer to `(1.0 / SpawnsPerSecond)`
|
||||||
- Call SpawnWave function
|
- Call SpawnWave function
|
||||||
|
|
||||||
### 2. CREATE "SpawnWave" FUNCTION:
|
### 2. CREATE "SpawnWave" FUNCTION
|
||||||
|
|
||||||
|
#### a) Inside
|
||||||
|
|
||||||
#### a) Inside:
|
|
||||||
- Calculate burst size based on time:
|
- Calculate burst size based on time:
|
||||||
|
|
||||||
```
|
```
|
||||||
BaseCount = 1 + (NormalizedTime * 6)
|
BaseCount = 1 + (NormalizedTime * 6)
|
||||||
BurstSize = BaseCount + Random(0, 2)
|
BurstSize = BaseCount + Random(0, 2)
|
||||||
Clamp between 1 and 12
|
Clamp between 1 and 12
|
||||||
```
|
```
|
||||||
|
|
||||||
#### b) For Loop from 0 to BurstSize - 1:
|
#### b) For Loop from 0 to BurstSize - 1
|
||||||
|
|
||||||
- Call SpawnEnemy function
|
- Call SpawnEnemy function
|
||||||
|
|
||||||
### 3. CREATE "SpawnEnemy" FUNCTION:
|
### 3. CREATE "SpawnEnemy" FUNCTION
|
||||||
|
|
||||||
|
#### a) Inside
|
||||||
|
|
||||||
#### a) Inside:
|
|
||||||
- Check: Get All Actors of Class (BP_Enemy)
|
- Check: Get All Actors of Class (BP_Enemy)
|
||||||
- Get array length
|
- Get array length
|
||||||
- If >= MaxSimultaneousEnemies: Return (don't spawn)
|
- If >= MaxSimultaneousEnemies: Return (don't spawn)
|
||||||
|
|
||||||
#### b) Calculate spawn position:
|
#### b) Calculate spawn position
|
||||||
|
|
||||||
- X = Random Float in Range (-SpawnAreaHalfWidth, SpawnAreaHalfWidth)
|
- X = Random Float in Range (-SpawnAreaHalfWidth, SpawnAreaHalfWidth)
|
||||||
- Y = Get this actor's Y position (top of screen)
|
- Y = Get this actor's Y position (top of screen)
|
||||||
- Z = 0
|
- Z = 0
|
||||||
|
|
||||||
#### c) Spawn Actor from Class:
|
#### c) Spawn Actor from Class
|
||||||
|
|
||||||
- Class: EnemyClass
|
- Class: EnemyClass
|
||||||
- Location: calculated position
|
- Location: calculated position
|
||||||
- Rotation: (0, 0, 0)
|
- Rotation: (0, 0, 0)
|
||||||
|
|
||||||
### 4. CREATE "StopSpawning" FUNCTION:
|
### 4. CREATE "StopSpawning" FUNCTION
|
||||||
|
|
||||||
- Set SpawningActive = false
|
- Set SpawningActive = false
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- "SpawnWave", "SpawnEnemy", "StopSpawning" functions appear under Functions
|
- "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
|
- Enemies spawn at top of screen (Y=500) at random X positions
|
||||||
- Spawn rate increases over time following the curve
|
- Spawn rate increases over time following the curve
|
||||||
- Maximum 120 enemies on screen at once
|
- Maximum 120 enemies on screen at once
|
||||||
|
|||||||
@ -13,15 +13,16 @@
|
|||||||
|
|
||||||
5. **Create Variables:**
|
5. **Create Variables:**
|
||||||
|
|
||||||
| Variable Name | Type | Default Value |
|
| Variable Name | Type | Default Value |
|
||||||
|---------------|------|---------------|
|
| ------------------ | ----------------------------------- | ------------- |
|
||||||
| `PlayerReference` | Object Reference to BP_Player | - |
|
| `PlayerReference` | Object Reference to BP_Player | - |
|
||||||
| `SpawnerReference` | Object Reference to BP_EnemySpawner | - |
|
| `SpawnerReference` | Object Reference to BP_EnemySpawner | - |
|
||||||
| `GameDuration` | Float | 300.0 |
|
| `GameDuration` | Float | 300.0 |
|
||||||
| `ElapsedTime` | Float | 0.0 |
|
| `ElapsedTime` | Float | 0.0 |
|
||||||
| `GameActive` | Boolean | true |
|
| `GameActive` | Boolean | true |
|
||||||
|
|
||||||
|
### Expected Result after Compile
|
||||||
|
|
||||||
### Expected Result after Compile:
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- Variables panel shows all 5 variables with correct types
|
- Variables panel shows all 5 variables with correct types
|
||||||
- No components needed (director is invisible logic actor)
|
- No components needed (director is invisible logic actor)
|
||||||
@ -30,27 +31,32 @@
|
|||||||
|
|
||||||
## Step 6.2: Game Director Initialization
|
## 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 All Actors of Class → BP_Player
|
||||||
- Get first element (index 0)
|
- Get first element (index 0)
|
||||||
- Set PlayerReference
|
- Set PlayerReference
|
||||||
|
|
||||||
#### b) Find spawner in scene:
|
#### b) Find spawner in scene
|
||||||
|
|
||||||
- Get All Actors of Class → BP_EnemySpawner
|
- Get All Actors of Class → BP_EnemySpawner
|
||||||
- Get first element
|
- Get first element
|
||||||
- Set SpawnerReference
|
- Set SpawnerReference
|
||||||
|
|
||||||
#### c) Initialize ScoreManager:
|
#### c) Initialize ScoreManager
|
||||||
|
|
||||||
- Get ScoreManager reference
|
- Get ScoreManager reference
|
||||||
- Call RegisterGameStart with initial lives and duration
|
- Call RegisterGameStart with initial lives and duration
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- BeginPlay event connected to "Get All Actors of Class" nodes
|
- 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
|
- Game Director automatically finds Player, Spawner, and ScoreManager
|
||||||
- UI initializes with correct starting values (Lives: 3, Time: 05:00)
|
- UI initializes with correct starting values (Lives: 3, Time: 05:00)
|
||||||
|
|
||||||
@ -58,41 +64,49 @@
|
|||||||
|
|
||||||
## Step 6.3: Game Director Update Logic
|
## Step 6.3: Game Director Update Logic
|
||||||
|
|
||||||
### 1. From Event Tick:
|
### 1. From Event Tick
|
||||||
|
|
||||||
#### a) Check if GameActive
|
#### a) Check if GameActive
|
||||||
|
|
||||||
- If false, skip everything
|
- If false, skip everything
|
||||||
|
|
||||||
#### b) Update elapsed time:
|
#### b) Update elapsed time
|
||||||
|
|
||||||
- Add DeltaSeconds to ElapsedTime
|
- Add DeltaSeconds to ElapsedTime
|
||||||
|
|
||||||
#### c) Calculate remaining time:
|
#### c) Calculate remaining time
|
||||||
|
|
||||||
- Subtract ElapsedTime from GameDuration
|
- Subtract ElapsedTime from GameDuration
|
||||||
- Max with 0 (don't go negative)
|
- Max with 0 (don't go negative)
|
||||||
|
|
||||||
#### d) Update UI timer:
|
#### d) Update UI timer
|
||||||
|
|
||||||
- Get ScoreManager
|
- Get ScoreManager
|
||||||
- Call UpdateTimer with remaining time
|
- Call UpdateTimer with remaining time
|
||||||
|
|
||||||
#### e) Check for victory:
|
#### e) Check for victory
|
||||||
|
|
||||||
- If ElapsedTime >= GameDuration:
|
- If ElapsedTime >= GameDuration:
|
||||||
- Set GameActive = false
|
- Set GameActive = false
|
||||||
- Get SpawnerReference → Call StopSpawning
|
- Get SpawnerReference → Call StopSpawning
|
||||||
- Get ScoreManager → Call HandleGameClear
|
- Get ScoreManager → Call HandleGameClear
|
||||||
|
|
||||||
### 2. CREATE "HandlePlayerDeath" FUNCTION:
|
### 2. CREATE "HandlePlayerDeath" FUNCTION
|
||||||
|
|
||||||
|
#### a) Inside
|
||||||
|
|
||||||
#### a) Inside:
|
|
||||||
- Set GameActive = false
|
- Set GameActive = false
|
||||||
- Get SpawnerReference → Call StopSpawning
|
- Get SpawnerReference → Call StopSpawning
|
||||||
- Get ScoreManager → Call HandleGameOver
|
- Get ScoreManager → Call HandleGameOver
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- "HandlePlayerDeath" function appears under Functions
|
- "HandlePlayerDeath" function appears under Functions
|
||||||
- Event Tick connected to timer update and victory check
|
- 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
|
- Timer counts down from 05:00 to 00:00
|
||||||
- At 00:00: "Mission Complete" appears, enemies stop spawning
|
- At 00:00: "Mission Complete" appears, enemies stop spawning
|
||||||
- When player dies: "Game Over" appears, enemies stop spawning
|
- When player dies: "Game Over" appears, enemies stop spawning
|
||||||
|
|||||||
@ -11,8 +11,10 @@
|
|||||||
3. Name: `WBP_HUD`
|
3. Name: `WBP_HUD`
|
||||||
4. Double-click to open Widget Designer
|
4. Double-click to open Widget Designer
|
||||||
|
|
||||||
### Expected Result:
|
### Expected Result
|
||||||
|
|
||||||
Widget Designer opens with:
|
Widget Designer opens with:
|
||||||
|
|
||||||
- Hierarchy panel on left
|
- Hierarchy panel on left
|
||||||
- Canvas preview in center
|
- Canvas preview in center
|
||||||
- Details panel on right
|
- Details panel on right
|
||||||
@ -22,9 +24,10 @@ Widget Designer opens with:
|
|||||||
## Step 7.2: Design HUD Layout
|
## Step 7.2: Design HUD Layout
|
||||||
|
|
||||||
### 1. In the Palette panel (left side), search for "Canvas Panel"
|
### 1. In the Palette panel (left side), search for "Canvas Panel"
|
||||||
|
|
||||||
- Drag Canvas Panel to the Hierarchy (if not already there)
|
- Drag Canvas Panel to the Hierarchy (if not already there)
|
||||||
|
|
||||||
### 2. Add Score Text:
|
### 2. Add Score Text
|
||||||
|
|
||||||
1. In Palette, search for "Text"
|
1. In Palette, search for "Text"
|
||||||
2. Drag "Text" widget onto Canvas Panel in hierarchy
|
2. Drag "Text" widget onto Canvas Panel in hierarchy
|
||||||
@ -41,7 +44,7 @@ Widget Designer opens with:
|
|||||||
- Font Size: `24`
|
- Font Size: `24`
|
||||||
- Color: White
|
- Color: White
|
||||||
|
|
||||||
### 3. Add Lives Text:
|
### 3. Add Lives Text
|
||||||
|
|
||||||
1. Drag another "Text" widget
|
1. Drag another "Text" widget
|
||||||
2. Rename to `LivesText`
|
2. Rename to `LivesText`
|
||||||
@ -49,7 +52,7 @@ Widget Designer opens with:
|
|||||||
4. Text: `Lives: 3`
|
4. Text: `Lives: 3`
|
||||||
5. Same font settings as Score
|
5. Same font settings as Score
|
||||||
|
|
||||||
### 4. Add Timer Text:
|
### 4. Add Timer Text
|
||||||
|
|
||||||
1. Drag another "Text" widget
|
1. Drag another "Text" widget
|
||||||
2. Rename to `TimerText`
|
2. Rename to `TimerText`
|
||||||
@ -59,9 +62,10 @@ Widget Designer opens with:
|
|||||||
|
|
||||||
### 5. Click "Compile" and "Save" (top buttons)
|
### 5. Click "Compile" and "Save" (top buttons)
|
||||||
|
|
||||||
### Expected Result:
|
### Expected Result
|
||||||
|
|
||||||
Preview shows:
|
Preview shows:
|
||||||
|
|
||||||
```
|
```
|
||||||
┌────────────────────────────────────┐
|
┌────────────────────────────────────┐
|
||||||
│ Score: 0 │
|
│ Score: 0 │
|
||||||
@ -83,14 +87,15 @@ Preview shows:
|
|||||||
|
|
||||||
5. **Create Variables:**
|
5. **Create Variables:**
|
||||||
|
|
||||||
| Variable Name | Type | Default Value |
|
| Variable Name | Type | Default Value |
|
||||||
|---------------|------|---------------|
|
| ---------------- | --------------------------- | ---------------- |
|
||||||
| `Score` | Integer | 0 |
|
| `Score` | Integer | 0 |
|
||||||
| `CurrentLives` | Integer | 3 |
|
| `CurrentLives` | Integer | 3 |
|
||||||
| `HUDWidget` | Object Reference to WBP_HUD | - |
|
| `HUDWidget` | Object Reference to WBP_HUD | - |
|
||||||
| `HUDWidgetClass` | Class Reference | (set to WBP_HUD) |
|
| `HUDWidgetClass` | Class Reference | (set to WBP_HUD) |
|
||||||
|
|
||||||
|
### Expected Result after Compile
|
||||||
|
|
||||||
### Expected Result after Compile:
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- Variables panel shows all 4 variables with correct types
|
- Variables panel shows all 4 variables with correct types
|
||||||
|
|
||||||
@ -98,25 +103,30 @@ Preview shows:
|
|||||||
|
|
||||||
## Step 7.4: Score Manager Initialization
|
## 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`
|
- Right-click → `Create Widget`
|
||||||
- Class: Select WBP_HUD (or use HUDWidgetClass variable)
|
- Class: Select WBP_HUD (or use HUDWidgetClass variable)
|
||||||
- Owning Player: Get Player Controller (index 0)
|
- Owning Player: Get Player Controller (index 0)
|
||||||
|
|
||||||
#### b) Store widget reference:
|
#### b) Store widget reference
|
||||||
|
|
||||||
- Set HUDWidget to the created widget
|
- Set HUDWidget to the created widget
|
||||||
|
|
||||||
#### c) Add to viewport:
|
#### c) Add to viewport
|
||||||
|
|
||||||
- Right-click → `Add to Viewport`
|
- Right-click → `Add to Viewport`
|
||||||
- Connect widget reference as target
|
- Connect widget reference as target
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- BeginPlay event connected to Create Widget → Add to Viewport
|
- 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 appears immediately when game starts
|
||||||
- HUD displays in top-left corner of screen
|
- HUD displays in top-left corner of screen
|
||||||
- Text is visible and readable (white on game background)
|
- Text is visible and readable (white on game background)
|
||||||
@ -125,22 +135,25 @@ Preview shows:
|
|||||||
|
|
||||||
## Step 7.5: Score Manager Functions
|
## Step 7.5: Score Manager Functions
|
||||||
|
|
||||||
### 1. CREATE "RegisterGameStart" FUNCTION:
|
### 1. CREATE "RegisterGameStart" FUNCTION
|
||||||
|
|
||||||
|
**Inputs:**
|
||||||
|
|
||||||
**Inputs:**
|
|
||||||
- `InitialLives` (Integer)
|
- `InitialLives` (Integer)
|
||||||
- `Duration` (Float)
|
- `Duration` (Float)
|
||||||
|
|
||||||
**Inside:**
|
**Inside:**
|
||||||
|
|
||||||
- Set Score = 0
|
- Set Score = 0
|
||||||
- Set CurrentLives = InitialLives
|
- Set CurrentLives = InitialLives
|
||||||
- Update all UI labels
|
- Update all UI labels
|
||||||
|
|
||||||
### 2. CREATE "AddScore" FUNCTION:
|
### 2. CREATE "AddScore" FUNCTION
|
||||||
|
|
||||||
**Input:** `Amount` (Integer)
|
**Input:** `Amount` (Integer)
|
||||||
|
|
||||||
**Inside:**
|
**Inside:**
|
||||||
|
|
||||||
- Add Amount to Score
|
- Add Amount to Score
|
||||||
- Update Score label in HUD:
|
- Update Score label in HUD:
|
||||||
- Get HUDWidget
|
- Get HUDWidget
|
||||||
@ -148,42 +161,50 @@ Preview shows:
|
|||||||
- Get "ScoreText" widget
|
- Get "ScoreText" widget
|
||||||
- Set Text to "Score: " + Score
|
- Set Text to "Score: " + Score
|
||||||
|
|
||||||
### 3. CREATE "SetLives" FUNCTION:
|
### 3. CREATE "SetLives" FUNCTION
|
||||||
|
|
||||||
**Input:** `Lives` (Integer)
|
**Input:** `Lives` (Integer)
|
||||||
|
|
||||||
**Inside:**
|
**Inside:**
|
||||||
|
|
||||||
- Set CurrentLives = Lives
|
- Set CurrentLives = Lives
|
||||||
- Update Lives label in HUD
|
- Update Lives label in HUD
|
||||||
|
|
||||||
### 4. CREATE "UpdateTimer" FUNCTION:
|
### 4. CREATE "UpdateTimer" FUNCTION
|
||||||
|
|
||||||
**Input:** `TimeRemaining` (Float)
|
**Input:** `TimeRemaining` (Float)
|
||||||
|
|
||||||
**Inside:**
|
**Inside:**
|
||||||
|
|
||||||
- Convert to minutes:seconds format:
|
- Convert to minutes:seconds format:
|
||||||
|
|
||||||
```
|
```
|
||||||
Minutes = Floor(TimeRemaining / 60)
|
Minutes = Floor(TimeRemaining / 60)
|
||||||
Seconds = Floor(TimeRemaining mod 60)
|
Seconds = Floor(TimeRemaining mod 60)
|
||||||
```
|
```
|
||||||
|
|
||||||
- Format string: "Time: MM:SS"
|
- Format string: "Time: MM:SS"
|
||||||
- Update Timer label
|
- Update Timer label
|
||||||
|
|
||||||
### 5. CREATE "HandleGameOver" FUNCTION:
|
### 5. CREATE "HandleGameOver" FUNCTION
|
||||||
|
|
||||||
**Inside:**
|
**Inside:**
|
||||||
|
|
||||||
- Set Timer text to "Game Over"
|
- Set Timer text to "Game Over"
|
||||||
|
|
||||||
### 6. CREATE "HandleGameClear" FUNCTION:
|
### 6. CREATE "HandleGameClear" FUNCTION
|
||||||
|
|
||||||
**Inside:**
|
**Inside:**
|
||||||
|
|
||||||
- Set Timer text to "Mission Complete"
|
- Set Timer text to "Mission Complete"
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- All 6 functions appear under Functions panel
|
- 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)
|
- Score updates instantly when enemies are killed (+50 each)
|
||||||
- Lives display updates when player is hit
|
- Lives display updates when player is hit
|
||||||
- Timer counts down in MM:SS format
|
- Timer counts down in MM:SS format
|
||||||
|
|||||||
@ -19,7 +19,8 @@
|
|||||||
- Default Pawn Class: Select `BP_Player`
|
- Default Pawn Class: Select `BP_Player`
|
||||||
- Player Controller Class: Keep default
|
- Player Controller Class: Keep default
|
||||||
|
|
||||||
### Expected Result after Compile:
|
### Expected Result after Compile
|
||||||
|
|
||||||
- Compile button shows GREEN checkmark
|
- Compile button shows GREEN checkmark
|
||||||
- Details panel shows "Default Pawn Class" set to BP_Player
|
- Details panel shows "Default Pawn Class" set to BP_Player
|
||||||
|
|
||||||
@ -33,7 +34,8 @@
|
|||||||
- Default GameMode: Select `BP_BulletHellGameMode`
|
- Default GameMode: Select `BP_BulletHellGameMode`
|
||||||
4. Close Project Settings
|
4. Close Project Settings
|
||||||
|
|
||||||
### Expected Result:
|
### Expected Result
|
||||||
|
|
||||||
- Project Settings shows BP_BulletHellGameMode as Default GameMode
|
- Project Settings shows BP_BulletHellGameMode as Default GameMode
|
||||||
- This means the game will automatically spawn BP_Player when Play is pressed
|
- This means the game will automatically spawn BP_Player when Play is pressed
|
||||||
|
|
||||||
@ -48,7 +50,8 @@
|
|||||||
5. Name: `BulletHellLevel`
|
5. Name: `BulletHellLevel`
|
||||||
6. Click Save
|
6. Click Save
|
||||||
|
|
||||||
### Expected Result:
|
### Expected Result
|
||||||
|
|
||||||
- New level file "BulletHellLevel" appears in Content folder
|
- New level file "BulletHellLevel" appears in Content folder
|
||||||
- Level is completely empty (black viewport)
|
- Level is completely empty (black viewport)
|
||||||
- Outliner shows only default actors (if any)
|
- Outliner shows only default actors (if any)
|
||||||
@ -59,7 +62,7 @@
|
|||||||
|
|
||||||
In the level (main viewport), we need to add game actors:
|
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)
|
1. In Place Actors panel (left side, or Window → Place Actors)
|
||||||
2. Search for "Camera Actor"
|
2. Search for "Camera Actor"
|
||||||
@ -73,44 +76,47 @@ In the level (main viewport), we need to add game actors:
|
|||||||
- Check the box, set Player Index to `0`
|
- Check the box, set Player Index to `0`
|
||||||
|
|
||||||
**For Orthographic View:**
|
**For Orthographic View:**
|
||||||
|
|
||||||
- Click on Camera component
|
- Click on Camera component
|
||||||
- In Details, set "Projection Mode" to `Orthographic`
|
- In Details, set "Projection Mode" to `Orthographic`
|
||||||
- Set "Ortho Width" to `1920` (or your screen width)
|
- Set "Ortho Width" to `1920` (or your screen width)
|
||||||
|
|
||||||
### 2. ADD PLAYER:
|
### 2. ADD PLAYER
|
||||||
|
|
||||||
1. From Content Browser, drag `BP_Player` into level
|
1. From Content Browser, drag `BP_Player` into level
|
||||||
2. Position: `X=0, Y=-300, Z=0` (bottom center)
|
2. Position: `X=0, Y=-300, Z=0` (bottom center)
|
||||||
|
|
||||||
### 3. ADD ENEMY SPAWNER:
|
### 3. ADD ENEMY SPAWNER
|
||||||
|
|
||||||
1. Drag `BP_EnemySpawner` into level
|
1. Drag `BP_EnemySpawner` into level
|
||||||
2. Position: `X=0, Y=500, Z=0` (top of screen)
|
2. Position: `X=0, Y=500, Z=0` (top of screen)
|
||||||
3. In Details panel, set EnemyClass to `BP_Enemy`
|
3. In Details panel, set EnemyClass to `BP_Enemy`
|
||||||
|
|
||||||
### 4. ADD GAME DIRECTOR:
|
### 4. ADD GAME DIRECTOR
|
||||||
|
|
||||||
1. Drag `BP_GameDirector` into level
|
1. Drag `BP_GameDirector` into level
|
||||||
2. Position doesn't matter (it's invisible)
|
2. Position doesn't matter (it's invisible)
|
||||||
|
|
||||||
### 5. ADD SCORE MANAGER:
|
### 5. ADD SCORE MANAGER
|
||||||
|
|
||||||
1. Drag `BP_ScoreManager` into level
|
1. Drag `BP_ScoreManager` into level
|
||||||
2. Position doesn't matter
|
2. Position doesn't matter
|
||||||
|
|
||||||
### 6. Save the level (`Ctrl+S`)
|
### 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)
|
- Camera actor visible at top of scene (Z=1000)
|
||||||
- BP_Player visible at bottom center (Y=-300)
|
- BP_Player visible at bottom center (Y=-300)
|
||||||
- BP_EnemySpawner visible at top (Y=500)
|
- BP_EnemySpawner visible at top (Y=500)
|
||||||
- BP_GameDirector and BP_ScoreManager in Outliner (invisible actors)
|
- BP_GameDirector and BP_ScoreManager in Outliner (invisible actors)
|
||||||
|
|
||||||
### Expected Result in Outliner:
|
### Expected Result in Outliner
|
||||||
|
|
||||||
```
|
```
|
||||||
- CameraActor
|
- CameraActor
|
||||||
- BP_Player
|
- BP_Player
|
||||||
- BP_EnemySpawner
|
- BP_EnemySpawner
|
||||||
- BP_GameDirector
|
- BP_GameDirector
|
||||||
- BP_ScoreManager
|
- BP_ScoreManager
|
||||||
```
|
```
|
||||||
@ -125,7 +131,8 @@ In the level (main viewport), we need to add game actors:
|
|||||||
- Editor Startup Map: Select `BulletHellLevel`
|
- Editor Startup Map: Select `BulletHellLevel`
|
||||||
- Game Default Map: Select `BulletHellLevel`
|
- Game Default Map: Select `BulletHellLevel`
|
||||||
|
|
||||||
### Expected Result:
|
### Expected Result
|
||||||
|
|
||||||
- Project Settings shows BulletHellLevel as both startup and default map
|
- Project Settings shows BulletHellLevel as both startup and default map
|
||||||
- Launching the game (standalone or in editor) loads this level automatically
|
- Launching the game (standalone or in editor) loads this level automatically
|
||||||
|
|
||||||
|
|||||||
@ -6,26 +6,31 @@
|
|||||||
|
|
||||||
## Step 9.1: Assign Blueprint References
|
## Step 9.1: Assign Blueprint References
|
||||||
|
|
||||||
### 1. Open BP_Player:
|
### 1. Open BP_Player
|
||||||
|
|
||||||
- In Details panel (with blueprint open)
|
- In Details panel (with blueprint open)
|
||||||
- Set BulletClass: `BP_Bullet`
|
- Set BulletClass: `BP_Bullet`
|
||||||
|
|
||||||
### 2. Open BP_EnemySpawner:
|
### 2. Open BP_EnemySpawner
|
||||||
|
|
||||||
- Set EnemyClass: `BP_Enemy`
|
- Set EnemyClass: `BP_Enemy`
|
||||||
|
|
||||||
### 3. Open BP_Enemy:
|
### 3. Open BP_Enemy
|
||||||
|
|
||||||
- Set BulletClass: `BP_Bullet`
|
- Set BulletClass: `BP_Bullet`
|
||||||
|
|
||||||
### 4. Compile and Save all blueprints
|
### 4. Compile and Save all blueprints
|
||||||
|
|
||||||
### Expected Result after Compile (all blueprints):
|
### Expected Result after Compile (all blueprints)
|
||||||
|
|
||||||
- All blueprints compile with GREEN checkmark
|
- All blueprints compile with GREEN checkmark
|
||||||
- No "None" or missing references in variable defaults
|
- No "None" or missing references in variable defaults
|
||||||
- BP_Player.BulletClass → BP_Bullet
|
- BP_Player.BulletClass → BP_Bullet
|
||||||
- BP_Enemy.BulletClass → BP_Bullet
|
- BP_Enemy.BulletClass → BP_Bullet
|
||||||
- BP_EnemySpawner.EnemyClass → BP_Enemy
|
- BP_EnemySpawner.EnemyClass → BP_Enemy
|
||||||
|
|
||||||
### Expected Result in Play mode:
|
### Expected Result in Play mode
|
||||||
|
|
||||||
- Player can shoot bullets (BP_Bullet spawns)
|
- Player can shoot bullets (BP_Bullet spawns)
|
||||||
- Enemies spawn and shoot bullets
|
- Enemies spawn and shoot bullets
|
||||||
- All collision/damage systems functional
|
- All collision/damage systems functional
|
||||||
@ -36,7 +41,7 @@
|
|||||||
|
|
||||||
Now replace the temporary cube visuals with proper colored materials:
|
Now replace the temporary cube visuals with proper colored materials:
|
||||||
|
|
||||||
### 1. REMOVE TEMPORARY COMPONENTS:
|
### 1. REMOVE TEMPORARY COMPONENTS
|
||||||
|
|
||||||
1. Open BP_Player blueprint
|
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))
|
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
|
### 2. Content Browser → Materials folder
|
||||||
|
|
||||||
### 3. PLAYER MATERIAL:
|
### 3. PLAYER MATERIAL
|
||||||
|
|
||||||
1. Right-click → Material
|
1. Right-click → Material
|
||||||
2. Name: `M_Player`
|
2. Name: `M_Player`
|
||||||
@ -56,19 +61,21 @@ Now replace the temporary cube visuals with proper colored materials:
|
|||||||
6. Connect to Base Color
|
6. Connect to Base Color
|
||||||
7. Save and Close
|
7. Save and Close
|
||||||
|
|
||||||
### 4. BULLET MATERIALS:
|
### 4. BULLET MATERIALS
|
||||||
|
|
||||||
- Create `M_PlayerBullet` - Yellow `(1, 1, 0)`
|
- Create `M_PlayerBullet` - Yellow `(1, 1, 0)`
|
||||||
- Create `M_EnemyBullet` - Red `(1, 0, 0)`
|
- Create `M_EnemyBullet` - Red `(1, 0, 0)`
|
||||||
|
|
||||||
### 5. ENEMY MATERIAL:
|
### 5. ENEMY MATERIAL
|
||||||
|
|
||||||
- Create `M_Enemy` - Magenta `(1, 0, 1)`
|
- Create `M_Enemy` - Magenta `(1, 0, 1)`
|
||||||
|
|
||||||
### 6. Apply materials to sprite components in each Blueprint
|
### 6. Apply materials to sprite components in each Blueprint
|
||||||
|
|
||||||
(Or use Sprite assets if you have 2D images)
|
(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 visible as blue shape
|
||||||
- Player bullets visible as yellow shapes
|
- Player bullets visible as yellow shapes
|
||||||
- Enemy bullets visible as red 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)
|
## Step 9.3: Add Background (Optional)
|
||||||
|
|
||||||
### 1. In level, add a Plane mesh:
|
### 1. In level, add a Plane mesh
|
||||||
|
|
||||||
- Place Actors → Basic → Plane
|
- Place Actors → Basic → Plane
|
||||||
- Scale: `X=20, Y=30, Z=1`
|
- Scale: `X=20, Y=30, Z=1`
|
||||||
- Position: `X=0, Y=0, Z=-100` (behind everything)
|
- Position: `X=0, Y=0, Z=-100` (behind everything)
|
||||||
|
|
||||||
### 2. Create dark space material:
|
### 2. Create dark space material
|
||||||
|
|
||||||
- `M_Background` - Dark blue/black
|
- `M_Background` - Dark blue/black
|
||||||
|
|
||||||
### Expected Result in Play mode:
|
### Expected Result in Play mode
|
||||||
|
|
||||||
- Dark background visible behind all game elements
|
- Dark background visible behind all game elements
|
||||||
- Game elements (player, enemies, bullets) clearly visible against background
|
- Game elements (player, enemies, bullets) clearly visible against background
|
||||||
- Background doesn't interfere with gameplay (Z=-100, behind everything)
|
- Background doesn't interfere with gameplay (Z=-100, behind everything)
|
||||||
@ -97,31 +107,33 @@ Now replace the temporary cube visuals with proper colored materials:
|
|||||||
## Step 9.4: Test the Game
|
## Step 9.4: Test the Game
|
||||||
|
|
||||||
### 1. Click "Play" button (green arrow in main toolbar)
|
### 1. Click "Play" button (green arrow in main toolbar)
|
||||||
|
|
||||||
OR press `Alt+P`
|
OR press `Alt+P`
|
||||||
|
|
||||||
### 2. TEST CHECKLIST:
|
### 2. TEST CHECKLIST
|
||||||
|
|
||||||
| # | Test | Pass? |
|
| # | Test | Pass? |
|
||||||
|---|------|-------|
|
| --- | --------------------------------------------- | ----- |
|
||||||
| 1 | Player moves with WASD or Arrow keys | ☐ |
|
| 1 | Player moves with WASD or Arrow keys | ☐ |
|
||||||
| 2 | Player stays within screen bounds | ☐ |
|
| 2 | Player stays within screen bounds | ☐ |
|
||||||
| 3 | Player shoots with Z key or Left Mouse | ☐ |
|
| 3 | Player shoots with Z key or Left Mouse | ☐ |
|
||||||
| 4 | Bullets travel upward | ☐ |
|
| 4 | Bullets travel upward | ☐ |
|
||||||
| 5 | Enemies spawn at top of screen | ☐ |
|
| 5 | Enemies spawn at top of screen | ☐ |
|
||||||
| 6 | Enemies move down with wavy motion | ☐ |
|
| 6 | Enemies move down with wavy motion | ☐ |
|
||||||
| 7 | Enemies shoot radial bullet patterns | ☐ |
|
| 7 | Enemies shoot radial bullet patterns | ☐ |
|
||||||
| 8 | Player bullets damage enemies | ☐ |
|
| 8 | Player bullets damage enemies | ☐ |
|
||||||
| 9 | Enemy bullets damage player | ☐ |
|
| 9 | Enemy bullets damage player | ☐ |
|
||||||
| 10 | Score increases when enemies die | ☐ |
|
| 10 | Score increases when enemies die | ☐ |
|
||||||
| 11 | Lives decrease when player is hit | ☐ |
|
| 11 | Lives decrease when player is hit | ☐ |
|
||||||
| 12 | Timer counts down from 5:00 | ☐ |
|
| 12 | Timer counts down from 5:00 | ☐ |
|
||||||
| 13 | Game shows "Game Over" when lives = 0 | ☐ |
|
| 13 | Game shows "Game Over" when lives = 0 | ☐ |
|
||||||
| 14 | Game shows "Mission Complete" after 5 minutes | ☐ |
|
| 14 | Game shows "Mission Complete" after 5 minutes | ☐ |
|
||||||
| 15 | Special ability (X key) clears screen | ☐ |
|
| 15 | Special ability (X key) clears screen | ☐ |
|
||||||
|
|
||||||
### 3. To stop playing: Press `ESC` or click "Stop" button
|
### 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
|
- All checklist items above should pass
|
||||||
- Frame rate stable (60+ FPS recommended)
|
- Frame rate stable (60+ FPS recommended)
|
||||||
- No crashes or Blueprint errors in Output Log
|
- No crashes or Blueprint errors in Output Log
|
||||||
@ -137,7 +149,8 @@ OR press `Alt+P`
|
|||||||
4. Navigate to output folder → WindowsNoEditor → [ProjectName].exe
|
4. Navigate to output folder → WindowsNoEditor → [ProjectName].exe
|
||||||
5. Run the executable to play standalone
|
5. Run the executable to play standalone
|
||||||
|
|
||||||
### Expected Result:
|
### Expected Result
|
||||||
|
|
||||||
- Build completes without errors (check Output Log)
|
- Build completes without errors (check Output Log)
|
||||||
- Executable file created in output folder
|
- Executable file created in output folder
|
||||||
- Running .exe launches the game in fullscreen
|
- 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!
|
You have completed the Unreal Engine Bullet Hell tutorial!
|
||||||
|
|
||||||
Your game includes:
|
Your game includes:
|
||||||
|
|
||||||
- ✅ Player with 3 lives, WASD movement, Z/mouse shooting
|
- ✅ Player with 3 lives, WASD movement, Z/mouse shooting
|
||||||
- ✅ Volley shooting (3 bullets in spread pattern)
|
- ✅ Volley shooting (3 bullets in spread pattern)
|
||||||
- ✅ Screen-clear special ability (X key, one use)
|
- ✅ 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 A: Complete Variable Reference](appendix-a-variables.md)
|
||||||
- [Appendix B: Troubleshooting](appendix-b-troubleshooting.md)
|
- [Appendix B: Troubleshooting](appendix-b-troubleshooting.md)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user