praca_magisterska/games/unreal/tutorial/part-4-create-enemy.md

17 KiB

Part 4: Create the Enemy

← Previous: Part 3 - Create the Bullet | Back to Index | Next: Part 5 - Create Enemy Spawner →


Step 4.1: Create Enemy Blueprint

  1. Content Browser → Blueprints

  2. Right-click → Blueprint ClassActor

  3. Name: BP_Enemy

  4. Double-click to open

  5. Add Components:

    • Paper Sprite → EnemySprite
    • Box Collision → EnemyCollision
      • Box Extent: X=30, Y=30, Z=10
      • Generate Overlap Events: CHECKED
    • Cube (Static Mesh) → name TempVisual
      • Scale: (0.6, 0.6, 0.1) - larger than player cube
      • This provides visibility until proper visuals in Part 9
  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)

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):

  • Cube visible (the TempVisual placeholder, larger than player)
  • Box collision visible (30x30x10)

Step 4.2: Enemy Initialization

  1. In Event Graph, from "Event BeginPlay":

a) Set CurrentHealth = MaxHealth

b) Get Actor Location → Break Vector → Set BaseX = X value

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:

  • Compile button shows GREEN checkmark
  • BeginPlay event connected to variable setters

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

Step 4.3: Enemy Movement Logic

  1. In Event Graph, from "Event Tick":

a) VERTICAL MOVEMENT:

  • Get Actor Location → Break into X, Y, Z
  • Subtract (VerticalSpeed * DeltaSeconds) from Y

b) HORIZONTAL SINE WAVE:

  • Get Game Time in Seconds
  • Add WaveSeed
  • Multiply by HorizontalFrequency
  • Calculate Sine of result
  • Multiply by HorizontalAmplitude
  • Add to BaseX
  • This becomes new X position

c) Set Actor Location with new X, Y (Z stays 0)

d) DESPAWN CHECK:

  • If Y < DespawnY: Destroy Actor
  • If Abs(X) > 1400: Destroy Actor

Visual Diagram:

┌─────────────┐
│ Event Tick  │
└──────┬──────┘
       │
       ▼
┌─────────────────────────────────────────────┐
│  Get Location                                │
│  NewY = Y - (VerticalSpeed * DeltaSeconds)   │
│  NewX = BaseX + Sin(Time * Freq) * Amplitude │
│  Set Location (NewX, NewY, 0)                │
└──────────────────────────────────────────────┘

Expected Result after Compile:

  • Compile button shows GREEN checkmark
  • Event Tick connected to movement and despawn logic

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)
  • Enemies disappear when Y < -750 or |X| > 1400

Step 4.4: Enemy Firing Logic

1. Continue in Event Tick (after movement):

a) Decrease FireTimer by DeltaSeconds

b) Branch: if FireTimer <= 0:

  • Reset FireTimer to FireInterval
  • Call "FireBurst" function

2. CREATE "FireBurst" FUNCTION:

a) Add function "FireBurst"

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:

  • Calculate angle: i * AngleStep
  • Convert to direction vector:
    • X = Sin(angle in radians)
    • Y = -Cos(angle in radians) ← negative because enemies fire DOWN
  • Spawn bullet:
    • Spawn Actor from Class (BP_Bullet)
    • Get spawned bullet, call Initialize:
      • Direction = calculated direction
      • Speed = EnemyBulletSpeed
      • bIsEnemy = true
      • Lifetime = EnemyBulletLifetime

Visual: Radial bullet pattern (20 bullets in 360°)

                ↑
             ↗  |  ↖
          ↗    |    ↖
       →───────●───────←
          ↘    |    ↙
             ↘  |  ↙
                ↓

Expected Result after Compile:

  • Compile button shows GREEN checkmark
  • "FireBurst" function appears under Functions

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
  • Different enemies fire at different times (randomized FireTimer)

Step 4.5: Enemy Damage and Death

1. CREATE "ApplyDamage" FUNCTION:

a) Add input: DamageAmount (Integer)

b) Inside:

  • Subtract DamageAmount from CurrentHealth
  • If CurrentHealth <= 0:
    • Call HandleDeath

2. CREATE "HandleDeath" FUNCTION:

a) Inside:

  • Get reference to ScoreManager (we'll create in Part 7)
  • Call AddScore, passing ScoreValue
  • Spawn death effect (optional)
  • Destroy Actor

3. COLLISION WITH PLAYER:

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:

  • Compile button shows GREEN checkmark
  • "ApplyDamage" and "HandleDeath" functions appear under Functions
  • EnemyCollision has overlap event in Event Graph

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

Step 4.6: Complete Special Ability in BP_Player

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.

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:

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:

  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:

  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):

  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):

  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:

  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:

┌─────────────────────────────┐
│ EnhancedInputAction         │
│ IA_Special - Triggered      │
└───────────┬─────────────────┘
            │
            ▼
┌─────────────────────────────┐
│ Branch                      │
│ Condition: NOT SpecialUsed  │
└───────┬─────────────┬───────┘
        │ TRUE        │ FALSE
        ▼             ▼
┌───────────────┐   (nothing - ability already used)
│Set SpecialUsed│
│   = true      │
└───────┬───────┘
        │
        ▼
┌────────────────────────────┐
│ Get All Actors of Class    │
│ Actor Class: BP_Enemy      │
│ Out Actors ○───────────────┼──────────┐
└───────────┬────────────────┘          │
            │                           │
            ▼                           ▼
┌────────────────────────────┐   ┌─────────────┐
│ For Each Loop              │──►│Destroy Actor│
│   Array ○──────────────────┼───│Target: Enemy│
│                            │   └─────────────┘
│   Loop Body ───────────────┼───────────┘
│   Completed ───────────────┼───┐
└────────────────────────────┘   │
                                 │
            ┌────────────────────┘
            ▼
┌────────────────────────────┐
│ Get All Actors of Class    │
│ Actor Class: BP_Bullet     │
│ Out Actors ○───────────────┼──────────┐
└───────────┬────────────────┘          │
            │                           │
            ▼                           ▼
┌────────────────────────────┐   ┌─────────────────────────────────┐
│ For Each Loop              │──►│ Get IsEnemyProjectile           │
│   Array ○──────────────────┼───│   (from Array Element)          │
│                            │   └───────────────┬─────────────────┘
│   Loop Body ───────────────┼───────────────────┘
└────────────────────────────┘          │
                                        ▼
                              ┌─────────────────────────────┐
                              │ Branch                      │
                              │ Condition: IsEnemyProjectile│
                              └───────┬─────────────┬───────┘
                                      │ TRUE        │ FALSE
                                      ▼             ▼
                              ┌───────────────┐   (skip - player bullet)
                              │ Destroy Actor │
                              │ Target: Bullet│
                              └───────────────┘

4. Compile and Save

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:

  • Press X (or Right Mouse Button) once:
    • ALL enemies on screen instantly disappear
    • ALL enemy bullets (red) instantly disappear
    • Player bullets (yellow) remain unaffected
    • "SPECIAL ABILITY ACTIVATED!" no longer prints (we deleted it)
  • Press X again: Nothing happens (SpecialUsed = true, one use only)

Step 4.7: Complete Bullet Collision Logic (BP_Bullet)

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.

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:

a) Delete the placeholder:

  • Select the Print String node
  • Press Delete

b) Add Cast to BP_Enemy:

  1. Right-click → search Cast to BP_Enemy → add it

  2. Connect execution wire:

    • From Branch FALSE pin → Cast to BP_Enemy
  3. Connect the "Other Actor" to the cast:

    • 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:

  1. 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
  2. Connect the Damage parameter:

    • Right-click → Get Damage (the bullet's damage variable)
    • Connect to ApplyDamage's "DamageAmount" input

d) Destroy the bullet after damaging:

  1. From ApplyDamage, drag execution → Destroy Actor
    • Leave "Target" as "Self" (destroys this bullet)

Visual:

                                  FALSE
                                    │
                                    ▼
                         ┌──────────────────────┐
  Other Actor ──────────►│ Cast to BP_Enemy     │
                         └──────────┬───────────┘
                                    │
                           Cast Succeeded
                                    │
                                    ▼
                         ┌──────────────────────┐
                         │ ApplyDamage          │
  Get Damage ───────────►│   DamageAmount       │
                         └──────────┬───────────┘
                                    │
                                    ▼
                         ┌──────────────────────┐
                         │ Destroy Actor        │
                         │ (Self)               │
                         └──────────────────────┘

3. Compile and Save

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:

  • Player bullets (IsEnemyProjectile=false) hitting enemies:
    • Enemy takes damage, bullet disappears
    • After enough hits, enemy dies and awards score
  • Enemy bullets (IsEnemyProjectile=true) hitting player:
    • Player takes hit, bullet disappears
  • Bullets passing through other objects: No effect (bullets ignore non-targets)

← Previous: Part 3 - Create the Bullet | Back to Index | Next: Part 5 - Create Enemy Spawner →