For loop when given character Hit Result triggers multiple times

I this is in a replicated Function on the character

the problem is that the TakeDamage is called around 10 times making the character die instantly but when i trace for other objects like the FLOOR it only prints the string 1 time like it should

I want the take damage function to only call once for the character

TArray<AActor*> ActorsToIgnore;

AActor* SourceActor = this;
if (!SourceActor)
{
	// Hero is dead
	return;
}

 // this is use for other reasons
ActorsToIgnore.Add(SourceActor);

FCollisionQueryParams Params;
//Params.AddIgnoredActors(ActorsToIgnore);

TArray<FHitResult> HitResult;
FVector Start = RootComponent->GetComponentLocation();

GetWorld()->SweepMultiByChannel(HitResult, Start, Start, FQuat::Identity, ECC_GameTraceChannel4, FCollisionShape::MakeSphere(500.f), Params);

for (auto& LoopHitResult : HitResult)
{
	if (LoopHitResult.GetActor() == SourceActor)
	{
		LoopHitResult.Actor->TakeDamage(20.f, FDamageEvent(), GetController(), this);

		GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Black, FString::Printf(TEXT("Hit: %s"), *LoopHitResult.Actor->GetName()));
	}
}

Could try checking a bit directly:

if (LoopHitResult.GetActor()->IsA(YourActorClass::StaticClass())

Yes but why is the loop going through my character 10 times every time the function is called but when it is the floor it only goes through once

This is what prints on screen

You should consider drawing a debug sphere where you trace it, see how it looks in the scene, and check the collision settings of your components.

Also, it might be that both the actor and mesh components each have an associated FHitResult. You can confirm this by printing the display name of each primitive component for all the hit results.

A dirty way to solve this problem is just have a break statement after you trigger the damage once. This ensures it will only happen once, regardless of whats happening behind the scenes.

I made a new sphere component attached to the character , this component is the only one in the game that has the custom collision channel That the SphereTraceMulti traces for. but it still happens more then once it only happens twice now not 10 but i only want it happening 1 time

313024-annotation-2020-09-10-101302.png

i did what you said and printed out the components but CollisionCylinder still happens twice

A side comment, this check will never be true, and so that return will never be run.

AActor* SourceActor = this;
 if (!SourceActor)
 {
     // Hero is dead
     return;
 }

this will never be null unless you’re corrupting memory or something. If you want to see if the actor/component has had Destroy*() called recently you can instead call IsPendingKill(): UObjectBaseUtility::IsPendingKill | Unreal Engine Documentation

Is your character’s collisions settings set to block or overlap with the ECC_GameTraceChannel4 channel? You can check in your project’s collision settings. I would expect to see this sort of behavior if it was set to overlap rather than block.

Also is there a reason you’re calling SweepMultiByChannel and not something simpler?

yes collision is for Overlap

All i am trying to do is find all characters near my character and apply Take damage on them

i am not sure if having a sphere component and checking for overlaps

or

doing SweepMultiByChannel is best option as i am searching for efficient option

I’m going to assume its happening twice without a break statement. Imagine you have two circles that intersect each other. They will share two points, if it isn’t the edge case. In the 3D case you will get a surface overlap, but it will just be points if you are dealing with unfilled volumes.

I very much recommend drawing the sphere with DrawDebugSphere() from “DrawDebugHelpers.h”. You will see the overlap points. Then post the picture. Text output is useful, but it really helps to see the traces.

If you read the documentation on SweepMultiByChannel you’ll notice that it says “return all initial overlaps using a specific channel (including blocking) if requested, then overlapping hits and then first blocking hit”. Since your character only overlaps with this trace channel that means your sweep is going to keep passing through your character, returning an event for every physical body in the character that it overlaps with. If you want to only have one event be returned, either set the trace channel to block on your character or use a different trace/sweep function.

thanks

but i have multiple meshes on my characters so i only set the capsule component to block the custom collision channel but it still happens 2 times

Maybe you can check to see which components/bones are in each hit result returned. That might provide some clues as to what is going on. You can then use that information to double check all your collision settings on each bone/component, you might have multiple problems going on.

it is very odd because i am using UE4 Shooter Game demo and it is doing this but on other demos don’t do it

I think I understand what you’re trying to do a bit better now. Unreal’s damage system actually has a built in function to do what you’re trying to do, it’s called Apply Radial Damage.

this would be the correct answer but i also want to do other things that can only be done if i get an array of the hit characters

Bellow is the debug messages

313155-annotation-2020-09-11-102430.png

i got it working with this

TArray<FHitResult> OutHits;
FCollisionQueryParams Params;
FVector Start = GetRootComponent()->GetComponentLocation();
FVector End = Start + 0.1f;
FCollisionShape CS;
CS.MakeSphere(1000.f);
CS.SetSphere(1000.f);


if (GetWorld()->SweepMultiByObjectType(OutHits, Start, End, FQuat::Identity, FCollisionObjectQueryParams(ECC_Pawn), CS, Params))
{
	int32 Segments = 1;
	DrawDebugSphere(GetWorld(), Start, CS.GetSphereRadius(), Segments, FColor::Blue, true);
}

TArray<AShooterCharacter*> CharactersHit;

for (const FHitResult& ArrayElements : OutHits)
{
	int32 Segments = 1;
	DrawDebugSphere(GetWorld(), ArrayElements.ImpactPoint, 20.f, Segments, FColor::Blue, true);

	AShooterCharacter* HitCharac = Cast<AShooterCharacter>(ArrayElements.GetActor());
	CharactersHit.AddUnique(HitCharac);
}

#But when i add this

for (const AShooterCharacter* const ArrayElements : CharactersHit)
{
	const FString& PlayerName = ArrayElements->GetActorLabel();
	GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Blue, FString::Printf(TEXT("character hit: %s"), *PlayerName));
}

it crashes

and i need this to iterate through the refined CharactersHit which has no duplicates from AddUnique()

You should be able to see where the crash happened in the stacktrace in the crash popup window. Alternatively you could build the visual studio project for “Development Editor” target and debug locally, then recreate the crash. I suspect it might be the case that your cast to AShooterCharacter might be failing, leading to dereferencing null pointers later on.