x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

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.

alt text

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.

alt text

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

Product Version: UE 4.20
Tags:
more ▼

asked Jan 23 '19 at 03:34 PM in C++ Programming

avatar image

3darkman
28 2 6 5

(comments are locked)
10|2000 characters needed characters left

3 answers: sort voted first

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!

more ▼

answered Jan 23 '19 at 06:53 PM

avatar image

the_batch
1.2k 4 5 8

avatar image 3darkman Jan 23 '19 at 07:10 PM

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?

avatar image 3darkman Jan 23 '19 at 07:25 PM

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)));
avatar image the_batch Jan 23 '19 at 08:19 PM

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!

avatar image the_batch Jan 23 '19 at 08:08 PM

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!

avatar image 3darkman Jan 23 '19 at 08:27 PM

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.

(comments are locked)
10|2000 characters needed characters left

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?

more ▼

answered Jan 23 '19 at 04:09 PM

avatar image

the_batch
1.2k 4 5 8

avatar image 3darkman Jan 23 '19 at 04:15 PM

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.

avatar image the_batch Jan 23 '19 at 04:21 PM

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?

avatar image 3darkman Jan 23 '19 at 04:39 PM

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.

avatar image the_batch Jan 23 '19 at 06:12 PM

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

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.)

avatar image 3darkman Jan 23 '19 at 06:18 PM

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.

avatar image the_batch Jan 23 '19 at 06:58 PM

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.

(comments are locked)
10|2000 characters needed characters left

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

more ▼

answered Jan 23 '19 at 05:16 PM

avatar image

Khubur
140 7 11 12

avatar image 3darkman Jan 23 '19 at 06:05 PM

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.

avatar image Khubur Jan 23 '19 at 10:03 PM

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.

(comments are locked)
10|2000 characters needed characters left
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question