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