Difficulties with SweepMultiByChannel and storing pointers to hit actors.

I’m working on creating an ability that applies a status effect to enemies in the form of attaching actors of a class I made “WeakPoint” to their meshes. I ran into a wall today when trying to apply this ability to an indefinite number of targets. I’m attempting to take all of the actors hit in the OutHIts array, itterate through it and store pointers to the actors that are derived from my Enemy class into its own array, that I then use to add the weakpoints to their respective meshes. I have two enemies currently in the world, and after performing the code below if I UE_LOG the size of my array for enemies hit by the sweep the number shown is 30… If I UE_LOG the size of OutHIts the number shown is 68. So clearly my for loop is doing some kind of filtering, but it’s storing duplicate addresses to the same enemy, causing an excess of weak points to be spawned. Below is my code for my sweep and how I’m currently iterating through actors as well as my Struct I’m using to store the weakpoints to the enemies for context.

FCollisionShape HitSphere = FCollisionShape::MakeSphere(TacticalRange);

bool bHitSuccess = GetWorld()->SweepMultiByChannel(
OutHits, 
PlayerCharacter->GetActorLocation(), 
PlayerCharacter->GetActorLocation(), 
FQuat::Identity, 
ECC_GameTraceChannel1, 
HitSphere);

  if(bHitSuccess)
  {
       for(int32 i = 0; i < OutHits.Num(); i++)
       {

            if (OutHits[i].GetActor()->IsA(AEnemy::StaticClass()))
            {
                FEnemyData EnemyHit = {Cast<AEnemy>(OutHits[i].GetActor())};
                EnemiesHitByTactical.Add(EnemyHit);
               
            }
        }

       UE_LOG(LogTemp, Warning, TEXT("number of enemies hit %i"), 
       EnemiesHitByTactical.Num());
       UE_LOG(LogTemp, Warning, TEXT("number of actors hit %i"), 
       OutHits.Num());
}
USTRUCT(BlueprintType)
struct FEnemyData
{
    GENERATED_BODY()
    
    class AEnemy* EnemyPtr;
    TArray<AWeakPoint*> WeakPointsArr;
    
};

SweepMultiByChannel sweeps for ALL overlaps (ie all components), so it’s possible to get the same actor twice through multiple components.

So you could have a custom trace channel for this purpose and make sure it’s assigned to overlap/block only on the root component of the class you are looking for.

Or make a TArray of actor pointers using AddUnique and then make your structs after the sweep is done being sorted (but I don’t know immediately what the most performant variant of that method would be)

1 Like

Do you know how one would go about specifying that a trace channel only overlap/block the root component?

From within the Editor you can go Edit → Project Settings, search up Trace Channels and click New Trace Channel… (and set to Ignore by default)

Once you add it there, it will be present in your ProjectFolder/Config/DefaultEngine.ini as one of the ECC_GameTraceChannels in this kind of format:

+DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Block,bTraceType=False,bStaticObject=False,Name="Projectile")

Then just replace your ECC_GameTraceChannel1 in your example with the corresponding number and change the collision profile of the component of your choice to start overlapping or blocking the new trace channel you created.

1 Like

I tried implementing a capsule component on the enemy base class like so:

AEnemy::AEnemy()
{
	PrimaryActorTick.bCanEverTick = true;

	EnemyIdentifier = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Enemy 
    Identifier"));
	EnemyIdentifier->InitCapsuleSize(5.f, 5.f);
	EnemyIdentifier->SetupAttachment(RootComponent);
	EnemyIdentifier->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
	EnemyIdentifier->SetCollisionResponseToChannel(ECC_GameTraceChannel2, 
        ECR_Overlap);

}

And now no collision is being detected by my sweep which looks the same as before with the exception of changing the channel name

bool bHitSuccess = GetWorld()->SweepMultiByChannel(OutHits, PlayerCharacter->GetActorLocation(), PlayerCharacter->GetActorLocation(), FQuat::Identity, ECC_GameTraceChannel2, HitSphere);

But weirdly enough while messing with the capsule component I made in the constructor at a certain point I went from being able to edit and view the component in my Zombie blueprint, to seeing two versions of the component, to then only one again except now when I click on it the details window is blank. Any idea what’s going on?

Screenshot (809)

If it has nothing in details pane, that is usually because you are missing the right UPROPERTY specifiers for the component (like EditAnywhere, BlueprintReadWrite) or might need to restart the editor and compile if using live coding

Sorry to hound you, but do you see something wrong with how I tried to implement the new collision component? The hit is no longer successful.

1 Like

I believe it is because SweepMultiByChannel only returns True if it encountered a blocking hit, so you are testing out based on that condition. You can just remove that test and only worry about the OutHits, which will be empty if there are no overlaps.

If you use blocking hits, the first blocking hit encountered will end the sweep, so you want to use only overlaps if you want to ensure you get everything in the region you’re sweeping against.

When you say remove that test you mean take out SweepMultiByChannel entirely? What would be used in place of it that can sweep in a area all at once? I’m new to using Unreal engine so I only know about this method and line tracing.

FCollisionShape HitSphere = FCollisionShape::MakeSphere(TacticalRange);

GetWorld()->SweepMultiByChannel(
	OutHits,
	PlayerCharacter->GetActorLocation(),
	PlayerCharacter->GetActorLocation(),
	FQuat::Identity,
	ECC_GameTraceChannel1,
	HitSphere);

for (int32 i = 0; i < OutHits.Num(); i++)
{

	if (OutHits[i].GetActor()->IsA(AEnemy::StaticClass()))
	{
		FEnemyData EnemyHit = { Cast<AEnemy>(OutHits[i].GetActor()) };
		EnemiesHitByTactical.Add(EnemyHit);

	}
}

UE_LOG(LogTemp, Warning, TEXT("number of enemies hit %i"),
	EnemiesHitByTactical.Num());
UE_LOG(LogTemp, Warning, TEXT("number of actors hit %i"),
	OutHits.Num());

You can just remove the check against the bool you are assigning to the return value of SweepMultiByChannel, there’s no need to use it if you only care about overlaps

The UE_LOG still says that the contents of OutHits is 0. I don’t know why but it’s still not detecting the enemies in the sweep. It seems like there’s something wrong with my Enemy base class. Do I need OnOverlapBegin and end functions or something??

I ultimately remedied the issue by going into my Enemy base class and putting the following code in the constructor

AEnemy::AEnemy()
{
 	
	PrimaryActorTick.bCanEverTick = true;

	GetCapsuleComponent()- 
        >SetCollisionResponseToChannel(ECollisionChannel::ECC_GameTraceChannel2, 
         ECR_Overlap);

}

Apparently I was overcomplicating things trying to make it my own custom capsule collision. I still wish I could’ve figured out how to make it work that way just for future reference. But At least now I can move on to the next issue.

1 Like

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