Component added to FCollisionQueryParams::AddIgnoredComponents not ignored

Hello,

I have a situation whereby I’m doing a lineTrace using LineTraceSingleByChannel, and in some cases, I’m adding the hit component to the IgnoreComponents list and then performing the LineTrace again, with all other parameters identical. However, the second trace also hits the same component, even though it was in the IgnoreComponents list.

This appears to be due to a mismatch between the components UniqueId and the ShapeFilter data. In the filter callback, the ignored component should be caught by this code:

	if(IgnoreComponents.Contains(ShapeFilter.word0))
	{
		//UE_LOG(LogTemp, Log, TEXT("Ignoring Actor: %d"), ShapeFilter.word0);
		return (PrefilterReturnValue = PxSceneQueryHitType::eNONE);
	}

However, the IgnoreComponents value is not matching that of the ShapeFilter. The UniqueId is 111774, but the shape filter is 111766.

The component in question is a DestructibleComponent.

Hey eyesiah-

How are you adding the component to your ignore list? I was able to write a short function that executes certain code based on if the component is being ignored or not. In the following sample, I preformed the line trace in blueprint and pass the hit result to my function.

void AMyProject26Character::IgnoredComponent(FHitResult HitRef)
{
	
	if (!IgnoredCompArray.Contains(HitRef.GetComponent()))
	{
		IgnoredCompArray.Add(HitRef.GetComponent());
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1, 4.f, FColor::Yellow, TEXT("Component Added to ignore array"));
		}
	}
	else
	{
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1, 4.f, FColor::Red, TEXT("Component already part of array"));
		}
	}
}

With this I can hit each individual component of an actor and each will trigger the if statement the first time I do my line trace and the else each time I hit the same component after. If this doesn’t help, please post your code for comparison.

Cheers

Hi , here’s the relevant function. I’ve edited it a bit to remove some stuff that’s irrelevant to this topic. Note that it’s a recursive function, it’s basically the raytrace part of our weapon system for instant shooting weapons.

void UDAWeaponCompShooting::PerformShot(AActor * owningAgent, FVector targetLocation, FCollisionQueryParams params, float damage, TSubclassOf<UDADamageType> damageType, FHitResult &hit)
{	
	FVector thisPawnLocation = owningAgent->GetActorLocation();
	
	FVector toTarget = targetLocation - thisPawnLocation;
	toTarget.Normalize();
	targetLocation = thisPawnLocation + (toTarget * m_shootingRange);

	params.bTraceComplex = true;
	GetWorld()->LineTraceSingleByChannel(hit, thisPawnLocation, targetLocation, ECC_WeaponTrace, params);
	
	AActor *hitActor = hit.GetActor();
	if (hit.bBlockingHit && hitActor != nullptr)
	{
		AController *instigtor = nullptr;
		if (APawn *owningPawn = Cast<APawn>(owningAgent))
		{
			instigtor = owningPawn->GetController();
		}

		FPointDamageEvent damageEvent;
		damageEvent.ShotDirection = hit.Location - thisPawnLocation;
		damageEvent.ShotDirection.Normalize();
		damageEvent.HitInfo = hit;
		damageEvent.DamageTypeClass = *damageType;
		hitActor->TakeDamage(damage, damageEvent, instigtor, owningAgent);

		UDestructibleComponent *hitDestructible = Cast<UDestructibleComponent>(hit.GetComponent());
		if (hitDestructible)
		{
			// pass through destructibles: try again
			int32 numIgnored = params.IgnoreComponents.Num();
			params.AddIgnoredComponent(hitDestructible);
			if (numIgnored != params.IgnoreComponents.Num())
			{
				 PerformShot(owningAgent, targetLocation, params, damage, damageType, hit)
			}
		}
	}
}

Note the check for if the ignored component was successfully added. Without this, I get a stack overflow as the component will be constantly hit by the line trace.

I did some testing for this and wanted to report my findings. I created an actor class and added a box and static mesh to it. Then on BeginPlay() I created an FCollisonQueryParams varaible, printed the IgnoredComponents.Num() value, added the box component to the ignored array, and printed the .Num() again.

.h

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Test)
	UBoxComponent* Boxxy;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Test)
	UStaticMeshComponent* Moxxy;

.cpp

AMyActor::AMyActor()
{
 	// 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;
	Boxxy = CreateDefaultSubobject<UBoxComponent>(TEXT("BOxxy"));
	RootComponent = Boxxy;

	Moxxy = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Moxxy"));
	Moxxy->AttachTo(RootComponent);

}

// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
	Super::BeginPlay();


	FCollisionQueryParams Parapa;
	if (GEngine)
	{
		GEngine->AddOnScreenDebugMessage(-1, 4.f, FColor::Blue, FString::FromInt(Parapa.IgnoreComponents.Num()));
	}

	Parapa.AddIgnoredComponent(Boxxy);
	if (GEngine)
	{
		GEngine->AddOnScreenDebugMessage(-1, 4.f, FColor::Red, FString::FromInt(Parapa.IgnoreComponents.Num()));
	}
}

After creating a blueprint and adding it to the level, it printed 0 in blue and 1 in red, indicating that the component had been added to the ignored components array. Can you confirm if you’re seeing the component added properly?

Additionally, when I tried to copy your function into my class (editing project specific code - mainly the damageType lines) I received a compile error in the header file about FCollisionQueryParams being unrecognized because it is not a USTRUCT. Removing the UFUNCTION allowed this to compile, however that raises the question of where your function is being called from and what is being passed into the function?

Lastely, when testing in 4.11 I noticed that IgnoreComponents of the FCollisionQueryParams class is a private variable. Instead of accessing this directly you will want to use the GetIgnoredComponents() function in its place.

But to simplify things a bit, I’ve created a new project using 4.11 which has a 100% repro testcase. In it, I’ve created a map with an actor that has 3 destructible mesh components. I’ve also created some test code that performs a recursive raycast, ignoring each component it encounters.

Unfortunately I can’t include the code here as there’s a character limit on this reply.

If this code is run with a ray that intersects all 3 components, it correctly ignores the components, one by one. However, if one of the components becomes fractured, the component ignoring no longer works. I’ve created a level script in the included level that has this behaviour, and prints:

------- BEGINNING RAYCAST IGNORE TEST -------
Hit component 'Destructible_First' in actor 'TestActor_BP_157'
Ignoring hit component 'Destructible_First' and trying again
Hit component 'Destructible_Middle' in actor 'TestActor_BP_157'
Ignoring hit component 'Destructible_Middle' and trying again
Hit component 'Destructible_Back' in actor 'TestActor_BP_157'
Ignoring hit component 'Destructible_Back' and trying again
------- END RAYCAST IGNORE TEST: 3 components ignored -------
    
    /// now fracture the component "Destructible_First" and delay 0.1 seconds
    
------- BEGINNING RAYCAST IGNORE TEST -------
Hit component 'Destructible_First' in actor 'TestActor_BP_157'
Ignoring hit component 'Destructible_First' and trying again
Hit component 'Destructible_First' in actor 'TestActor_BP_157'
Attempting to ignore component 'Destructible_First' had no effect!
------- END RAYCAST IGNORE TEST: 1 components ignored -------

I hope this helps you reproduce the issue. I would say, the expected behaviour is that the same output should be printed in both caseslink text

Hi , thanks for looking into this issue for me.
My function isn’t a UFUNCTION, it’s called from elsewhere in my weapon code.

Anyway, I’ve attached a new repro to the original question thread. Hopefully that’ll help.

Hey eyesiah-

I have entered a bug report about the recursive line trace function not properly populating the IgnoredComponents array (UE-29111).

In case this information helps, I did notice that adding a breakpoint at the beginning of the recursive function and stepping through the function or simply continuing with the execution will show each component being hit both times the function is called. This leads me to believe that there may be an issue with the timing of when the line trace executes however I can’t confirm that for certain.

Cheers