Below is the entire script.
TraceProjectileBase.cpp
#include "CoreMinimal.h"
#include "TraceProjectile/Public/TraceProjectileBase.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "Components/SceneComponent.h"
#include "Components/PrimitiveComponent.h"
#include "Engine/World.h"
#include "Engine/HitResult.h"
#include "GameFramework/WorldSettings.h"
#include "CollisionShape.h"
#include "CollisionQueryParams.h"
#include "Delegates/IDelegateInstance.h"
#include "DrawDebugHelpers.h"
#include "UObject/NoExportTypes.h"
void UTraceProjectileBase::Activate()
{
OnWorldTearDownDelegateHandle = FWorldDelegates::OnWorldBeginTearDown.AddUObject( this, &UTraceProjectileBase::OnWorldTearDown );
for ( int32 i = 0; i < AttachComponents.Num(); i++ ) {
if ( UPrimitiveComponent* PrimCompPtr = Cast<UPrimitiveComponent>( AttachComponents[i] ) ) {
AttachPrimitiveComponents.Add( PrimCompPtr );
}
}
OnProjectileCreated.Broadcast( this );
bIsProjectileActive = true;
}
void UTraceProjectileBase::Destroy()
{
if ( bAutoDestroyAttachComponents ) {
for ( int32 i = 0; i < AttachComponents.Num(); i++ ) {
if ( IsValid( AttachComponents[i] ) ) {
AttachComponents[i]->DestroyComponent();
}
}
AttachComponents.Empty();
AttachPrimitiveComponents.Empty();
}
OnProjectileDestroy.Broadcast( this );
bIsProjectileActive = false;
SetReadyToDestroy();
}
void UTraceProjectileBase::DestroyAttachComponents()
{
for ( int32 i = 0; i < AttachComponents.Num(); i++ ) {
if ( IsValid( AttachComponents[i] ) ) {
AttachComponents[i]->DestroyComponent();
}
}
AttachComponents.Empty();
AttachPrimitiveComponents.Empty();
}
void UTraceProjectileBase::OnWorldTearDown( UWorld* World )
{
FWorldDelegates::OnWorldBeginTearDown.Remove( OnWorldTearDownDelegateHandle );
Destroy();
}
void UTraceProjectileBase::Tick( float DeltaTime )
{
if ( IsValid( WorldPtr ) ) {
float DeltaWithTimeDilation = ( DeltaTime * WorldPtr->GetWorldSettings()->TimeDilation );
ProjectileLifeTime -= DeltaWithTimeDilation;
if ( ProjectileLifeTime <= 0.f ) {
Destroy();
return;
}
ProjectileGravityStartTime -= DeltaWithTimeDilation;
for ( int32 i = 0; i < AttachComponents.Num(); i++ ) {
if ( IsValid( AttachComponents[i] ) ) {
AttachComponents[i]->SetWorldLocationAndRotationNoPhysics( ProjectileLocation, FRotationMatrix::MakeFromX( ProjectileDirection ).Rotator() );
}
}
float TimeDilation = WorldPtr->GetWorldSettings()->TimeDilation;
float DeltaSpeed = ( ProjectileSpeed * DeltaWithTimeDilation );
float DeltaGravity = ( ProjectileGravityStartTime > 0.f ? 0.f : ProjectileGravity * DeltaWithTimeDilation );
FVector TraceStartLocation = ProjectileLocation;
FVector TraceEndLocation = ( ( ProjectileDirection * DeltaSpeed ) + ProjectileLocation );
TArray<FHitResult> TraceHitResults;
FCollisionQueryParams TraceCollisionQueryParams;
TraceCollisionQueryParams.AddIgnoredActors( IgnoreActors );
TraceCollisionQueryParams.AddIgnoredComponents( AttachPrimitiveComponents );
TraceCollisionQueryParams.bTraceComplex = bTraceComplex;
bool bIsTraceBlock = false;
if ( ProjectileRadius >= 1.f ) {
bIsTraceBlock = WorldPtr->SweepMultiByChannel( TraceHitResults, TraceStartLocation, TraceEndLocation, FQuat::Identity, TraceCollisionChannel, FCollisionShape::MakeSphere( ProjectileRadius ), TraceCollisionQueryParams );
} else {
bIsTraceBlock = WorldPtr->LineTraceMultiByChannel( TraceHitResults, TraceStartLocation, TraceEndLocation, TraceCollisionChannel, TraceCollisionQueryParams );
}
ProjectileLocation = TraceEndLocation;
ProjectileDirection.Z = FMath::Clamp( ( ProjectileDirection.Z - DeltaGravity ), -1.f, 1.f );
if ( TraceHitResults.Num() > 0 ) {
if ( bIsTraceBlock ) {
OnProjectileHit.Broadcast( this, TraceHitResults );
} else {
OnProjectileOverlap.Broadcast( this, TraceHitResults );
}
}
} else {
Destroy();
return;
}
}
bool UTraceProjectileBase::IsTickable() const
{
return bIsProjectileActive;
}
TStatId UTraceProjectileBase::GetStatId() const
{
return TStatId();
}
UTraceProjectileBase* UTraceProjectileBase::SpawnProjectile(
UObject* WorldContextObject,
const FVector InProjectileLocation,
const FVector InProjectileDirection,
const float InProjectileRadius,
const float InProjectileSpeed,
const float InProjectileGravity,
const float InProjectileGravityStartTime,
const float InProjectileLifeTime,
bool bInTraceComplex,
const TArray<AActor*>& InIgnoreActors,
ETraceTypeQuery InTraceChannel,
const TArray<class USceneComponent*> InAttachComponents,
bool bInAutoDestroyAttachComponents,
bool bInEnableDebug,
const float InDrawDebugTime)
{
UTraceProjectileBase* BlueprintNode = NewObject<UTraceProjectileBase>();
BlueprintNode->WorldContextObject = WorldContextObject;
if ( IsValid( WorldContextObject ) ) {
BlueprintNode->WorldPtr = WorldContextObject->GetWorld();
}
BlueprintNode->ProjectileLocation = InProjectileLocation;
BlueprintNode->ProjectileDirection = InProjectileDirection.GetClampedToSize( -1.f, 1.f );
BlueprintNode->ProjectileRadius = InProjectileRadius;
BlueprintNode->ProjectileSpeed = InProjectileSpeed;
BlueprintNode->ProjectileGravity = InProjectileGravity;
BlueprintNode->ProjectileGravityStartTime = InProjectileGravityStartTime;
BlueprintNode->ProjectileLifeTime = InProjectileLifeTime;
BlueprintNode->bTraceComplex = bInTraceComplex;
BlueprintNode->IgnoreActors = InIgnoreActors;
BlueprintNode->bEnableDebug = bInEnableDebug;
BlueprintNode->DrawDebugTime = InDrawDebugTime;
BlueprintNode->AttachComponents = InAttachComponents;
BlueprintNode->bAutoDestroyAttachComponents = bInAutoDestroyAttachComponents;
BlueprintNode->TraceCollisionChannel = UEngineTypes::ConvertToCollisionChannel( InTraceChannel );
BlueprintNode->RegisterWithGameInstance( WorldContextObject );
return BlueprintNode;
}
TraceProjectileBase.h
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "UObject/NoExportTypes.h"
#include "Tickable.h"
#include "Engine/EngineTypes.h"
#include "Engine/HitResult.h"
#include "TraceProjectileBase.generated.h"
UCLASS( Blueprintable )
class TRACEPROJECTILE_API UTraceProjectileBase : public UBlueprintAsyncActionBase, public FTickableGameObject
{
GENERATED_BODY()
public:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( FOnProjectileCreated, UTraceProjectileBase*, OutProjectileObject );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FOnProjectileHit, UTraceProjectileBase*, OutProjectileObject, TArray<FHitResult>&, OutHitResults );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FOnProjectileOverlap, UTraceProjectileBase*, OutProjectileObject, TArray<FHitResult>&, OutHitResults );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( FOnProjectileDestroy, UTraceProjectileBase*, OutProjectileObject );
virtual void Activate() override;
UFUNCTION( BlueprintCallable, Category = "TraceProjectile" )
void Destroy();
UFUNCTION( BlueprintCallable, Category = "TraceProjectile" )
void DestroyAttachComponents();
void OnWorldTearDown( UWorld* World );
virtual void Tick( float DeltaTime ) override;
virtual bool IsTickable() const override;
virtual TStatId GetStatId() const override;
UPROPERTY( BlueprintAssignable, Category = "TraceProjectile" )
FOnProjectileCreated OnProjectileCreated;
UPROPERTY( BlueprintAssignable, Category = "TraceProjectile" )
FOnProjectileHit OnProjectileHit;
UPROPERTY( BlueprintAssignable, Category = "TraceProjectile" )
FOnProjectileOverlap OnProjectileOverlap;
UPROPERTY( BlueprintAssignable, Category = "TraceProjectile" )
FOnProjectileDestroy OnProjectileDestroy;
UFUNCTION( BlueprintCallable, meta = ( BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", AutoCreateRefTerm = "InIgnoreActors, InAttachComponents", InProjectileSpeed = "1000.f", InProjectileGravity = "0.1f", InProjectileGravityStartTime = "0.5f", InProjectileLifeTime = "5.f", bInAutoDestroyAttachComponents = "true", bInEnableDebug = "true", InDrawDebugTime = "1.f" ), Category = "TraceProjectile" )
static UTraceProjectileBase* SpawnProjectile(
UObject* WorldContextObject,
const FVector InProjectileLocation,
const FVector InProjectileDirection,
const float InProjectileRadius,
const float InProjectileSpeed,
const float InProjectileGravity,
const float InProjectileGravityStartTime,
const float InProjectileLifeTime,
bool bInTraceComplex,
const TArray<AActor*>& InIgnoreActors,
ETraceTypeQuery InTraceChannel,
const TArray<class USceneComponent*> InAttachComponents,
bool bInAutoDestroyAttachComponents,
bool bInEnableDebug,
const float InDrawDebugTime);
protected:
class FDelegateHandle OnWorldTearDownDelegateHandle;
const UObject* WorldContextObject;
class UWorld* WorldPtr = nullptr;
float ProjectileRadius = 1.f;
float ProjectileSpeed = 100.f;
float ProjectileGravity = 1.f;
float ProjectileGravityStartTime = 1.f;
FVector ProjectileLocation = FVector::ZeroVector;
FVector ProjectileDirection = FVector::ZeroVector;
float ProjectileLifeTime = 5.f;
bool bIsProjectileActive = false;
bool bTraceComplex = false;
TArray<AActor*> IgnoreActors;
ECollisionChannel TraceCollisionChannel = ECC_Visibility;
bool bAutoDestroyAttachComponents;
bool bEnableDebug = true;
float DrawDebugTime = 5.f;
UPROPERTY()
TArray<class USceneComponent*> AttachComponents;
UPROPERTY()
TArray<class UPrimitiveComponent*> AttachPrimitiveComponents;
public:
UFUNCTION( BlueprintPure, Category = "TraceProjectile" )
float GetProjectileRadius()
{
return ProjectileRadius;
}
UFUNCTION( BlueprintCallable, Category = "TraceProjectile" )
void SetProjectileRadius( float Value )
{
ProjectileRadius = Value;
}
UFUNCTION( BlueprintPure, Category = "TraceProjectile" )
float GetProjectileSpeed()
{
return ProjectileSpeed;
}
UFUNCTION( BlueprintCallable, Category = "TraceProjectile" )
void SetProjectileSpeed( float Value )
{
ProjectileSpeed = Value;
}
UFUNCTION( BlueprintPure, Category = "TraceProjectile" )
float GetProjectileGravity()
{
return ProjectileGravity;
}
UFUNCTION( BlueprintCallable, Category = "TraceProjectile" )
void SetProjectileGravity( float Value )
{
ProjectileGravity = Value;
}
UFUNCTION( BlueprintCallable, Category = "TraceProjectile" )
void SetProjectileLocation( FVector InProjectileLocation )
{
ProjectileLocation = InProjectileLocation;
}
UFUNCTION( BlueprintCallable, Category = "TraceProjectile" )
void SetProjectileDirection( FVector InProjectileDirection )
{
ProjectileDirection = InProjectileDirection;
}
UFUNCTION( BlueprintPure, Category = "TraceProjectile" )
float GetProjectileLifeTime()
{
return ProjectileLifeTime;
}
UFUNCTION( BlueprintCallable, Category = "TraceProjectile" )
void SetProjectileLifeTime( float Value )
{
ProjectileLifeTime = Value;
}
UFUNCTION( BlueprintCallable, Category = "TraceProjectile" )
void SetIgnoreActors( TArray<class AActor*> InIgnoreActors )
{
IgnoreActors = InIgnoreActors;
}
UFUNCTION( BlueprintCallable, Category = "TraceProjectile" )
void AddIgnoreActor( class AActor* IgnoreActor )
{
IgnoreActors.Add( IgnoreActor );
}
UFUNCTION( BlueprintCallable, Category = "TraceProjectile" )
void RemoveIgnoreActor( class AActor* IgnoreActor )
{
if ( IgnoreActors.Contains( IgnoreActor ) ) {
IgnoreActors.Remove( IgnoreActor );
}
}
UFUNCTION( BlueprintCallable, Category = "TraceProjectile" )
void ClearIgnoreActors()
{
IgnoreActors.Empty();
}
};
I’m out of ideas. I can’t seam to get OutHitResults pin to show at all.