I have a problem where I bind a lambda to a subobject’s delegate within a component and when the subobjects executes the delegate, it is called in a different component.
test_ownership_actor.h:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/GameMode.h"
#include "utils/log.h"
#include "test_ownership_actor.generated.h"
DECLARE_DELEGATE(FTestGenericCallback);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FTestGenericMultiCallback);
UCLASS()
class MEMER2_API UTestOwnershipComponentSub : public UObject {
GENERATED_BODY()
public:
FTestGenericCallback callback;
void do_something() {
callback.ExecuteIfBound();
}
};
UCLASS()
class MEMER2_API UTestOwnershipComponent : public UActorComponent {
GENERATED_BODY()
public:
FTestGenericCallback callback;
UTestOwnershipComponent() {
UE_LOG(LogTemp, Warning, TEXT("Created component address: %p"), this);
sub = CreateDefaultSubobject<UTestOwnershipComponentSub>(TEXT("sub"));
}
virtual void BeginPlay() override {
sub->callback.BindLambda([this] {
UE_LOG(LogTemp, Warning, TEXT("[AT CALLBACK] Component address: %p | Subobject: %p"), this, sub);
callback.ExecuteIfBound();
});
}
void do_something() {
UE_LOG(LogTemp, Warning, TEXT("[AT START] Component address: %p | Subobject: %p"), this, sub);
sub->do_something();
}
protected:
UPROPERTY()
UTestOwnershipComponentSub* sub;
};
UCLASS()
class MEMER2_API ATestOwnershipActor : public AActor {
GENERATED_BODY()
public:
UPROPERTY(BlueprintAssignable)
FTestGenericMultiCallback callback;
ATestOwnershipActor() {
UE_LOG(LogTemp, Warning, TEXT("Created actor address: %p"), this);
comp = CreateDefaultSubobject<UTestOwnershipComponent>(TEXT("comp"));
comp->callback.BindLambda([this] {
log_to_screen("Callback is called succesfully.");
UE_LOG(LogTemp, Warning, TEXT("[AT CALLBACK] Owner address: %p | Component: %p"), this, comp);
callback.Broadcast();
});
}
UFUNCTION()
void on_f_pressed() {
log_to_screen("Pressing key.");
UE_LOG(LogTemp, Warning, TEXT("[AT START] Owner address: %p | Component: %p"), this, comp);
comp->do_something();
}
protected:
UPROPERTY()
UTestOwnershipComponent* comp;
};
UCLASS()
class ATestOwnershipPc : public APlayerController {
GENERATED_BODY()
public:
virtual void BeginPlay() override {
Super::BeginPlay();
on_set_actor();
}
virtual void SetupInputComponent() override {
Super::SetupInputComponent();
if (InputComponent) {
InputComponent->BindKey(EKeys::F, IE_Pressed, this, &ATestOwnershipPc::on_f_pressed);
}
}
UFUNCTION(BlueprintNativeEvent)
void on_set_actor();
virtual void on_set_actor_Implementation() {
actor = GetWorld()->SpawnActor<ATestOwnershipActor>(ATestOwnershipActor::StaticClass(), FTransform());
}
UFUNCTION()
void on_f_pressed() {
actor->on_f_pressed();
}
UPROPERTY(BlueprintReadWrite)
ATestOwnershipActor* actor;
};
UCLASS()
class MEMER2_API ATestOwnershipGm : public AGameMode {
GENERATED_BODY()
public:
ATestOwnershipGm() {
PlayerControllerClass = ATestOwnershipPc::StaticClass();
}
};
I have a bp_test_ownership_actor which just derives the C++ actor (no other change).
And a BP GM which just uses teh BP PC as PC:
bp_test_ownership_pc:
The Blueprint derivation is required, the C++ actor on its own doesn’t cause the issue.
I am running 2 players 1 as listen server.
Related log (after pressing F once):
LogTemp: Warning: Created actor address: 000005E3DB274100
LogTemp: Warning: Created component address: 000005E3C6BEA600
LogBlueprintUserMessages: [bp_test_ownership_pc_C_0] Server: BP bind happened
LogTemp: Warning: Created actor address: 000005E37606AF00
LogTemp: Warning: Created component address: 000005E3D5EA0500
LogBlueprintUserMessages: [bp_test_ownership_pc_C_1] Server: BP bind happened
LogTemp: Warning: Created actor address: 000005E376065500
LogTemp: Warning: Created component address: 000005E3A6FC1700
LogBlueprintUserMessages: [bp_test_ownership_pc_C_0] Client 0: BP bind happened
LogTemp: Warning: [AT START] Owner address: 000005E3DB274100 | Component: 000005E3C6BEA600
LogTemp: Warning: [AT START] Component address: 000005E3C6BEA600 | Subobject: 000005E3C6B2BE50
LogTemp: Warning: [AT CALLBACK] Component address: 000005E3A6FC1700 | Subobject: 000005E3C6B2BE50
LogTemp: Warning: [AT CALLBACK] Owner address: 000005E376065500 | Component: 000005E3A6FC1700
LogBlueprintUserMessages: [bp_test_ownership_pc_C_0] Client 0: BP callback called
As you can see:
LogTemp: Warning: [AT START] Component address: 000005E3C6BEA600 | Subobject: 000005E3C6B2BE50
LogTemp: Warning: [AT CALLBACK] Component address: 000005E3A6FC1700 | Subobject: 000005E3C6B2BE50
These dont match and this causes issues in other gameplay. How is this possible?