# Part 3 (C++): Create the Bullet

[← Previous: Part 2 (C++) - Create the Player](part-2-cpp-create-player.md) | [Back to Index](README.md) | [Next: Part 4 (C++) - Create the Enemy →](part-4-cpp-create-enemy.md)

---

## Overview

Create the bullet/projectile class in C++. Again, we'll copy-paste all variables instead of clicking UI.

**Time comparison:**
- Blueprint: ~30 minutes (5 variables + Blueprint nodes)
- C++ (this part): ~10 minutes (copy-paste code)

---

## Step 3.1: Create STGProjectile C++ Class

1. **Tools → New C++ Class**
2. Choose **"Actor"** as parent class → Next
3. Name: `STGProjectile` → Create Class
4. Wait for compilation

---

## Step 3.2: Copy-Paste STGProjectile.h

Replace the entire header file with:

> **⚠️ IMPORTANT:** Replace `YOURPROJECTNAME_API` with your actual project's API macro (e.g., `BULLETHELLCPP_API`).

```cpp
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "STGProjectile.generated.h"

class USphereComponent;
class UProjectileMovementComponent;
class UStaticMeshComponent;

UCLASS()
class YOURPROJECTNAME_API ASTGProjectile : public AActor
{
    GENERATED_BODY()
    
public:    
    ASTGProjectile();

protected:
    virtual void BeginPlay() override;

public:    
    virtual void Tick(float DeltaTime) override;

    // ===== COMPONENTS =====
    UPROPERTY(VisibleDefaultsOnly, Category=Projectile)
    USphereComponent* CollisionComp;

    UPROPERTY(VisibleDefaultsOnly, Category=Projectile)
    UStaticMeshComponent* MeshComp;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Movement)
    UProjectileMovementComponent* ProjectileMovement;

    // ===== VARIABLES (all with defaults!) =====
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gameplay")
    bool bIsPlayerBullet = false;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gameplay")
    float Damage = 1.0f;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gameplay")
    FLinearColor BulletColor = FLinearColor::Green;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gameplay")
    float Lifetime = 4.0f;

    // ===== FUNCTIONS =====
    UFUNCTION()
    void OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, 
                        UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, 
                        bool bFromSweep, const FHitResult& SweepResult);

    void SetBulletColor(FLinearColor InColor);
    void SetSpeed(float InSpeed);

private:
    UMaterialInstanceDynamic* DynamicMaterial;
};
```

**What just happened:** 5 bullet variables defined with defaults in 10 seconds!

---

## Step 3.3: Copy-Paste STGProjectile.cpp

Replace the implementation file with:

```cpp
#include "STGProjectile.h"
#include "Components/SphereComponent.h"
#include "Components/StaticMeshComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "STGPawn.h"

ASTGProjectile::ASTGProjectile()
{
    PrimaryActorTick.bCanEverTick = true;

    // Collision sphere
    CollisionComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComp"));
    CollisionComp->InitSphereRadius(5.0f);
    CollisionComp->SetCollisionProfileName("OverlapAllDynamic");
    CollisionComp->OnComponentBeginOverlap.AddDynamic(this, &ASTGProjectile::OnOverlapBegin);
    RootComponent = CollisionComp;

    // Visual mesh
    MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComp"));
    MeshComp->SetupAttachment(CollisionComp);
    MeshComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereMesh(TEXT("/Engine/BasicShapes/Sphere"));
    if (SphereMesh.Succeeded())
    {
        MeshComp->SetStaticMesh(SphereMesh.Object);
        MeshComp->SetRelativeScale3D(FVector(0.1f, 0.1f, 0.1f));
    }

    // Projectile movement
    ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileComp"));
    ProjectileMovement->UpdatedComponent = CollisionComp;
    ProjectileMovement->InitialSpeed = 1200.f;
    ProjectileMovement->MaxSpeed = 1200.f;
    ProjectileMovement->bRotationFollowsVelocity = true;
    ProjectileMovement->bShouldBounce = false;
    ProjectileMovement->ProjectileGravityScale = 0.0f;  // No gravity for top-down shooter!
}

void ASTGProjectile::BeginPlay()
{
    Super::BeginPlay();

    // Set lifetime here so Blueprint overrides of Lifetime variable work
    SetLifeSpan(Lifetime);

    // Create dynamic material for bullet color
    if (MeshComp)
    {
        DynamicMaterial = MeshComp->CreateAndSetMaterialInstanceDynamic(0);
        if (DynamicMaterial)
        {
            DynamicMaterial->SetVectorParameterValue(TEXT("BaseColor"), BulletColor);
            DynamicMaterial->SetVectorParameterValue(TEXT("EmissiveColor"), BulletColor * 3.0f);
        }
    }
}

void ASTGProjectile::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}

void ASTGProjectile::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, 
                                     UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, 
                                     bool bFromSweep, const FHitResult& SweepResult)
{
    if (OtherActor && OtherActor != this)
    {
        if (bIsPlayerBullet)
        {
            // Player bullet hits enemy (will implement in Part 4)
            // ASTGEnemy* Enemy = Cast<ASTGEnemy>(OtherActor);
            // if (Enemy) { Enemy->HandleDamage(Damage); Destroy(); }
        }
        else
        {
            // Enemy bullet hits player
            ASTGPawn* Player = Cast<ASTGPawn>(OtherActor);
            if (Player)
            {
                Player->TakeHit(1);
                Destroy();
            }
        }
    }
}

void ASTGProjectile::SetBulletColor(FLinearColor InColor)
{
    BulletColor = InColor;
    if (DynamicMaterial)
    {
        DynamicMaterial->SetVectorParameterValue(TEXT("BaseColor"), BulletColor);
        DynamicMaterial->SetVectorParameterValue(TEXT("EmissiveColor"), BulletColor * 3.0f);
    }
}

void ASTGProjectile::SetSpeed(float InSpeed)
{
    if (ProjectileMovement)
    {
        ProjectileMovement->InitialSpeed = InSpeed;
        ProjectileMovement->MaxSpeed = InSpeed;
    }
}
```

---

## Step 3.4: Compile

1. Click **Compile** in Unreal Editor
2. Wait for compilation
3. Check for errors

---

## Step 3.5: Update Player Firing Logic

Now that we have bullets, let's make the player actually spawn them!

### Open STGPawn.cpp

Find the `FireShot()` function and replace it with:

```cpp
void ASTGPawn::FireShot()
{
    // Spawn volley of bullets shooting FORWARD (+X direction = toward top of screen)
    for (int32 i = 0; i < VolleySize; i++)
    {
        // Spawn slightly in front of player (+X)
        FVector SpawnLocation = GetActorLocation() + FVector(50.f, 0.f, 0.f);
        
        // Base rotation: FRotator(0,0,0) points in +X direction (forward in top-down view)
        FRotator SpawnRotation = FRotator::ZeroRotator;

        // Spread calculation: fan out on Yaw (left/right)
        float Angle = VolleySpread * (i - (VolleySize - 1) / 2.0f);
        SpawnRotation.Yaw += Angle;

        // Spawn bullet
        UWorld* World = GetWorld();
        if (World)
        {
            ASTGProjectile* Bullet = World->SpawnActor<ASTGProjectile>(
                ASTGProjectile::StaticClass(), 
                SpawnLocation, 
                SpawnRotation
            );
            
            if (Bullet)
            {
                Bullet->bIsPlayerBullet = true;
                Bullet->SetSpeed(BulletSpeed);
                Bullet->SetBulletColor(FLinearColor::Green);
            }
        }
    }
}
```

### Add include at top of STGPawn.cpp:

```cpp
#include "STGProjectile.h"  // Add this line
```

### Compile again!

---

## Step 3.6: Test Bullet Firing

1. Press **Play** (`Alt+P`)
2. Move with WASD
3. Press Space to fire
4. You should see spheres shooting toward the top of the screen!

### Expected Result

- ✅ Player fires 3 bullets in a spread pattern
- ✅ Bullets travel toward the top of the screen (forward direction)
- ✅ Bullets auto-destroy after 4 seconds
- ✅ Can hold Space to auto-fire

> **Note:** Bullets use the default engine material (gray/white). We'll add colored materials in Part 9 (Polish phase).

---

## Comparison Summary

### Blueprint Approach:

- Create BP_Bullet Blueprint
- Add 5 variables manually (×5 clicks each = 25 clicks)
- Create Blueprint nodes for movement (~15 nodes)
- Create Blueprint nodes for collision (~10 nodes)
- Update BP_Player firing logic (~15 more nodes)
- **Total: ~30 minutes**

### C++ Approach:

- Create STGProjectile class
- Copy-paste header (5 variables with defaults)
- Copy-paste implementation
- Update one function in STGPawn
- Compile twice
- **Total: ~10 minutes**

**Time saved: 20 minutes** ⚡

---

## What's Next?

In Part 4, we'll create enemies with C++. You'll see how easy it is to copy enemy patterns!

---

[← Previous: Part 2 (C++) - Create the Player](part-2-cpp-create-player.md) | [Back to Index](README.md) | [Next: Part 4 (C++) - Create the Enemy →](part-4-cpp-create-enemy.md)
