[Question][Request?] Radial Trace

I’ve been thinking (and looking trough code), for something that would allow me to create radial trace.

Simply speaking radial trace would search in set radius from point of origin (that could specified by user, or attained from example line trace hit info) for actors on specific collision channels.

The closest thing that come to this is probably UGameplayStatics::ApplyRadialDamage. But problem is, that this function have very specific use, and more over it calls AActor::TakeDamage (as far I can tell).

I don’t need to deal damage. What I need is to get array of actors (HitResult) in radius from trace origin.

I’ve been trying to think about solution on my own, but to be perfectly honest it bit beyond my expertise to think about something efficient right now, as my line o thinking basically revolves about brute force line tracing in radius from origin point. And I don’t really think that is efficient solution to the problem.

Why do I need radial trace ? Two use cases for right now:

  1. Hit actor with line trace. Now on actor hit execute radial trace, and jump to another actor (firs hit), make another radial trace, jump to first found actor, etc. X amount of times.
  2. I have more or less custom damage system right now. TakeDamage from AActor is to limiting, or would become far to complex if I would try to push all possible damage trough it. As right now damage can be dealt to all attributes of character (and each attribute can take damage bit differently). There is of course build in system for radial damage. But since I can’t really use it I need to build my own and build in radial trace would probably solve the hardest part.

Hello Lukasz,

Your best bet is to use UWorld. You can get the instance of the world that any AActor exists in by calling their GetWorld() method. The class itself provides a bunch of world sampling methods, such as OverlapMulti. OverlapMulti will return a list of colliding AActors from multiple primitive types, including a sphere. Here is some example code:

TArray overlaps;
AActor* Origin = 0;
float Radius = 150.0f;

if (Origin->GetWorld()->OverlapMulti(
    //output list
    overlaps, 
    //origin location
    Origin->GetActorLocation(), 
    //origin rotation
    FQuat::Identity, 
    //collision channel
    ECollisionChannel::ECC_Pawn, 
    //collision primitive
    FCollisionShape::MakeSphere(Radius), 
    //collision parameters
    FCollisionQueryParams())) 
{
    ...
}

If anyone is interested here is simplified BlueprintLibrary code for use with Sphere Sweep.

void URPGEffectBPLibrary::MultiRadialHitCheck(FHitResult hitData, float radius, TArray& OutHitsResult)
{
	static FName AbilityTag = FName(TEXT("AbilityTrace"));
	UWorld* world = nullptr;
	if(hitData.GetActor())
	{
		world = GEngine->GetWorldFromContextObject(hitData.GetActor());
	}

	TArray hitList;
	FCollisionShape collShape;
	FCollisionQueryParams queryParam;
	collShape.ShapeType = ECollisionShape::Sphere;
	collShape.SetSphere(radius);

	queryParam.bTraceComplex = true;
	queryParam.TraceTag = AbilityTag;
	queryParam.bTraceAsyncScene = true;

	FVector startLocation = hitData.ImpactPoint;

	DrawDebugSphere(world, startLocation, radius, 32, FColor::Red, false, 10.0f);
	world->SweepMulti(hitList, startLocation, startLocation+radius, FQuat(1.0f, 1.0f, 1.0f, 1.0f), ECollisionChannel::ECC_Pawn, collShape, queryParam);

	if(hitList.Num() > 0)
	{
		OutHitsResult = hitList;
	}
}

I’m trying to minimize the amount in input parameters, so they won’t make unnecessary clutter within blueprint editor.

Instead of passing trough reference FHitResult, we can return actors like that:

	if(hitList.Num() > 0)
	{
		for(FHitResult& hit : hitList)
		{
			ActorList.AddUnique(hit.GetActor());
		}
		return ActorList;
	}

Hope it save some time other people who might encounter that problem in future.

#A Simple Distance Test

I am not sure why you need a radial trace at all though

For any case where you want a radial trace

why dont you just do a ActorIterator Loop and find all actors that are within a certain distance of your origin point?

Keep in mind that a simple distance calculation is a lot more low-cost than a trace which has to test collision over distance.

Additionally you are not going to loop over ALLLL actors, you will specify a subclass of actor like CreaturesThatCanBeJumpedTo, or simply your base Character class.

Also keep in mind that the ActorIterator is VERY fast!

It will never be an effort for that loop compared to tracing in a radius or doing a Volume / Component Trace.

The reason again is Collision

Collision is one of the most expensive things for the game engine to test.

If you do want to be as efficient as possible,
I recommend using FVector::DistSquared I think it is faster than the non-squared version.

#My Tutorial

Here is my tutorial on iterating over only a specific class of actors

http://forums.epicgames.com/threads/972861-TUTORIALS-C-for-UE4-gt-gt-New-Change-Global-Post-Process-During-Game!?p=31667115&viewfull=1#post31667115

you can do the for loop

check distance of the looped actor to your origin point:

if(FVector::DistSquared(origin, Itr->GetActorLocation()) < 262144)  //512^2

and then make sure to BREAK when you find the first suitable actor for your needs,

break;

this ends the loop as quickly as possible

#Done!

Then you are done!

no tracing involved :slight_smile:

Rama

PS:

here’s the core example from my tutorial

void AYourController::PrintAllSMAActorsLocations()
{
  //EngineUtils.h
	TActorIterator< AStaticMeshActor > ActorItr =
    TActorIterator< AStaticMeshActor >(GetWorld());
	
	//While not reached end (overloaded bool operator)
	while (ActorItr)
	{
		ClientMessage(ActorItr->GetClass()->GetDesc());
		ClientMessage(ActorItr->GetActorLocation().ToString());
		
		//next actor
		++ActorItr;
	}
}

This would indeed bit quite a bit faster than a physics check. There are two downsides however:

  1. It is inaccurate for larger actors that could overlap the trace radius but not have their center points within it.
  2. It doesn’t take line of sight into account. SweepMulti (as opposed to OverlapMulti as I suggested) will still return overlapping hits, ending on a blocking hit in order of distance from the origin. This could be used to not return actors behind walls and such.

It would depend a lot on your use case. If you don’t need either of these features, I would suggest using the pure distance comparison. Otherwise, go with the physics check.

Also, the reason DistSquared is faster is because it doesn’t need to root the result like Dist does (some people might think Dist would be quicker because it doesn’t need to square it). Behind the scenes it’s just running a dot product.

the Line of Sight issue is a great point Andrew!

I was envisioning him only needing a short distance like 512 or so for the actor to jump to, so the chance of a wall being in the way and being thin enough seems relatively small

but it is a great point to keep in mind :slight_smile:

I think the advantages of the distance calc outweigh any extra consideration of the wall issue as long as the distance of allowed jumping is pretty small

#solution for longer distances

you can always do a single trace to test a potential actor after it has been found with the simple distance calc, to confirm it is in line of sight, and if not then move on to the next actor in the loop.

This will keep you traces to a minimum and will likely not run much ever, if the allowed distance is pretty small.

#Accuracy of Single Traces + ActorIterator

It will always be faster than a series of traces in radial fashion, and also infinitely more ACCURATE

because radial traces are “guessing” where an actor might be

the distance calc with actor iterator knows exactly where the actors are and then your single confirmation trace is always perfectly accurate.

Actually, a single sphere overlap (assuming it doesn’t move, which is the case of OverlapMulti) should be about as efficient as a line trace. It would also be more accurate. The reasons:

  • The sphere overlap doesn’t send out a series of line traces, it does a single sphere-to-bounding box comparison for all potential actors.
  • A line trace stills need to find a collision sample area (say all actors in a radius equal to the length of the line) for it to do more accurate checks against. It then checks against the bounding box/capsule, then against the complex collider if opted for in the collision parameters, for each sampled actor. The sphere overlap on the other hand would have the same sample list of colliders as the line trace, it would just run a different check against them (using radius instead of a solid line). In the end, you’re getting the same number of tested actors. There may be some optimization that would reduce the number of sample actors, but it cannot perfectly reduce the sample area and it would involve its own overhead.
  • A line trace against the actor would still be significantly less accurate, as it would only cast against the actors’ center point. If the center is obscured, but the majority of the actor is visible, it would fail when it should succeed. This could easily be the case on uneven ground, as it is standard practice to have skeletal mesh center points at the base between the feet. A small bump in the ground, or a tile, or anything, would result in a failed check.
  • If you’re going to line trace each actor found with the iterator, you would have to run multiple line traces (each determining the sample area and doing separate collision checks) where a sphere overlap would still only require one pass.

I agree an overlap sphere test could be great too!

Lukasz let us know what method ends up suiting your needs!

Rama

Hey thanks for help guys.
After you both pointing me in right direction, I’m now far more aware of plethora of method to do all kind of area search for objects.

There is even something like:

SphereTraceMulti_NEW

But for some reason typing trace in visual studio didn’t find it, but sphere keyword worked -;-.

I’m marking Andrew answer, since it is the thing that is more suited to my needs. After all we don’t want actors behind walls or cover to be hit (;.
But you both helped great deal!