I have two classes, ATank and UBuff. ATank holds a TArray of UBuff to keep track of every active reference of UBuff that is owned by ATank and UBuff holds a reference to its owner ATank so it can invoke methods or access properties. This causes circular dependency if I include each other’s header in their header, but how else can I structure this so that these two classes know what each other are?
I have temporarily solved this by only forward declaring UBuff in ATank, but what if at some point ATank needs to invoke a method on UBuff. For example a cleanse effect that would call the Destroy method on UBuff. Holding refs to each other in blueprint seems possible while invoking methods and etc, but I’m doing this in code because I want more flexible use out of FTimerHandle which is what I’m using for buff timers.
///* Project/Public/Tank.h */
#pragma once
//#include "Buffs/Buff.h"
#include "GameFramework/Pawn.h"
#include "Tank.generated.h"
UCLASS()
class PASTRYPANZERPANIC_API ATank : public APawn
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, Category="Tank")
TArray<class UBuff*> activeBuffs;
public:
// Sets default values for this pawn's properties
ATank();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
};
///* Project/Public/Buffs/Buff.h */
#pragma once
#include "../Tank.h"
#include "UObject/NoExportTypes.h"
#include "Buff.generated.h"
UCLASS()
class PASTRYPANZERPANIC_API UBuff : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Buff", Meta=(ExposeOnSpawn=true))
float lifeSpan;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Buff", Meta=(ExposeOnSpawn=true))
float timeCreated;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Buff", Meta=(ExposeOnSpawn=true))
float tickRate;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Buff", Meta=(ExposeOnSpawn=true))
float timeActive;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Buff", Meta=(ExposeOnSpawn=true))
bool bCanExpire;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Buff", Meta=(ExposeOnSpawn=true))
class ATank *owningTank;
public:
UBuff();
UFUNCTION(BlueprintCallable, Category="Buff")
virtual void OnCreation();
UFUNCTION(BlueprintCallable, Category="Buff")
virtual void OnTick();
UFUNCTION(BlueprintCallable, Category="Buff")
virtual void OnDestroy();
};
///* Project/Public/Buffs/Buff.cpp
#include "PastryPanzerPanic.h"
#include "Buff.h"
UBuff::UBuff(){
}
void UBuff::OnCreation(){
// FTimerHandle buffTimer;
//
// if(tickRate > 0.f){
// GetWorldTimerManager().SetTimer(buffTimer, this, &UBuff::OnTick, tickRate, true);
// }
//
// owningTank->activeBuffs.Add(this);
//
// GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor:Blue, "Buff Created.");
}
void UBuff::OnTick(){
// GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor:Yellow, "Buff Ticking.");
// timeActive += tickRate;
// if(timeActive > lifeSpan){
// this->OnDestroy();
// }
}
void UBuff::OnDestroy(){
// GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor:Red, "Destroy Called.");
// GetWorldTimerManager().ClearTimer(buffTimer);
// owningTank->activeBuffs.Remove(this);
// Destroy();
}