Add Radial Impulse to Actor

December 01, 2017

UE4 Version: 4.18.3

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

}

Author

Harrison McGuire

Harrison McGuire