Delegate output pin not showing in custom blueprint node

I have the following, but for some reason the OutHitResults refuses to display. Any suggestions?

image

I can’t figure out why just that pin won’t show. If I add other pins they seam to show fine, but not OutHitResults.

It does show up if I bind the event to the projectile object, but I want it to appear below the projectile object so I can use it on the outbound pins.

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.

The problem is with the engine, sort of.

The blueprint nodes that are created for BlueprintAsyncActionBase function can only support a single delegate signature. If you changed the names of the parameters for all but FOnProjectileCreated you’d find that even your UTraceProjectileBase parameter would be missing. If you take a look at the places in the source where the UBlueprintAsyncActionBase is used you’ll see that there is only ever one delegate type declared and it’s reused for all the assignable delegates of the async task. Regardless of whether or not the parameters make sense in all those cases.

If you’re on a source build and you’re comfortable making engine changes, there is a pending pull request (https://github.com/EpicGames/UnrealEngine/pull/10552) that you could use to modify your engine to work the way that you’re expected. Although I believe you’d have to rename some of your delegate parameters so that there aren’t any overlapping names.

Or simplify down to one delegate with the projectile and hit results, use that for all your delegates and just live with the fact that you have to provide an empty collection in a couple of cases.

1 Like

Aaah, that would certainly explain it then lol. Thank you! I’ll just go with a generic declare then. Below seams to work fine now.

#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_TwoParams( FOnProjectileSignature, UTraceProjectileBase*, OutProjectileObject, const TArray<FHitResult>&, OutHitResults );

	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" )
		FOnProjectileSignature OnProjectileCreated;

	UPROPERTY( BlueprintAssignable, Category = "TraceProjectile" )
		FOnProjectileSignature OnProjectileHit;

	UPROPERTY( BlueprintAssignable, Category = "TraceProjectile" )
		FOnProjectileSignature OnProjectileOverlap;

	UPROPERTY( BlueprintAssignable, Category = "TraceProjectile" )
		FOnProjectileSignature 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();
	}
};

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.