Add Radial Impulse to Actor
Github Link: https://github.com/Harrison1/unrealcpp/tree/master/AddRadialForce
For this tutorial we are using the standard first person C++ template with starter content. If you don't know how to add a new actor class to your project, please visit the Add C++ Actor Class post.
This tutorial is super fun, we are going to simulate a explosion by adding radial impulse to all objects within a set range.
Create a new actor called AddRadialForce
. We don't have to do anything to the header file. Below is the default header file generated by Unreal.
AddRadialForce.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "AddRadialForce.generated.h"
UCLASS()
class UNREALCPP_API AAddRadialForce : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AAddRadialForce();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
To help us during the debugging process, let's add the DrawDebugHelpers
script to our code.
include DrawDebugHelpers.h
#include "AddRadialForce.h"
// add debug helpfers
#include "DrawDebugHelpers.h"
In this example we are going to perform all of our logic inside the BeginPlay()
function. We want to collect all the hit results that are within our range and get the results from our range's sweep. To do this we are going to use TArrays track our overlapping actors.
add hit result and sweep actors TArrays
void AAddRadialForce::BeginPlay()
{
Super::BeginPlay();
// create tarray for hit results
TArray<FHitResult> OutHits;
// crate tarray for sweep actors
TArray<AActor*> SweepActors;
}
Next we're going to declare our OutHits
TArray. We want the sweep range to start and end at this actor's position and have the CollisionShape
be a sphere of 500 units. You get the actor's location by using GetActorLocation()
which returns a vector. We create a CollisionShape
by using FCollisionShape::MakeSphere(500.0f)
which returns a FCollisionShape
.
add sweep variables and shape
// Called when the game starts or when spawned
void AAddRadialForce::BeginPlay()
{
Super::BeginPlay();
// create tarray for hit results
TArray<FHitResult> OutHits;
// get actor locations
FVector MyLocation = GetActorLocation();
// start and end locations. The sphere will create the radial sweep.
FVector Start = MyLocation;
FVector End = MyLocation;
// create a collision sphere
FCollisionShape MyColSphere = FCollisionShape::MakeSphere(500.0f);
}
To visualize the sphere of the sweep we will draw a debug sphere.
DrawDebugSphere
// Called when the game starts or when spawned
void AAddRadialForce::BeginPlay()
{
Super::BeginPlay();
// create tarray for hit results
TArray<FHitResult> OutHits;
// get actor locations
FVector MyLocation = GetActorLocation();
// start and end locations. The sphere will create the radial sweep.
FVector Start = MyLocation;
FVector End = MyLocation;
// create a collision sphere
FCollisionShape MyColSphere = FCollisionShape::MakeSphere(500.0f);
// draw collision sphere
DrawDebugSphere(GetWorld(), GetActorLocation(), MyColSphere.GetSphereRadius(), 50, FColor::Cyan, true);
}
Next we have to check if our actor hits anything when we BeginPlay()
. Every actor has the GetWorld
function. From the GetWorld()
function we will SweepMultiByChannel
and set the parameters to our variables we created above. This will return
a bool
indicating if actors are within range of this actor. Here is Unreal's documentation on SweepMultiByChannel.
void AAddRadialForce::BeginPlay()
{
Super::BeginPlay();
// create tarray for hit results
TArray<FHitResult> OutHits;
// get actor locations
FVector MyLocation = GetActorLocation();
// start and end locations. The sphere will create the radial sweep.
FVector Start = MyLocation;
FVector End = MyLocation;
// create a collision sphere
FCollisionShape MyColSphere = FCollisionShape::MakeSphere(500.0f);
// draw collision sphere
DrawDebugSphere(GetWorld(), GetActorLocation(), MyColSphere.GetSphereRadius(), 50, FColor::Cyan, true);
// check if something got hit in the sweep
bool isHit = GetWorld()->SweepMultiByChannel(OutHits, Start, End, FQuat::Identity, ECC_WorldStatic, MyColSphere);
}
If isHit
is true
, we will loop through the OutHits TArray and add radial impulse to each successful casted actor's root component. You can learn more about TArray for loops here.
add hit actors to SweepActors.
if (isHit)
{
// loop through TArray
for (auto& Hit : OutHits)
{
UStaticMeshComponent* MeshComp = Cast<UStaticMeshComponent>((Hit.GetActor())->GetRootComponent());
if (MeshComp)
{
// alternivly you can use ERadialImpulseFalloff::RIF_Linear for the impulse to get linearly weaker as it gets further from origin.
// set the float radius to 500 and the float strength to 2000.
MeshComp->AddRadialImpulse(GetActorLocation(), 500.f, 2000.f, ERadialImpulseFalloff::RIF_Constant, true);
}
}
}
Below is the full .cpp
file.
#include "AddRadialForce.h"
// add debug helpfers
#include "DrawDebugHelpers.h"
// Sets default values
AAddRadialForce::AAddRadialForce()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AAddRadialForce::BeginPlay()
{
Super::BeginPlay();
// create tarray for hit results
TArray<FHitResult> OutHits;
// get actor locations
FVector MyLocation = GetActorLocation();
// start and end locations. The sphere will create the radial sweep.
FVector Start = MyLocation;
FVector End = MyLocation;
// create a collision sphere
FCollisionShape MyColSphere = FCollisionShape::MakeSphere(500.0f);
// draw collision sphere
DrawDebugSphere(GetWorld(), GetActorLocation(), MyColSphere.GetSphereRadius(), 50, FColor::Cyan, true);
// check if something got hit in the sweep
bool isHit = GetWorld()->SweepMultiByChannel(OutHits, Start, End, FQuat::Identity, ECC_WorldStatic, MyColSphere);
if (isHit)
{
// loop through TArray
for (auto& Hit : OutHits)
{
UStaticMeshComponent* MeshComp = Cast<UStaticMeshComponent>((Hit.GetActor())->GetRootComponent());
if (MeshComp)
{
// alternivly you can use ERadialImpulseFalloff::RIF_Linear for the impulse to get linearly weaker as it gets further from origin.
// set the float radius to 500 and the float strength to 2000.
MeshComp->AddRadialImpulse(GetActorLocation(), 500.f, 2000.f, ERadialImpulseFalloff::RIF_Constant, true);
}
}
}
}
// Called every frame
void AAddRadialForce::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}