I am working on a UI design and am storing within a custom button some information about corresponding UI elements (e.g. each button corresponds to a “buy item” button, with the button carrying the icon and class of the item).
My button class is
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FRTSButtonClickSignature, AMyRTSPlayerController*, UIOwner, TSubclassOf< UObject>, LinkedObject);
/**
*
*/
UCLASS()
class URTSButton : public UButton
{
GENERATED_BODY()
public:
URTSButton();TSubclassOf LinkedClass;
AMyRTSPlayerController* LinkedController;UFUNCTION()
void MyOnButtonClick();FRTSButtonClickSignature OnRTSButtonClicked;
};
In the uniform grid, which represents my UI, I have a for loop which iterates over an Array of TSubClasses of UObjects (because it is supposed to be a more generic UI):
void URTSUniformGridPanel::Populate(TArray<TSubclassOf> Array, UActorComponent* LinkedComponent)
{
Clear();
for(auto Class: Array)
{
URTSButton* Button = NewObject();
Button->LinkedClass = Class;
// Button->LinkedClass->AddToRoot(); // Prevents Garbage Collection
Button->LinkedController = this->UIOwner;
if(LinkedComponent && LinkedComponent->GetClass()->ImplementsInterface(UButtonClickAction::StaticClass()))
{
IButtonClickAction* ClickAction = Cast(LinkedComponent);
if (ClickAction)
{
Button->OnRTSButtonClicked.AddDynamic(ClickAction, &IButtonClickAction::ReactToButtonClick);
}
}
if(Class->ImplementsInterface(UCommandGridInterface::StaticClass()))
{
ICommandGridInterface* Interface = Cast(Class->GetDefaultObject());
TObjectPtr Icon = Interface->GetIcon();
FButtonStyle Style;
Style.Hovered.SetResourceObject(Icon);
Style.Normal.SetResourceObject(Icon);
Style.Pressed.SetResourceObject(Icon);Button->SetStyle(Style); } AddElement(Button);
}
The binding is successful, I confirmed this using a debugger:
In my RTSButton.cpp class I proceed as follows
URTSButton::URTSButton()
{
OnClicked.AddDynamic(this, &ThisClass::MyOnButtonClick);
}void URTSButton::MyOnButtonClick(void)
{
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, TEXT(“Some debug message!”));
if(OnRTSButtonClicked.IsBound())
{
OnRTSButtonClicked.Broadcast(this->LinkedController, this->LinkedClass);
}}
Again, using a debugger, I confirmed, that at this very instance, this->LinkedClass is valid and correctly set. However, immediately afterwards, the receiving InventoryComponent receives a nullptr as Object. However, the controller is passed correctly:
void URTSShopComponent::ReactToButtonClick(AMyRTSPlayerController* Controller, TSubclassOf< UObject> Object)
{
// auto Item = Object->GetClass();
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, TEXT(“ROFL Some debug message!”));if(Controller)
{
auto Builder = Controller->GetBuilder();
auto Inventory = Builder->FindComponentByClass< URTSInventoryComponent>();
if(Inventory)
{
Inventory->AddItem(Object->GetClass());
}}
}
Controller is valid and set correctly. Object is a nullptr out of nowhere. Does anyone know, why this occurs and how to fix it? I fear, that “Class” in the initial for-loop gets out of scope, is destroyed, and Unreal’s garbace collector sets the “Object” variable to nullptr. However, as mentioned, “LinkedClass” is still a valid pointer right before broadcasting.