How to call properly call BeginEventOverlap in c++?

I’m trying to create a “Death box” of sorts for my side scroller project - it will be placed under the level, and when the player falls far enough it will change the game state - destroying the player and outputting a message - such as “You lose”, etc.

but what i’m struggling to do is get the EventBeginOverlap function to call the necessary function - everytime i compile, drag the volume into the level and test, nothing seems to happen.

as of now i’ve simply handled the begin overlap event in blueprint, and that’s seemed to work, but in future, how would one go about handling such behaviour correctly? do i even use C++, or should i opt to use the KillZVolume, instead?
i’ve seen it within the “volumes” section and wondered if it would be right for the job, if so, how does one use it so that it destroys the character and switches the game mode when entered? thanks in advance…

my game mode header:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "Platformer_ProjectGameMode.generated.h"

UENUM(BlueprintType)
enum class eGameState : uint8 //stores all the possible play states within the game.
{
	EPlaying,
	Ewon,
	ELost
};

UCLASS(minimalapi)
class APlatformer_ProjectGameMode : public AGameModeBase
{
	GENERATED_BODY()

public:
	APlatformer_ProjectGameMode();

	//imagine the viewport being a larger class, the menus being widgets that have smaller classes which fit into the larger class.

	//method to replace current widget in the viewport with another widget from another class.
	UFUNCTION(BlueprintCallable, Category = "Menus/widgets")
	void replaceMenuWidget(TSubclassOf<UUserWidget>newWidgetClass); //in the parameters we are passing a different widget class.


	//functions handling the game state.
	UFUNCTION(BlueprintPure, category = "Game states")
	eGameState getCurrentGameState(); //returns the current game state.

	UFUNCTION(Blueprintcallable, category = "Game state")
	void setCurrentGameState(eGameState newGameState);

	UFUNCTION(BlueprintCallable, category = "Game state")
	void handlePlayState(eGameState newGameState); //function that will handle game states accordingly
protected:

	virtual void BeginPlay() override;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, category = "Menus/widgets")
	//create a sublass of the UUWidget class and name it "Starting widget class."
	TSubclassOf<UUserWidget> initWidgetClass; //the widget class we are initially displaying.

	UPROPERTY() //this needs to be left blank, as was left blank in the UMG tutorial.
	UUserWidget * currentWidget; //current widget we are using.


private:
	//current playState for the game.
	eGameState currentPlayState;
};

my game mode cpp file:

#include "Platformer_ProjectGameMode.h"
#include "PlatformerHUD.h"
#include "Platformer_ProjectCharacter.h"
#include "UObject/ConstructorHelpers.h"
#include "Blueprint/UserWidget.h" //this is required in order to use widget functions like removeFromViewport.
#include "Platformer_ProjectCharacter.h" //i'll need this to cast to the character class.
#include "EngineMinimal.h"//i'll need this in order to use gameplayStatics and get the player character.
#include "Engine.h" //we'll need this for writing debug messages.

APlatformer_ProjectGameMode::APlatformer_ProjectGameMode()
{
	// set default pawn class to our Blueprinted character
	static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/SideScrollerCPP/Blueprints/SideScrollerCharacter"));
	if (PlayerPawnBPClass.Class != NULL)
	{
		DefaultPawnClass = PlayerPawnBPClass.Class;
	}
}


void APlatformer_ProjectGameMode::BeginPlay()
{
	Super::BeginPlay();
	replaceMenuWidget(initWidgetClass); //when the game begins, place the starting menu widget onto the screen.
}


void APlatformer_ProjectGameMode::replaceMenuWidget(TSubclassOf<UUserWidget>newMenuWidget)//method to replace the current menu widget with a widget made from another class: we pass the class as a parameter.
{
	//check if there is something in the currentWidget. if there is we remove it from the viewport and declare it null. if not, this if statement is skipped.
	if (currentWidget != nullptr)
	{
		currentWidget->RemoveFromViewport();
		currentWidget = nullptr;
	}

	currentWidget = CreateWidget<UUserWidget>(GetWorld(), newMenuWidget); //create a new widget using the create widget function, passing in the class parameters as our arguments.

	if (currentWidget != nullptr) //check to see if there is something inside the viewport(which there should be), adding it to the viewport.
	{
		currentWidget->AddToViewport();
	}

}

eGameState APlatformer_ProjectGameMode::getCurrentGameState()
{
	return eGameState();
}

void APlatformer_ProjectGameMode::setCurrentGameState(eGameState newGameState)
{
	currentPlayState = newGameState;
	handlePlayState(currentPlayState); //handles the play state immediately after setting it.
	//commented out due to possible redundancy.
}



void APlatformer_ProjectGameMode::handlePlayState(eGameState newGameState)
{
	//changed testCharacter to myCharacter.
	//if the game state is lost, get the player character and destroy them(requires casting). class will automatically be set to pawn.
	if (currentPlayState == eGameState::ELost)
	{
		//create a test character and cast him to our actual character.
		APlatformer_ProjectCharacter * myCharacter = Cast<APlatformer_ProjectCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));

		//check if the cast is successful first, good practice.
		if (myCharacter)
		{
			myCharacter->death(); //destroys the player character/setting the default pawn to pawn.
		}

		//write a debug message to the screen stating a game mode change(we won't need this, it's really only a check).
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Game mode has changed to ELost. player should recive some type of message saying as such"));
		}
	}
	

}

my deathbox header file:

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

UCLASS()
class PLATFORMER_PROJECT_API ADeathBox : public AActor
{
	GENERATED_BODY()
	

private:

	//create the box collision component that will cause the player actor to be destroyed when entered.
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, category = "Death box", meta = (AllowPrivateAccess = "true"))
	class UBoxComponent * boxCollision;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, category = "Death box", meta = (AllowPrivateAccess = "true"))
	class UStaticMeshComponent * staticMesh; //the static mesh will never be overidden - it is simply there for the sake of testing.

public:	
	// Sets default values for this actor's properties
	ADeathBox();

	//mandatory forceinline getter fucntion when creating a component.
	FORCEINLINE class UBoxComponent * getBox() const { return boxCollision; }
	//forceinline component for the static mesh.
	FORCEINLINE class UStaticMeshComponent * getMesh() const { return staticMesh; }

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

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

	//behaviour which outlines what happens when the death box is entered.
	virtual void whenEntered(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);


	
	
};

my deathbox cpp file:

#include "DeathBox.h"
#include "Components/BoxComponent.h" //i need this header file in order to attach the box collision component to the rootComponent.
#include "Platformer_ProjectGameMode.h" //include the game mode so that we can change the current game state.
#include "EngineMinimal.h" //we need this so that we can call UGameplayStatistics::getGameMode().
#include "Engine.h" //use this to write on screen debug messages.


// Sets default values
ADeathBox::ADeathBox()
{
 	// 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;

	staticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Static mesh component"));
	RootComponent = staticMesh;

	boxCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("Box collision compoenent for the death box"));
	//bind the whenEntered function to the begin overlap event.
	boxCollision->OnComponentBeginOverlap.AddDynamic(this, &ADeathBox::whenEntered);//lol i forgot to add this.
	boxCollision->SetWorldScale3D(FVector(2.0f, 2.0f, 2.0f));
	boxCollision->AttachToComponent(RootComponent, FAttachmentTransformRules::SnapToTargetIncludingScale); //attach the box collision component to the root component, as it cannot become it.

}

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

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

}

void ADeathBox::whenEntered(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	//get the game mode and cast it to our game mode.
	APlatformer_ProjectGameMode * myGameMode = Cast<APlatformer_ProjectGameMode>(UGameplayStatics::GetGameMode(GetWorld()));

	if (myGameMode) //quick check to see if our cast was successful.
	{
		//set it to ELost, which will then destroy the player character.
		myGameMode->setCurrentGameState(eGameState::ELost);

		//write a debug message signalling that the player should be dead.
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("Player has entered death box. change of state should now occur."));
		}

	}
}

I think it might be as simple as adding a UFUNCTION() macro infront of your whenEntered function. Go ahead and try that and see if you can get your debug message to display.

     //behaviour which outlines what happens when the death box is entered.
     UFUNCTION()
     virtual void whenEntered(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

Cabewz is correct, when using .AddDynamic you need to use a UFUNCTION, otherwise the Engine annoyingly does nothing without warning or error I think.

You could instead use the EndPlay() function on your character however, and put your KillZ in the world settings to your correct point.

Thanks, you helped fix another silly mistake of mine, lol

I’m sure this has hit all of us multiple times. There is no warning when you miss it which really sucks.