Referencing Blueprint Interfaces as UPROPERTY

I have created a Blueprint interface in C++, like this:


/// Implemented by dialogs and menus that can be controlled with keyboard and gamepad
class NUCLEX_API IButtonControllableDialog {

  GENERATED_BODY()

  /// Accepts the currently selected dialog option
  public: UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category="UI")
  void Accept();

  /// Cancels the currently selected dialog
  public: UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category="UI")
  void Cancel();

  // ...

};

And inherited said interface in a pure Blueprint class:

inheritinblueprint.png

Now I thought my C++ player controller could be given a property like this:


/// Dialog to which input is currently being sent (can be null if no dialog is active)
public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category="UI")
TScriptInterface<IButtonControllableDialog> ActiveDialog;

But as I found in the Interfaces in C++ wiki article, that does not work for Blueprint interfaces implemented in Blueprint. Instead, I have to check:


this->ActiveDialog->GetClass()->ImplementsInterface(UButtonControllableDialog::StaticClass())

Fine.

But if I assign the [FONT=courier new]ActiveDialog property in Blueprint, even though it compiles fine, it instead assigns [FONT=courier new]NULL.

It works if I change the property type to [FONT=courier new]UObject *, but that would totally remove any type information. Blueprinters could assign literally anything :-/

Funnily, if I create a class with an [FONT=courier new]IButtonControllableDialog property in Blueprint and nativize it, the same [FONT=courier new]TScriptInterface<IButtonControllableDialog> property is generated.

How can I create a property of type [FONT=courier new]IButtonControllableDialog that works for both C++ and Blueprint classes?

So this is really the state of things?

Either expose properties as (UObject *) and lose any indication as to which interface type is supposed to be assigned to them,

or expose properties as (UMyBlueprintInterface *) and lose the ability to ever implement the interface using Blueprints?

:frowning:

Interface implemented to Blueprints using the Editor is an UInterface sub object attached to the Blueprint.
Casting will never work like that, this is why you get null.

Also there’s no reason to cast anything to call interface methods from Blueprint and Interfaces cannot have properties so I have no idea why you even want to cast them.

Please read my original post.

A) I have a C++ class (let’s say, a custom PlayerController) that has a UPROPERTY:

I am not trying to add a UPROPERTY to a blueprint interface.

B) I do not want to cast anything and know there is no need to. All blueprint interface calls are by name (and I can see exactly how someone at Epic attempted to design a system for actual method calls in UE C++, failed and clobbered stuff together to arrive at the current state of things)

I want some semblance of type safety. I’m given two choices in my [FONT=courier new]PlayerController:

Choice 1:


/// Dialog to which input is currently being sent (can be null if no dialog is active)
public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category="UI")
TScriptInterface<IButtonControllableDialog> ActiveDialog;

The Kismet editor shows the property as being of type [FONT=courier new]IButtonControllableDialog. Nice, the blueprint monkey knows which type to assign to it. Except when he creates a blueprint class and assigns it, it silently fails. Yes. it silently fails and assigns null.

Choice 2:


/// Dialog to which input is currently being sent (can be null if no dialog is active)
public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category="UI")
UObject *ActiveDialog;

The Kismet editor shows an [FONT=courier new]Object pin. The blueprint monkey could assign anything to it and my player controller would eat it. Only when I call [FONT=courier new]IButtonControllableDialog::SomeMethod_Execute() things would start going wrong.

UPROPERTY ( EditAnywhere, meta = (AllowedClasses=“ButtonControllableDialog”) )
UObject *ActiveDialog;

Thank you very much!

That totally fixes the pain on the Blueprint side for me

Erm, correction, it seems to do nothing.



/// Dialog to which input is currently being sent (can be null if no dialog is active)
public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category="UI")
TScriptInterface<IButtonControllableDialog> ActiveDialog;

/// Dialog to which input is currently being sent (can be null if no dialog is active)
public: UPROPERTY(
  EditAnywhere, BlueprintReadWrite, Transient, Category="UI",
  meta = (AllowedClasses = "ButtonControllableDialog")
)
UObject *SameWithBCD;

/// Dialog to which input is currently being sent (can be null if no dialog is active)
public: UPROPERTY(
  EditAnywhere, BlueprintReadWrite, Transient, Category="UI",
  meta = (AllowedClasses = "Gooblefoob")
)
UObject *SameWithGooblefoob;

/// Dialog to which input is currently being sent (can be null if no dialog is active)
public: UPROPERTY(
  EditAnywhere, BlueprintReadWrite, Transient, Category="UI",
  meta = (AllowedClasses = "IButtonControllableDialog")
)
UObject *SameWithIBCD;

/// Dialog to which input is currently being sent (can be null if no dialog is active)
public: UPROPERTY(
  EditAnywhere, BlueprintReadWrite, Transient, Category="UI",
  meta = (AllowedClasses = "UButtonControllableDialog")
)
UObject *SameWithUBCD;


According to the docs it should at least affect the asset picker, but that doesn’t seem to happen either.

So unless I’m missing something, I’m still at square one.

Force users to set variable from Details Panel.
Do not allow BlueprintReadWrite, if it must be set in graph then only a custom Kismet K2Node can do that filtering.

I am facing the exactly same problem right now.

The interface property related code should be given more love🥺.
The latest commit of most of the code is from Tim back in 2014🥺.

I seem found the reason in UObjectBaseUtility::GetInterfaceAddress : “Native interface should not be implemented by blueprint or you will get nullptr because there is no information about the interface address offset in the blueprint class(always 0 currently)”.

And I am curious about

UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category="UI") TScriptInterface<IButtonControllableDialog> ActiveDialog;

Could ActiveDialog be awared by garbage collection system ? Since the FScriptInterface::ObjectPointer is naked.

1 Like