OnComponentBeginOverlap only works in BeginPlay

I’m just coming over from Unity and have been following a tutorial on this but am very confused. The guide set up a challenge to have us set up a launching platform that works as follows;

  • If you walk into it you get launched
  • if you push any overlap enabled actors, they also get launched.

The issue is that I couldn’t get OnComponentBeginOverlap to fire to save my life. It got to the point that I gave up and looked at what he did, I even ended up downloading the working zip and even replaced my c++ files with his, and still wouldn’t fire. After googling for several hours I ended up finding out that ‘sometimes’ you have to bind the overlap function in ‘BeginPlay’. After doing this, with his code that was working in his project but not mine (he started in 4.18 but upgraded to 4.20, but I opened it in 4.22), I got the thing working.

What is going on? Should I be worried? Is my project corrupt?

Oh and just so you know, yes, I set the function as a UFUNCTION(). in the header file.

Here is working the .cpp (with the slight edit)

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

#include "FPSLaunchPad.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Components/ArrowComponent.h"
#include "GameFramework/Character.h"
#include "Kismet/GameplayStatics.h"


// Sets default values
AFPSLaunchPad::AFPSLaunchPad()
{
	OverlapComp = CreateDefaultSubobject<UBoxComponent>(TEXT("OverlapComp"));
	OverlapComp->SetBoxExtent(FVector(75, 75, 50));
	RootComponent = OverlapComp;

	MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComp"));
	MeshComp->SetupAttachment(RootComponent);

	LaunchStrength = 1500;
	LaunchPitchAngle = 35.0f;
}

void AFPSLaunchPad::BeginPlay()
{
	// Bind to Event
	OverlapComp->OnComponentBeginOverlap.AddDynamic(this, &AFPSLaunchPad::OverlapLaunchPad);
}

void AFPSLaunchPad::OverlapLaunchPad(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	// Make rotator with our specified 'pitch' and convert to a direction vector * intensity
	FRotator LaunchDirection = GetActorRotation();
	LaunchDirection.Pitch += LaunchPitchAngle;
	FVector LaunchVelocity = LaunchDirection.Vector() * LaunchStrength;

	UE_LOG(LogTemp, Log, TEXT("Hit launch pad!"));

	ACharacter* OtherCharacter = Cast<ACharacter>(OtherActor);
	if (OtherCharacter)
	{
		// Launch Player! Both booleans give consistent launch velocity by ignoring the current player velocity
		OtherCharacter->LaunchCharacter(LaunchVelocity, true, true);

		// Spawn FX
		UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ActivateLaunchPadEffect, GetActorLocation());
	}
	// Did not overlap a player, so check if it's a physics simulating actor we can launch
	else if (OtherComp && OtherComp->IsSimulatingPhysics())
	{
		OtherComp->AddImpulse(LaunchVelocity, NAME_None, true);

		// Spawn FX
		UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ActivateLaunchPadEffect, GetActorLocation());
	}
}

*Edit
Here’s the Header

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "FPSLaunchPad.generated.h"

class UBoxComponent;
class UStaticMeshComponent;
class UArrowComponent;


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

protected:

	UPROPERTY(VisibleAnywhere, Category = "Components")
	UStaticMeshComponent* MeshComp;

	UPROPERTY(VisibleAnywhere, Category = "Components")
	UBoxComponent* OverlapComp;

	// Marked with ufunction to bind to overlap event
	UFUNCTION()
	void OverlapLaunchPad(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

	/* Total impulse added to the character on overlap
		Marked 'EditInstanceOnly' to allow in-level editing of this property per instance. */
	UPROPERTY(EditInstanceOnly, Category = "LaunchPad")
	float LaunchStrength;

	/* Angle added on top of actor rotation to launch the character. 
		Marked 'EditInstanceOnly' to allow in-level editing of this property per instance. */ 
	UPROPERTY(EditInstanceOnly, Category = "LaunchPad")
	float LaunchPitchAngle;

	/* Effect to play when activating launch pad */
	UPROPERTY(EditDefaultsOnly, Category = "LaunchPad")
	UParticleSystem* ActivateLaunchPadEffect;

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

};

Thanks for the reply Rodja, I’ve updated my question to include the header file.

The course I’m following is an Epic Games approved Udemy course by Tom Looman (a former Epic Games engineer). I think the issue might be that the course is a few years old and only updated to 4.20. I did see that further on in one of the later lectures he say’s that some people will need to Bind these functions in BeginPlay() instead of how he originally does it.

I’ve started doing that, and since binding in BeginPlay() my problems with OnComponentBeginOverlap not working have gone away. I’m definitely going to remember to use IsBound() from now on whenever I have these issues, that sounds like it’ll help a lot!

Thanks

Hello, welcome!

I don’t think your project is corrupted. Some things to consider:

  • A good idea is always to search the engine code to see how epic developers are doing what you’re trying to do. Try searching for OnComponentBeginOverlap or for AddDynamic throughout the source code to see how they do it.
  • Maybe you’re adding the delegate multiple times? Try removing calling RemoveDynamic before calling the AddDynamic.
  • You can also use the IsBound() method on your OnComponentBeginOverlap to check if you need to call RemoveDynamic.

Can you also post your header file? Sometimes the problem is there.

Sounds good! There are other places that you can bind the function. If you want to dive a little deeper take a look at Actor.h header file.

You can also use PostLoad(), for instance.

Just don’t forget to RemoveDynamic (or unbind) whatever you bonded on EndPlay() and BeginDestroy()