Agne Skripkaite

on gamedev adventures!

Posted: 16 Mar 2025, UE 5.5.3

C++ tips for blueprints

Introduction


Blueprints are integral to many workflows in Unreal Engine, making interfacing between native and blueprints code a core skill to grow as a developer—especially when working in a team. This page lists tips and tricks I've found useful. I hope they prove handy to you as well!

Metadata specifiers


Metadata specifiers can be applied to classes, structs, enums (and their values), functions, and properties. I will highlight a small handful of these below, though I highly recommend taking a look at the full list in the official documentation on metadata specifiers.

Blueprint protected

Blueprints are full of unpleasant surprises for programmers. One of these surprises is that the protected access specifier is not honored automatically. You can fix it by using the BlueprintProtected metadata specifier for every protected UFUNCTION or UPROPERTY intended to be used in blueprints, like so:

1
2
3
4
5
6
    protected:
        UFUNCTION(BlueprintCallable, meta = (BlueprintProtected))
	    void MyProtectedFunction();

	    UPROPERTY(BlueprintReadWrite, meta = (BlueprintProtected))
	    int32 MyProtectedInt{ INDEX_NONE };    

Frustratingly, if "public" nodes accessing these protected members already exist in blueprints before the metadata specifier is added, those blueprints will continue compiling and running just fine and not log any errors when gleefully executing code they should not have access to.

Determines output type

Creating true wildcard pins in blueprint nodes is, unfortunately, not trivial. However, the DeterminesOutputType metadata specifier covers some of the cases. Here's how to use it:

1
2
    UFUNCTION(BlueprintCallable, meta = (DeterminesOutputType = "ActorClass"))
    static AActor* FindActorByClass(TSubclassOf<class AActor> ActorClass);    

The above code will create the following nodes. Notice how the ActorClass input determines the type of the output:

Display priority

When defining variables to be set in blueprint's class defaults, it can be very handy to be able to control their order. One can do so by using the DisplayPriority specifier. Lower display priority number means higher priority and any variables without a priority specified will appear on the bottom.

1
2
3
4
5
6
7
8
9
10
11
    UPROPERTY(EditAnywhere)
	int32 MyParam;

	UPROPERTY(EditAnywhere, meta = (DisplayPriority = 0))
	int32 MyParam0;

	UPROPERTY(EditAnywhere, meta = (DisplayPriority = 1))
	int32 MyParam1;

	UPROPERTY(EditAnywhere, meta = (DisplayPriority = -1))
	int32 MyParamNeg1;    

World context

The WorldContext specifier allows you to hide a world context input pin from the blueprint node if the calling object itself can be used as a world context object. For an object to be considered a valid substitute for the world context input pin it must override the GetWorld() function.

The principle for using this specifier is similar to other specifiers:

1
2
    UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"))
	static FString GetSomeStringFromWorld(UObject* Context);    

The two screenshots below show what the node for the function above will look like in an Actor blueprint and a blueprint function library blueprint, respectively:

Note that this specifier will make the function inaccessible in subsystem blueprints! Though I wouldn't worry about it too much as blueprint subsystems, while possible, aren't that common.

Getters and setters


Sometimes blueprints try to be too helpful. If you've ever created a protected variable with a public getter and/or setter you may have noticed you end up with 2 getters and/or setters in blueprints. Here's a concrete example:

1
2
3
4
5
6
7
8
9
10
    public:
        UFUNCTION(BlueprintPure)
        int32 GetMyValue() const { return MyValue; }

        UFUNCTION(BlueprintCallable)
        void SetMyValue(int32 InValue) { MyValue = InValue; }

    protected:
        UPROPERTY(blueprintReadWrite, meta = (BlueprintProtected))
        int32 MyValue{ INDEX_NONE };    

Do not despair! One can specify the native getter and/or setter as the only getter and/or setter for blueprints like so:

1
2
3
4
5
6
7
8
9
10
    public:
        UFUNCTION(BlueprintPure, BlueprintGetter)
        int32 GetMyValue() const { return MyValue; }

        UFUNCTION(BlueprintCallable, BlueprintSetter)
        void SetMyValue(int32 InValue) { MyValue = InValue; }

    protected:
        UPROPERTY(blueprintReadWrite, BlueprintGetter = GetMyValue, BlueprintSetter = SetMyValue, meta = (BlueprintProtected))
        int32 MyValue{ INDEX_NONE };    

Passing by reference from blueprints to native


If you can pass by reference, you should do so in most cases. Unfortunately, any reference parameters are assumed to be outputs in blueprint nodes:

1
2
    UFUNCTION(BlueprintCallable)
	static void PopulateGivenString(FString& OutString);    

This limitation can be circumvented by marking the parameter as a UPARAM(ref):

1
2
    UFUNCTION(BlueprintCallable)
	static void PopulateGivenString(UPARAM(ref) FString& OutString);