How to use the function SphereOverlapActors?

I am using this function to detect the targets of an attack, but it is detecting the targets beyond the radius of the sphere specified by me, in this case, 1.0f.

266290-50836349-816303878713195-8393939763811319808-n.png

In this image above I show the distance of the attacker to the point of origin of the sphere and the distance of the attacker to the location of the detected target.

In this image above I show the function call and inspection of the radius and the position of origin of the sphere.

In the screenshot you posted, the largest error above 1.0 was the value 1.000123, which is definitely close enough for most gameplay purposes. (It’s over by just 0.01%!)

Are you trying to correct such instances where the distance to the sphere is just slightly larger than 1.0? If so, I’m not sure there’s much you can do about it. You do appear to be using UKismetSystemLibrary::SphereOverlapComponents correctly, so no worries there. The inaccuracy is probably due to limitations in how precise PhysX is willing to be, or perhaps even due to limitations in floating point precision.

At some point, you have to accept a certain margin of error. For gameplay purposes, 0.01% is probably close enough, right?

Hi the-batch, thanks for the answer.

Follows some clarifications, perhaps I have not been clear, for test purposes only, I am placing as the point of origin of the sphere 1.0f and the radius 1.0f, the question is not the different decimals, but the distance to the target that,in some cases, exceeds 300.0f and even so the sphere is giving overlap in it.

So, you’re calling SphereOverlapActors(...) using a parameter called spherePos to set the location of the sphere.

Then, to get your distance, you’re comparing the overlapped actor’s location ( actor->GetActorLocation() ) to the location of your character: Character->GetActorLocation()

Your code is telling you that the overlapped actor is 300+ units away from your character actor’s location — not your test-sphere’s location.

Or is spherePos equal to Character->GetActorLocation()?

In other words…

float DistanceFromCharacterToOverlappedActor = (actor->GetActorLocation() - Character->GetActorLocation()).Size();
float DistanceFromSphereToOverlappedActor = (actor->GetActorLocation() - spherePos).Size();

Right? Or am I missing something?

Almost that, here is some extra information that I did not really show in my code and so it should be complicating your understanding.

float distance = 1.0f;  //just for the purpose of testing

float spherePos = Character->GetActorLocation() + (actor->GetActorForwardVector() * distance);

float DistanceFromSphereToOverlappedActor = (Character->GetActorLocation() - spherePos).Size();  // I was wondering if the sphere was being generated in the correct position, at 1.0f away from the attacker

then the sphere is actually being generated close to the attacker, but the target is at 300f of the attacker, and according to the sphere radius I am specifying, should not be happening overlap, at least I think.

Here I created AActor instance and add data member USphereComponent and binding dynamic events on what will be called. Also here is OverlappedActors pointer of AActor vectors represents overlapped actors binding dynamically on generated overlap events.

Header File

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Runtime/Engine/Classes/Components/SphereComponent.h"
#include "SphereActor.generated.h"

UCLASS(Blueprintable)
class TESTCODE_API ASphereActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ASphereActor();

	// Sphere Overlapping Begin Event
	UFUNCTION()
	void OnBeginOverlap(class UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

	// Sphere Overlapping End Event
	UFUNCTION()
	void OnEndOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

private:
	// Sphere component data member for holding
	UPROPERTY(EditAnywhere, Category = "Gameplay Collision")
	USphereComponent* SphereComponent;

	// Holding Actors that overlapped on SphereComponent
	UPROPERTY(EditAnywhere)
	TArray<AActor*> OverlappedActors;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

};

Source File

// Fill out your copyright notice in the Description page of Project Settings.

#include "SphereActor.h"
#include "Runtime/Core/Public/Math/UnrealMathUtility.h"
// Sets default values
ASphereActor::ASphereActor()
{
 	// 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;
	SphereComponent = CreateDefaultSubobject<USphereComponent>("Collision Sphere");
	SphereComponent->SetSphereRadius(120.0f);
	SphereComponent->OnComponentBeginOverlap.AddDynamic(this, &ASphereActor::OnBeginOverlap);
	SphereComponent->OnComponentEndOverlap.AddDynamic(this, &ASphereActor::OnEndOverlap);
}

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

void ASphereActor::OnBeginOverlap(UPrimitiveComponent * OverlappedComp, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
	// Assume you calculated precision, FMath::RoundToFloat makes you more predictable
	auto y = FMath::RoundToFloat(1.1);

	// Add element to vector
	if(OtherActor) OverlappedActors.Add(OtherActor);
}

void ASphereActor::OnEndOverlap(UPrimitiveComponent * OverlappedComp, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex)
{
	// Remove actor.
	if (OtherActor) OverlappedActors.Remove(OtherActor);
}

// Called every frame
void ASphereActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	for (auto SomeActor : OverlappedActors)
	{
		
		auto f = FVector::Dist(SphereComponent->GetComponentLocation(), SomeActor->GetActorLocation());
		UE_LOG(LogTemp, Warning, TEXT("distance is %f"), f);
	}

}

You may check for more using FNumberFormattingOptions struct that exists to create more precised float conversion explained by Rama on wiki: Here

Thanks for your solution, but I’m really trying to understand how the SphereOverlapActors function works and find out why the form I’m using is not working.

I can put parts of the code, I need to know which ones would be needed, so I’ll try to contextualize a bit.

This code is called by an animation notify, in the attack animation of a character. this function that I sent the print screen, checks through this sphere, if there is an enemy in the range of the attack and if there is one, apply the equivalent damage.

As for an answer that doesn’t technically answer your original question, but hopefully gets you up and running again, maybe try something like this:

(Put this wherever you need to check if enemies are within a certain range)

// Iterate over all enemy actors in the world using the TActorIterator
for (TActorIterator<AYourEnemyPawnClassHere> EnemyItr(GetWorld()); EnemyItr; ++EnemyItr)
{
    FVector EnemyLocation = EnemyItr->GetActorLocation();
    FVector AttackerLocation = Character->GetActorLocation();

    // Do a squared-distance comparison to see if the enemy is within range of this character's attack
    float AttackMaxRange = 300.0f;   // in world units; set this to your weapon's range
    float DistSquared = FVector::DistSquared(EnemyLocation, AttackerLocation);
    bool bEnemyInRange = DistSquared <= AttackMaxRange * AttackMaxRange;
    if (bEnemyInRange)
    {
        // Do stuff to strike the enemy with the attack, deal damage, etc.
    }
}

This approach should be very efficient, since it avoids doing any PhysX collisions checks. And since it uses the squared distance between the locations, there’s not even any square root calculations involved. Nice and quick.

Again, I realize that this answer avoids your original issue and therefore doesn’t actually answer your question, but I hope it’s useful to you anyhow!

I posted another answer that I hope is useful to you, even if it’s not the information you were hoping to find.

If you want to keep debugging what you have, let me know.

Okay. I have a better picture now, but I’m totally stumped as to why you’re not getting distances that are <= 2.0.

Would you be willing to share more of your code? There must be something simple going wrong. There’s some assumption somewhere that is incorrect.

If you don’t want to share more code, could you at least explain what you’re ultimately trying to accomplish? The context would help, and there may be an alternative that simply avoids the issue. (I know that’s technically not a solution, but I hope we can at least get you the results you need, so you can get back to being productive.)

Thanks more one time, the_batch.

I did not know about the square distance and the square root, I did a search now and I understood, thanks for the additional knowledge.

The problem with this solution is that it does not guarantee that the target is in front of the attacker, I have to do a calculation to get the angle between them, any suggestions?

I think this way I can (I’m not at work to test):

FVector distanceVector = Character->GetActorLocation() - Enemy->GetActorLocation();
FMath::RadiansToDegrees(acosf(FVector::DotProduct(Character->GetActorForwardVector(), distanceVector)));

Here’s how I would do it: take the attacking character’s forward vector, and take a vector that points toward each nearby enemy, and get the dot product of the two vectors:

// This goes inside your TActorIterator loop
FVector AttackerForwardVector = Character->GetActorForwardVector();
FVector DirectionToEnemy = EnemyLocation - AttackerLocation;
DirectionToEnemy.Normalize();

// Get the dot product of the forward vector and the vector that points toward the enemy:
float Dot = FVector::DotProduct(AttackerForwardVector, DirectionToEnemy);

// The enemy is in front of the attacker if the dot product is > 0.
//   You can limit the forward angle even further by increasing the value; e.g. Dot > 0.707 would limit the condition to a 90-degree cone in front of the attacker
//    This relationship is based on the cosine of the angle:
//        0 = 180 degree cone (90-degrees on each side of forward vector)
//        0.707 = 90 degree cone (45-degrees on each side of forward vector)
//        1 = 0 degree cone (identical to forward vector, infinitely small, impossible to use)
//        (Negative values allow greater than 180 degrees, in case you want that for another purpose; e.g. -0.5 allows for a 240-degree hit 'zone')
bool bEnemyInFront = Dot > 0.0f;   // using the 180-degree solution for now
if (bEnemyInFront)
{
    // Perform your attack stuff here, e.g. damage, etc.
}

If you’re curious or in need of a headache, you can check out Dot Product on Wikipedia for some serious details for how and why this dot product technique works.

Hopefully that makes sense!

I followed up with my own suggestion before I really saw yours, but now I see that you were getting to the same idea. In any case, I hope it works for you!

Great, so I was going down a path that works. I would still like to know why the overlap function is not working. But I will put that answer with the accepted one, it seems even better performatically and more controllable.

OnBeginOverlap and OnEndOverlap just a member function that takes specific function signature and invokes when overlap delegates binded that on constructor for the actor component which is sphere. The AddDynamic is kind of function pointer that executes specific function when event generated by casting of engine events. It is very similarly add event on blueprints.