[Solved] Collision (overlap) issues [c++] with spawned actors

I am writing a simple custom sensing component for a game I’m currently working on. The component inherits from UActorComponent and is attached to a custom type of Character (that inherits from ACharacter). I then create blueprints from the custom character class and assign them to spawn volumes for spawning at BeginPlay.

The sensing component has a private USphereComponent member that is used for collision checking (on overlap). I also register two delegates in this class that handle OnComponentBeginOverlap and OnComponentEndOverlap (in this case I just output debug messages).

Here is the SensingComponent.h


#pragma once
#include "Components/ActorComponent.h"
#include "CoreMinimal.h"

#include "SensingComponent.generated.h"

UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class AREA7UNREAL_API USensingComponent : public UActorComponent
{
    GENERATED_BODY()

public:
    // Sets default values for this component's properties
    USensingComponent();

    // Called every frame
    virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

    // React to sensing sphere overlaps
    UFUNCTION(BlueprintCallable, Category = "Agent|Sensing")
        void OnBeginOverlapSensingSphere(class UPrimitiveComponent* HitComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);

    UFUNCTION(BlueprintCallable, Category = "Agent|Sensing")
        void OnEndOverlapSensingSphere(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

    // EDITOR ONLY: Dynamic property change updates
#if WITH_EDITOR
    virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif


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

    // The sensing sphere radius
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agent|Sensing")
        float sensingSphereRadius;

private:

    /** The sensing sphere */
    class USphereComponent* SensingSphere;

};

And here’s the SensingComponent.cpp


#include "SensingComponent.h"
#include "Runtime/Engine/Classes/Components/SphereComponent.h"
#include "Runtime/Engine/Classes/Engine/Engine.h"


// Sets default values for this component's properties
USensingComponent::USensingComponent()
{
    // Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
    // off to improve performance if you don't need them.
    PrimaryComponentTick.bCanEverTick = true;

    // Initializing sensing sphere radius
    sensingSphereRadius = 500.0f;

    // Creating the sensing sphere
    SensingSphere = CreateDefaultSubobject<USphereComponent>(TEXT("SensingSphere"));
    if (SensingSphere)
    {
        SensingSphere->SetSphereRadius(sensingSphereRadius);
        SensingSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap);
        SensingSphere->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
        SensingSphere->OnComponentBeginOverlap.AddDynamic(this, &USensingComponent::OnBeginOverlapSensingSphere);
        SensingSphere->OnComponentEndOverlap.AddDynamic(this, &USensingComponent::OnEndOverlapSensingSphere);
        SensingSphere->bGenerateOverlapEvents = true;
    }
    else
    {
        UE_LOG(LogTemp, Error, TEXT("Could not create SensingComponent's sensing sphere."));
    }
}


void USensingComponent::OnBeginOverlapSensingSphere(class UPrimitiveComponent* HitComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
    UE_LOG(LogTemp, Warning, TEXT("*** Collision - [BEGIN OVERLAP]. Collider: %s"), *GetOwner()->GetName());
    TArray<AActor*> actors;
    SensingSphere->GetOverlappingActors(actors);
    for (AActor* actor : actors)
    {
        UE_LOG(LogTemp, Warning, TEXT("*** Collision target: %s"), *actor->GetName());
    }

}

void USensingComponent::OnEndOverlapSensingSphere(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
    UE_LOG(LogTemp, Warning, TEXT("*** We got a collision - END OVERLAP."));
}

// Dynamically update the sensing sphere's radius in the BP viewport and code when changing the value in BP
#if WITH_EDITOR
void USensingComponent::PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent)
{
    Super::PostEditChangeProperty(PropertyChangedEvent);

    FName PropertyName = (PropertyChangedEvent.Property != NULL) ? PropertyChangedEvent.Property->GetFName() : NAME_None;

    if ((PropertyName == GET_MEMBER_NAME_CHECKED(USensingComponent, sensingSphereRadius)))
    {
        UFloatProperty* property = static_cast<UFloatProperty*>(PropertyChangedEvent.Property);
        sensingSphereRadius = property->GetPropertyValue(&sensingSphereRadius);
        SensingSphere->SetSphereRadius(sensingSphereRadius);
    }
}
#endif

The main problem

The ‘OnComponentBeginOverlap’ event triggers correctly when I manually place my character blueprints into the level and they are close enough together to have their USpereComponents overlap.

The same does NOT work, though, when the characters are spawned instead of manually placed in the level. Mind you, the characters spawn correctly in the level.

  • How do i fix this so it works with spawned actors / characters as well?

Here’s how I spawn the characters (from a spawn volume that spawns a certain type of character). The SpawnAgent() function is triggered at BeginPlay().


void ASpawnVolume::SpawnAgent()
{
    // Check if we have something to spawn
    if (ActorToSpawn != NULL)
    {
        // Check for valid world
        UWorld* const world = GetWorld();
        if (world)
        {
            // Set up the spawn parameters
            FActorSpawnParameters spawnParameters;
            spawnParameters.Owner = this;
            spawnParameters.Instigator = Instigator;


            // Get random location within the volume to spawn at
            FVector spawnLocation = GetRandomPointInVolume();

            // Rotate the agent around z randomly for a bit of variety
            float zRotation = FMath::FRand() * 360.0f;
            FRotator spawnRotation = FRotator(0, zRotation, 0);


            // Spawn the agent and remember it
            AOrisAgentCharacter* const spawnedAgent = world->SpawnActor<AOrisAgentCharacter>(ActorToSpawn, spawnLocation, spawnRotation, spawnParameters);
        }
    }
    else
    {
        FString debugString = GetName();
        UE_LOG(LogTemp, Warning, TEXT("There is no Actor to Spawn."));
    }
}


The secondary problem

My characters (using the SensingComponent) also do NOT trigger when overlapping with a static mesh placed in the level (it is set to BlockAll and has GenerateOverlapEvents set to true).

  • What am I doing wrong here?

Cheers,
Michael

PS 1: I noticed that BeginOverlap and EndOverlap events are triggered whenever I open/close the blueprint of a character that uses the above SensingComponent…is this intentional behavior when opening blueprints?

PS 2: I also noticed that the USphereComponent’s bounds are shown in the Blueprint editor viewport but NOT in the game, no matter what visibility flags I set on the sphere. Is this to be expected behavior?

1 Like

Additional finding: When I run the game in simulation mode and then click ‘Possess’ to spawn the player character, the collisions **DO **work. If I jump into the game by the PIE (play in editor) feature, the collisions DO NOT work.

Okay, this is weird: The above (simulation mode + possess) triggers even when the newly spawned character is way outside the trigger volume…

Update: When I move the “SetSphereRadius” to the BeginPlay function like this:


void USensingComponent::BeginPlay()
{
   Super::BeginPlay();

   if (SensingSphere)
   {
      SensingSphere->SetSphereRadius(sensingSphereRadius, true);
   }
   else
   {
      UE_LOG(LogTemp, Error, TEXT("Could not create SensingComponent's sensing sphere."));
   }
}


The BeginOverlap events now DO fire… But it does not matter anymore how large the SensingSphere’s radius is - they always fire (even if the radius if 0.0f)… :o

I’d greatly appreciate if anyone can make sense of this…

Solved it (finally): I found out that the USphereComponent I used for the overlap checking was at world origin (instead of at the owning character’s location) and did not move with the character. All I needed to to is attach the USphereComponent to the RootComponent of the parent.

Thanks for taking the time to let us know what the problem was. I was having a similar issue and fixed it thanks to you!