Communication between two c++ classes based on AActor

Hello,

I have been trying to learn how to communicate between two C++ classes: the Trigger and the Spawner:

Trigger.h


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

#pragma once

#include "GameFramework/Actor.h"
#include "BlackHawkTrigger.generated.h"

//Forward declaration for class ASpawnBlackHawks - we will use this class
class ASpawnBlackHawks;

UCLASS()
class ZSFS_I_API ABlackHawkTrigger : public AActor{
	GENERATED_BODY()
	
public:	

	ABlackHawkTrigger();

	virtual void BeginPlay() override;
	virtual void Tick( float DeltaSeconds ) override;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "BlackHawkTrigger")
	UBoxComponent* Box;

	//UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "BlackHawkTrigger")
	//class UBlueprint* BlackHawkSpawnerBP;

	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "SpawnBlackHawk")
	ASpawnBlackHawks* BHSpawner;
	//TSubclassOf<class ASpawnBlackHawks> BHSpawner;

	UFUNCTION()
	void SetSpawn();

	UFUNCTION()
	void TriggerEnter(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

	//UFUNCTION()
	//void TriggerExit(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
	
	//Declaration of a pointer of function member of class ASpawnBlackHawks
	void SetbIsOneBH(bool SpawnUpdate);

	ASpawnBlackHawks* GetBlackHawk();

	//UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "BlackHawkTrigger")
	//TSubclassOf<class ASpawnBlackHawks> BHref;
	//ASpawnBlackHawks* BHref;
	//ASpawnBlackHawks* BH;
	

	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "BlackHawkTrigger")
	bool bIsOneBH;
	//ASpawnBlackHawks* BHSpawner;

	//UClass* ClassRef;
	
};


Trigger.cpp


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

#include "ZSFS_I.h"
#include "BlackHawkTrigger.h"
#include "SpawnBlackHawks.h"


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

	Box = CreateDefaultSubobject<UBoxComponent>(TEXT("Box"));
	Box->bGenerateOverlapEvents = true;
	RootComponent = Box;
	Box->OnComponentBeginOverlap.AddDynamic(this, &ABlackHawkTrigger::TriggerEnter);
	//Box->OnComponentEndOverlap.AddDynamic(this, &ABlackHawkTrigger::TriggerExit);
	BHSpawner = CreateDefaultSubobject<ASpawnBlackHawks>(TEXT("BHSpawner"));
	
	bIsOneBH = false;
}

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

	
	
}

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

}

ASpawnBlackHawks* ABlackHawkTrigger::GetBlackHawk() {

	return nullptr;

}

void ABlackHawkTrigger::SetbIsOneBH(bool SpawnUpdate) {

	
	//return SpawnUpdate;

}

void ABlackHawkTrigger::TriggerEnter(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) {
	
	APawn* Trig = Cast<APawn>(OtherActor);

	//ASpawnBlackHawks* BH = Cast<ASpawnBlackHawks>(BHSpawner);
	//&& (OtherActor != this) && (OtherBodyIndex > 0)
	if (Trig && BHSpawner) {
		bIsOneBH = true;
		BHSpawner->SetbIsOneBH(bIsOneBH);
			//BH->SetbIsOneBH(bIsOneBH);
	}
	else {
		UE_LOG(LogTemp, Warning, TEXT("Wrong!"));
	}

	
}

//void ABlackHawkTrigger::TriggerExit(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex) {
//	
//	if (BlackHawk) {
//		ClassRef = BlackHawk->GeneratedClass;
//
//	}
//
//	APawn* Trig = Cast<APawn>(OtherActor);
//	BHSpawner = Cast<ASpawnBlackHawks>(ClassRef);
//
//	if (Trig && BHSpawner) {
//		BHSpawner->SetbIsOneBH(false);
//	}
//}

void ABlackHawkTrigger::SetSpawn() {

}


Spawner.h


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

#pragma once

#include "GameFramework/Actor.h"
#include "SpawnBlackHawks.generated.h"

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

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	// Called every frame
	virtual void Tick( float DeltaSeconds ) override;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnBlackHawk")
	UBoxComponent* WhereToSpawn;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnBlackHawk")
	TSubclassOf<class AHelicopterBase> ObjectToSpawn;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnBlackHawk")
	float SpawnDelayRangeLow;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnBlackHawk")
	float SpawnDelayRangeHigh;

	UFUNCTION(BlueprintPure, BlueprintCallable, Category = "SpawnBlackHawk")
	FVector GetRandomPointInVolume();

	float SpawnDelay;

	float GetRandomSpawnDelay();

	void SpawnPickup();

	float SpawnTime;

	void SetNumberOfBHSpawned();

	TArray<AActor*> CollectedActors;
	
	TArray<AActor*> BlackHawks;

	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "SpawnBlackHawk")
	bool bIsOneBH;

	bool GetbIsOneBH();

	UFUNCTION()
	void SetbIsOneBH(bool SpawnUpdate);
	
};

Spawner.cpp


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

#include "ZSFS_I.h"
#include "SpawnBlackHawks.h"
#include "HelicopterBase.h"

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

	WhereToSpawn = CreateDefaultSubobject<UBoxComponent>(TEXT("WhereToSpawn"));
	RootComponent = WhereToSpawn;

	SpawnDelayRangeLow = 10.0f;
	SpawnDelayRangeHigh = 15.0f;

	SpawnDelay = GetRandomSpawnDelay();

	bIsOneBH = false;

}

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

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

	/*SetNumberOfBHSpawned();

	SpawnTime += DeltaTime;
	
	
	
	bool bShouldSpawn = (SpawnTime > SpawnDelay);

	if (bShouldSpawn) {

		SpawnPickup();
		SpawnTime -= SpawnDelay;

		SpawnDelay = GetRandomSpawnDelay();
	}*/

	if (bIsOneBH) {

		SpawnPickup();
	}
}

void ASpawnBlackHawks::SpawnPickup() {
	if (ObjectToSpawn != NULL) {
		UWorld* const World = GetWorld();
		if (World) {
			FActorSpawnParameters SpawnInfo;
			SpawnInfo.Owner = this;
			SpawnInfo.Instigator = Instigator;

			FVector SpawnLocation = GetRandomPointInVolume();

			FRotator SpawnRotation = FRotator(0.0f, 0.0f, 0.0f);

			//SpawnRotation.Yaw = FMath::FRand() * 360.0f;
			//SpawnRotation.Pitch = FMath::FRand() * 360.0f;
			//SpawnRotation.Roll = FMath::FRand() * 360.0f;

			AHelicopterBase* const SpawnedHelicopter = World->SpawnActor<AHelicopterBase>(ObjectToSpawn, SpawnLocation, SpawnRotation, SpawnInfo);
			
			bIsOneBH = false;
		}
	}
}

float ASpawnBlackHawks::GetRandomSpawnDelay() {

	return FMath::FRandRange(SpawnDelayRangeLow, SpawnDelayRangeHigh);

}

FVector ASpawnBlackHawks::GetRandomPointInVolume() {

	FVector RandomLocation;
	FVector Origin;
	FVector BoxExtent;

	float MinX, MinY, MinZ;
	float MaxX, MaxY, MaxZ;

	Origin = WhereToSpawn->Bounds.Origin;
	BoxExtent = WhereToSpawn->Bounds.BoxExtent;

	MinX = Origin.X - BoxExtent.X / 2.0f;
	MinY = Origin.Y - BoxExtent.Y / 2.0f;
	MinZ = Origin.Z - BoxExtent.Z / 2.0f;

	MaxX = Origin.X + BoxExtent.X / 2.0f;
	MaxY = Origin.Y + BoxExtent.Y / 2.0f;
	MaxZ = Origin.Z + BoxExtent.Z / 2.0f;

	RandomLocation.X = FMath::FRandRange(MinX, MaxX);
	RandomLocation.Y = FMath::FRandRange(MinY, MaxY);
	RandomLocation.Z = FMath::FRandRange(MinZ, MaxZ);

	return RandomLocation;

}

void ASpawnBlackHawks::SetNumberOfBHSpawned() {
	CollectedActors.Empty();
	for (AActor* It : CollectedActors) {
		if (It && !It->IsPendingKill()) {
			AActor* CurrentActor = It;
			if (CurrentActor && CurrentActor->ActorHasTag("BlackHawk")) {
				BlackHawks.Add(CurrentActor);
			}
			
		}
	}
}

bool ASpawnBlackHawks::GetbIsOneBH() { 
	return bIsOneBH;
}

void ASpawnBlackHawks::SetbIsOneBH(bool SpawnUpdate) { 
	bIsOneBH = SpawnUpdate;
}


Both classes have blueprints that are placed on the level. And now how to communicate between both objects on the level to trigger spawning actor. The trigger is fired when the player will run onto it. In other words, the player run onto the trigger and the trigger change the bool from false to true in the spawner class to spawn an actor. Whatever I have tried, doesn’t work.

Sounds like your player just isn’t firing the trigger, not detecting phyx overlapping.
Check your collision channels on both player and trigger.

My player is firing the trigger. There’s no problem in it. The problem is as soon as the player runs the trigger, the trigger should change the bool variable in the Spawner class to spawn an actor. How to get to the Spawner class in the Trigger class?

You aren’t holding any reference to the actual instance of a an BlackHawk actor; I see you picking its template class there, but you should actually grab a pointer to the one you have instantiated into the UWorld.

public:

UPROPERTY()
ASpawnBlackHawks* BHInstance;


BHInstance->SetbIsOneBH(true);

If you already have the BH blueprint in the level, you can select it in the Trigger detais panel and assign to the Instance pointer.
If it isn’t present in the level then you have to create one using the SpawnActor function.

Thank you very much! It seems to work. I need to think over that a little. I wrote:

in the Trigger.h


UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnBlackHawk")
ASpawnBlackHawks* BHInstance;

Should I use EditAnywhere?

and in the Trigger.cpp


void ABlackHawkTrigger::TriggerEnter(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) {
	
	APawn* Trig = Cast<APawn>(OtherActor);

	if (Trig && BHInstance) {
		
			BHInstance->SetbIsOneBH(true);
	}
	else {
		UE_LOG(LogTemp, Warning, TEXT("Wrong!"));
	}

	
}

It seems that the Trigger BP has automatically pick the Spawner BP on the level.

Yes, if you make your pointer assigned to a blueprint inside the game world, then your script knows it’s a valid object.
Your class exists outside of the game world too, so that is where may cause some confusion at first, just have to make sure that the blueprint instance is actually spawned in the level.

Unreal stuff is very confusing and you need to spend much time to know what’s happening. Thank you again. I have wasted many hours to solve it.