In Unreal Engine (UE) development, C++ handles performance-critical processing and complex game logic cores, while Blueprint enables rapid prototyping and intuitive logic construction by designers—truly two wheels of the same vehicle.
However, many beginners and intermediate developers face a challenge: "How can I easily and safely call convenient functions and variables written in C++ from Blueprint?" Even if you write efficient C++ code, you can't fully benefit from it if it can't be used from Blueprint.
This article thoroughly explains the core mechanisms for smooth C++ and Blueprint integration, focusing particularly on UFUNCTION and UPROPERTY macros, with concrete code examples and best practices. After reading this, your UE development capabilities will expand dramatically.
Overview of UCLASS, UFUNCTION, and UPROPERTY
To expose C++ code to the Blueprint world, you need to tell Unreal Engine's reflection system about its existence. This role is fulfilled by special macro groups like UCLASS, UFUNCTION, and UPROPERTY.
UCLASS: Registering Classes
First, classes you want to access from Blueprint must have the UCLASS() macro. This indicates that the class should be tracked and managed by UE's reflection system.
// MyActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
UCLASS() // This macro is required
class MYPROJECT_API AMyActor : public AActor
{
GENERATED_BODY()
public:
AMyActor();
};
UPROPERTY: Exposing Variables
To make C++-defined variables referenceable or editable from Blueprint, add the UPROPERTY() macro. The arguments (specifiers) in this macro provide fine control over Blueprint behavior.
| Specifier | Meaning | Blueprint Behavior |
|---|---|---|
EditAnywhere | Editable from anywhere | Editable in Details panel (instance, defaults) |
BlueprintReadOnly | Read-only from Blueprint | Get value node generated in Blueprint graph |
BlueprintReadWrite | Read/write from Blueprint | Get/Set value nodes generated in Blueprint graph |
VisibleAnywhere | Viewable from anywhere | Value reference only in Details panel |
Category = "My Category" | Display category in Details panel | Organized by category for easier management |
Code Example (UPROPERTY):
// MyActor.h
// ...
UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// Variable readable/writable from Blueprint and editable in Details panel
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
float Health = 100.0f;
// Read-only variable from Blueprint
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Stats")
int32 MaxAmmo = 30;
};
UFUNCTION: Exposing Functions
To call C++-implemented logic from Blueprint, add the UFUNCTION() macro to the function.
| Specifier | Meaning | Blueprint Behavior |
|---|---|---|
BlueprintCallable | Callable from Blueprint | Available as standard function node |
BlueprintPure | Callable as Pure Function | Available as node with no execution pins, only return value (recommended for functions that don't change state) |
BlueprintImplementableEvent | Event declared in C++, implemented in Blueprint | Can be overridden as event node in Blueprint |
BlueprintNativeEvent | Has default C++ implementation, can be overridden in Blueprint | Can have implementation in both C++ and Blueprint |
Code Example (UFUNCTION):
// MyActor.h
// ...
UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// 1. Function callable from Blueprint
UFUNCTION(BlueprintCallable, Category = "Combat")
void ApplyDamageToHealth(float DamageAmount);
// 2. Pure function callable from Blueprint
UFUNCTION(BlueprintPure, Category = "Stats")
float GetHealthPercentage() const;
// 3. Event implemented in Blueprint
UFUNCTION(BlueprintImplementableEvent, Category = "Events")
void OnHealthDepleted();
};
Code Example (UFUNCTION Implementation):
// MyActor.cpp
// ...
void AMyActor::ApplyDamageToHealth(float DamageAmount)
{
Health = FMath::Max(0.0f, Health - DamageAmount);
if (Health <= 0.0f)
{
// Call Blueprint-implemented event
OnHealthDepleted();
}
}
float AMyActor::GetHealthPercentage() const
{
// Dividing by 100.0f here since MaxHealth isn't defined as UPROPERTY
return Health / 100.0f;
}
Using from Blueprint
After compiling the above C++ code and returning to the editor, create a Blueprint class inheriting from AMyActor, and the C++-exposed features automatically become available.
Using Variables (UPROPERTY)
- Details Panel: Variables with
EditAnywhereorVisibleAnywhereappear in the "Stats" category of the Blueprint instance's Details panel, where values can be edited or referenced. - Graph: Variables with
BlueprintReadWriteorBlueprintReadOnlycan be searched and used as "Get Health" or "Set Health" nodes from the right-click menu in the Blueprint graph.
Using Functions (UFUNCTION)
- Calling: The
ApplyDamageToHealthfunction can be searched as "Apply Damage To Health" node from the right-click menu in the Blueprint graph, connected via execution pins and called.
Note on Function Name Conflicts
When defining functions in C++, names like
TakeDamagealready exist in theAActorclass, potentially causing conflicts or unintended overrides. We recommend giving your own features more specific, unique names likeApplyDamageToHealth.
- Pure Functions: The
GetHealthPercentagefunction can be used without execution pins, connecting the return value pin directly to other nodes. - Events: The
OnHealthDepletedevent can be searched as "Event On Health Depleted" node from the right-click menu in the Blueprint graph, implementing processing when the event occurs.
Best Practices and Common Mistakes
Follow these points to facilitate smooth C++ and Blueprint integration.
Best Practices
- Use BlueprintPure: Always specify
BlueprintPurefor functions that don't change state and simply calculate and return values. This prevents Blueprint graphs from becoming cluttered with execution pins and makes data flow visually clear. - Specify Category: Always specify
Category = "..."forUPROPERTYandUFUNCTION. This improves searchability in Blueprint right-click menus and Details panels, keeping things organized. - Separate Logic: Thoroughly implement performance-critical processing and complex algorithms in C++, and leave the timing of calling them and control of parameters designers adjust to Blueprint.
Common Mistakes
- Forgetting Macros: Forgetting
UCLASS,UFUNCTION, orUPROPERTYmacros means they won't be registered with the reflection system and will be completely inaccessible from Blueprint. Especially ensure macros are written in header files and the.generated.hfile is properly included. - Argument Types: Using C++ types not supported by Blueprint (e.g., standard library containers) as function arguments or return values will prevent Blueprint nodes from being generated. Use UE-specific types (
FString,TArray,TMap, etc.). - Using const: When specifying
BlueprintPurefor functions that don't change state, it's common practice in C++ to also add theconstqualifier. This guarantees the function doesn't change object state, a C++ best practice.
Key Points for C++ and Blueprint Integration
C++ and Blueprint integration in Unreal Engine is achieved through UFUNCTION and UPROPERTY macros.
| Element | Macro | Main Role | Blueprint Usage |
|---|---|---|---|
| Variables | UPROPERTY | Register variable with reflection system, set Blueprint access permissions. | Edit in Details panel, use Get/Set nodes. |
| Functions | UFUNCTION | Register function with reflection system, set how Blueprint calls it. | Use as executable node or pure function node. |
Mastering this integration maximizes both C++ performance and Blueprint flexibility, enabling efficient, maintainable game development. Start by actively exposing simple C++ features to Blueprint.