Create Radial Damage From Explosion Actor?

So I’ve created a generic ‘Explosion’ Actor that does things like spawn a particle system, sound effect and decal based on the surface it hits. One other thing I want to do now is be able to set how much damage the explosion generates and in what range, however I can’t seem to find a way to actually tell it to damage the things around it.

I’ve started by creating a couple of variables that should help me define the damage function parameters, based on FRadialDamageParams. This explosion could be generated any number of ways, from weapon projectiles to vehicle explosions, so I need to be able to set the owner of the explosion from other classes that call/create it. I believe this is the correct way to do so:


	/* Player that Caused This Explosion */
	TWeakObjectPtr<AController> MyController;

	UPROPERTY(EditDefaultsOnly, Category = "Explosion")
	struct FRadialDamageParams ExplosionDamage;

Now in my code, I override the BeginPlay function to spawn the Particle, Sound and Decal instantly, but now I also want to ‘Apply Radial Damage’ during that begin play function. What is the syntax for running a function that damages all actors around it based on the Damage Params that I set?

Here’s the full code to the actor itself:

Header File

#pragma once

#include "GameFramework/Actor.h"
#include "Types/BZGame_Types.h"
#include "BZGame_Explosion.generated.h"

class BZGAME_API ABZGame_Explosion : public AActor

	/* Player that Caused This Explosion */
	TWeakObjectPtr<AController> MyController;

	UPROPERTY(EditDefaultsOnly, Category = "Explosion")
	struct FRadialDamageParams ExplosionDamage;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Explosion")
	float RadialForce;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Explosion")
	float RadialForceRange;

	UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Explosion")
	TSubobjectPtr <URadialForceComponent> ExplosionForce;

	UPROPERTY(VisibleDefaultsOnly, Category = "Light")
	TSubobjectPtr <UPointLightComponent> ExplosionLight;

	UPROPERTY(EditDefaultsOnly, Category = "Light")
	float ExplosionLightFadeOut;

	/* Surface Particles */
	/* Defaults to Terrain Particle */
	UPROPERTY(EditDefaultsOnly, Category = "Particle")
	UParticleSystem* TerrainParticle;

	UPROPERTY(EditDefaultsOnly, Category = "Particle")
	UParticleSystem* VehicleParticle;

	UPROPERTY(EditDefaultsOnly, Category = "Particle")
	UParticleSystem* BuildingParticle;

	UPROPERTY(EditDefaultsOnly, Category = "Particle")
	UParticleSystem* CharacterParticle;

	/* Surface Audio */
	/* Defaults To Terrain Sound */
	UPROPERTY(EditDefaultsOnly, Category = "Audio")
	USoundCue* TerrainSound;

	UPROPERTY(EditDefaultsOnly, Category = "Audio")
	USoundCue* VehicleSound;

	UPROPERTY(EditDefaultsOnly, Category = "Audio")
	USoundCue* BuildingSound;

	UPROPERTY(EditDefaultsOnly, Category = "Audio")
	USoundCue* CharacterSound;	

	/* Surface Decal */
	/* Defaults To Terrain Decal */
	UPROPERTY(EditDefaultsOnly, Category = "Decals")
	struct FExplosionDecalData ExplosionDecalData;

	UPROPERTY(EditDefaultsOnly, Category = "Decals")
	UMaterial* TerrainDecal;

	UPROPERTY(EditDefaultsOnly, Category = "Decals")
	UMaterial* VehicleDecal;

	UPROPERTY(EditDefaultsOnly, Category = "Decals")
	UMaterial* BuildingDecal;

	UPROPERTY(EditDefaultsOnly, Category = "Decals")
	UMaterial* CharacterDecal;

	UPROPERTY(BlueprintReadOnly, Category = "Surface")
	FHitResult SurfaceHit;

	/* Spawn Explosion */
	virtual void BeginPlay() override;

	/* Tick For Fade Outs */
	virtual void Tick(float DeltaSeconds) override;

	/* Get FX From Material Type */
	UParticleSystem* GetExplosionFX(TEnumAsByte<EPhysicalSurface> SurfaceType) const;

	/* Get Audio From Material Type */
	USoundCue* GetExplosionSound(TEnumAsByte<EPhysicalSurface> SurfaceType) const;

	/* Get Decal Material From Surface Type */
	UMaterial* GetExplosionDecal(TEnumAsByte<EPhysicalSurface> SurfaceType) const;

CPP File

#include "BZGame.h"
#include "BZGame_Explosion.h"

ABZGame_Explosion::ABZGame_Explosion(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
	PrimaryActorTick.bCanEverTick = true;

	ExplosionLight = PCIP.CreateDefaultSubobject<UPointLightComponent>(this, TEXT("ExplosionLight"));
	ExplosionLight->AttenuationRadius = 400.0f;
	ExplosionLight->Intensity = 500.0f;
	ExplosionLight->bUseInverseSquaredFalloff = false;
	ExplosionLight->LightColor = FColor(255, 185, 35);
	ExplosionLight->CastShadows = false;
	ExplosionLight->bVisible = true;

	ExplosionLightFadeOut = 0.2f;
	RootComponent = ExplosionLight;

	ExplosionForce = PCIP.CreateDefaultSubobject<URadialForceComponent>(this, TEXT("ExplosionForce"));
	ExplosionForce->bAutoActivate = true;
	ExplosionForce->ForceStrength = 0.0f;
	ExplosionForce->ImpulseStrength = RadialForce;
	ExplosionForce->Radius = RadialForceRange;
	ExplosionForce->AttachParent = ExplosionLight;

	bAutoDestroyWhenFinished = true;

void ABZGame_Explosion::BeginPlay()

	UPhysicalMaterial* HitPhysMat = SurfaceHit.PhysMaterial.Get();
	EPhysicalSurface HitSurfaceType = UPhysicalMaterial::DetermineSurfaceType(HitPhysMat);

	/* Show Particles */
	UParticleSystem* ExplosionFX = GetExplosionFX(HitSurfaceType);
	if (ExplosionFX)
		UGameplayStatics::SpawnEmitterAtLocation(this, ExplosionFX, GetActorLocation(), GetActorRotation());

	/* Play Audio */
	USoundCue* ExplosionSound = GetExplosionSound(HitSurfaceType);
	if (ExplosionSound)
		UGameplayStatics::PlaySoundAtLocation(this, ExplosionSound, GetActorLocation());

	/* Get Decal Material */
	UMaterial* ExplosionDecal = GetExplosionDecal(HitSurfaceType);

	if (ExplosionDecal && ExplosionDecalData.LifeSpan > 0.0f)
		FRotator RandomDecalRotation = SurfaceHit.ImpactNormal.Rotation();
		RandomDecalRotation.Roll = FMath::FRandRange(-180.0f, 180.0f);

		UGameplayStatics::SpawnDecalAttached(ExplosionDecal, FVector(ExplosionDecalData.DecalSize, ExplosionDecalData.DecalSize, 1.0f), SurfaceHit.Component.Get(), SurfaceHit.BoneName, SurfaceHit.ImpactPoint, RandomDecalRotation, EAttachLocation::KeepWorldPosition, ExplosionDecalData.LifeSpan);


void ABZGame_Explosion::Tick(float DeltaSeconds)

	const float TimeAlive = GetWorld()->GetTimeSeconds() - CreationTime;
	const float TimeRemaining = FMath::Max(0.0f, ExplosionLightFadeOut - TimeAlive);

	if (TimeRemaining > 0.0f)
		const float FadeAlpha = 1.0f - FMath::Square(TimeRemaining / ExplosionLightFadeOut);

		ExplosionLight->SetIntensity(ExplosionLight->Intensity * FadeAlpha);

UParticleSystem* ABZGame_Explosion::GetExplosionFX(TEnumAsByte<EPhysicalSurface> SurfaceType) const
	UParticleSystem* ExplosionFX = NULL;

	switch (SurfaceType)
		case BZGAME_SURFACE_Terrain:	ExplosionFX = TerrainParticle; break;
		case BZGAME_SURFACE_Vehicle:	ExplosionFX = VehicleParticle; break;
		case BZGAME_SURFACE_Building:	ExplosionFX = BuildingParticle; break;
		case BZGAME_SURFACE_Character:	ExplosionFX = CharacterParticle; break;
		default:						ExplosionFX = TerrainParticle; break;

	return ExplosionFX;

USoundCue* ABZGame_Explosion::GetExplosionSound(TEnumAsByte<EPhysicalSurface> SurfaceType) const
	USoundCue* ExplosionSound = NULL;

	switch (SurfaceType)
		case BZGAME_SURFACE_Terrain:	ExplosionSound = TerrainSound; break;
		case BZGAME_SURFACE_Vehicle:	ExplosionSound = VehicleSound; break;
		case BZGAME_SURFACE_Building:	ExplosionSound = BuildingSound; break;
		case BZGAME_SURFACE_Character:	ExplosionSound = CharacterSound; break;
		default:						ExplosionSound = TerrainSound; break;

	return ExplosionSound;

UMaterial* ABZGame_Explosion::GetExplosionDecal(TEnumAsByte<EPhysicalSurface> SurfaceType) const
	UMaterial* DecalMaterial = NULL;

	switch (SurfaceType)
		case BZGAME_SURFACE_Terrain:	DecalMaterial = TerrainDecal; break;
		case BZGAME_SURFACE_Vehicle:	DecalMaterial = VehicleDecal; break;
		case BZGAME_SURFACE_Building:	DecalMaterial = BuildingDecal; break;
		case BZGAME_SURFACE_Character:	DecalMaterial = CharacterDecal; break;
		default:						DecalMaterial = TerrainDecal; break;

	return DecalMaterial;

It’s based heavily on the ShooterGame effect actors but with changes. Bonus Points Available: Tell me how I can set my decal material to be a dynamic material instance of the one I specify, and then fade it out over time?


Turns out it’s a UGameplayStatics function:

	if (ExplosionDamage.BaseDamage > 0.0f && ExplosionDamage)
		UGameplayStatics::ApplyRadialDamageWithFalloff(this, ExplosionDamage.BaseDamage, ExplosionDamage.MinimumDamage, GetActorLocation(), ExplosionDamage.InnerRadius, ExplosionDamage.OuterRadius, ExplosionDamage.DamageFalloff, DamageType, TArray<AActor>(), this, MyController.Get());

1 Like