EnemyProjectile: Creating a Homing Projectile
In this article, we will create a C++ base class to represent the Homing projectiles that are fired by enemy cannons.
In the Content Browser, access the TutoPart3 folder that is inside the C++ Classes folder. Right-click on free space and choose the New C++ Class… option:
On the next screen, choose the Actor class as the parent class and click the Next button.
In the Name field, write EnemyProjectile. In the Path field, keep the default project folder. Click the Create Class button.
The EnemyProjectile class will use four types of components:
- USphereComponent: It is used as the Root component and for collision testing.
- UStaticMeshComponent: Contains the Static Mesh that will visually represent the projectile.
- UParticleSystemComponent: Component with a particle emitter that will be used to simulate a fire effect on the projectile.
- UProjectileMovementComponent: Component used to move the projectile. It will be configured to follow the player.
Add the declaration of the components in the EnemyProjectile.h file that will look like this:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "EnemyProjectile.generated.h"
UCLASS()
class TUTOPART3_API AEnemyProjectile : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AEnemyProjectile();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
UStaticMeshComponent* StaticMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
class USphereComponent* CollisionComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
class UParticleSystemComponent* Particles;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
class UProjectileMovementComponent* ProjectileMovement;
};
In the EnemyProjectile.cpp file, we have the initialization of the components done in the constructor. Don’t forget to add the #include lines for the components.
#include "EnemyProjectile.h"
#include "Engine/CollisionProfile.h"
#include "Components/SphereComponent.h"
#include "Particles/ParticleSystemComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Kismet/GameplayStatics.h"
// Sets default values
AEnemyProjectile::AEnemyProjectile()
{
PrimaryActorTick.bCanEverTick = true;
InitialLifeSpan = 10.f;
CollisionComponent = CreateDefaultSubobject<USphereComponent>(
TEXT("CollisionComponent"));
RootComponent = CollisionComponent;
CollisionComponent->InitSphereRadius(25.f);
CollisionComponent->SetCollisionProfileName(
UCollisionProfile::BlockAllDynamic_ProfileName);
StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMesh->SetupAttachment(CollisionComponent);
Particles = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("Particles"));
Particles->SetupAttachment(StaticMesh);
ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(
TEXT("ProjectileMovement"));
ProjectileMovement->InitialSpeed = 300.f;
ProjectileMovement->MaxSpeed = 300.f;
ProjectileMovement->bRotationFollowsVelocity = true;
ProjectileMovement->bIsHomingProjectile = true;
ProjectileMovement->HomingAccelerationMagnitude = 300.f;
ProjectileMovement->ProjectileGravityScale = 0.f;
}
// Called when the game starts or when spawned
void AEnemyProjectile::BeginPlay()
{
Super::BeginPlay();
APawn* PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
if(PlayerPawn)
{
ProjectileMovement->HomingTargetComponent = PlayerPawn->GetRootComponent();
}
}
// Called every frame
void AEnemyProjectile::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
I did the following steps to configure the UProjectileMovementComponent as a Homing projectile:
- I assigned true to the variable bRotationFollowsVelocity so that the projectile rotates when it changes direction.
- I assigned true to the variable bIsHomingProjectile to indicate that this is a Homing Projectile.
- I assigned a value for the HomingAccelerationMagnitude variable that indicates the acceleration that the projectile will move towards the target. I used the same value as the velocity of the projectile.
- I removed the force of gravity from the projectile by assigning a value of 0 to the ProjectileGravityScale variable.
- The last step is to assign a USceneComponent to the variable HomingTargetComponent that will be used as a target. This is done in the BeginPlay() function. The target is the player’s RootComponent.
Compile the C++ code. The Static Mesh and Particle System Assets will be selected in the child Blueprints. Besides that, the initial values assigned to the components can be modified in child Blueprints.