Defining a C++ Struct¶
Similar to classes, Unreal Engine allows you to register a struct
with the reflection system via USTRUCT
. This would allow for runtime interaction and Blueprints integration. The engine frequently uses struct
s to define data containers, property bags, etc.
USTRUCT
s are commonly defined in the header file of a related class, but they could also have their own dedicated header file. You'll have to manually create the header file, however, as there is no option in the Unreal Editor to create a struct
.
Note
USTRUCT
s cannot be defined inside the body of a class. The USTRUCT
must be defined at the global scope.
Example¶
The following is a simple example of a struct
that defines a geographical coordinate.
#pragma once
#include "GeoPoint.generated.h"/* (1)! */
USTRUCT(BlueprintType)
struct MODULENAME_API/* (2)! */ FGeoPoint /* (3)! */ {
GENERATED_BODY()/* (4)! */
UPROPERTY(EditAnywhere)/* (5)! */
float Latitude;
UPROPERTY(EditAnywhere)/* (6)! */
float Longitude;
};
- Contains all the boilerplate code generated by UHT. This file must be included as the last
#include
in the header file. - Akin to the standard
extern
keyword in C++. See External Linkage in classes for more info. - Per recommended conventions, the
F
prefix is used to denote astruct
. - Used to inject some boilerplate code generated by UHT into the class body.
- The
UPROPERTY
macro in astruct
supports the same set of specifiers as in a class. SeeUPROPERTY
for the complete list. - The
UPROPERTY
macro in astruct
supports the same set of specifiers as in a class. SeeUPROPERTY
for the complete list.
USTURCT
s cannot contain UFUNCTION
s. You may want to look into UBlueprintFunctionLibrary
if you need to define companion functions for your struct
.
USTRUCT
¶
USTRUCT
is pretty limited compared to UCLASS
. There are only a few specifiers and meta specifiers that USTRUCT
can accept.
Specifiers¶
USTRUCT(BlueprintType)
Exposes this struct as a type that can be used for variables in Blueprints.
The inner properties of the struct
will show up as nested properties with the right specifiers.
You may want to use UPROPERTY(meta=(ShowOnlyInnerProperties))
to avoid nesting.
Meta Specifiers¶
USTRUCT(meta=(HasNativeBreak="FunctionName"))
Specifies a custom break function. A break function takes an instance of the struct
and breaks it down into its individual properties. USTRUCT
s have an automatic break function that exposes every BlueprintReadOnly
or BlueprintReadWrite
property. You can define a custom break function to expose only a subset of the properties, or extend them with computed ones.
The following example:
USTRUCT(BlueprintType, meta=(HasNativeBreak="ModuleName.GeoPointHelperLibrary.BreakGeoPoint"/* (1)! */))
struct FGeoPoint { /* Same body as in the example above. */ };
UCLASS()
class MODULENAME_API UGeoPointHelperLibrary : public UBlueprintFunctionLibrary {
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure)
static void BreakGeoPoint(const FGeoPoint& GeoPoint, float& Latitude, float& Longitude, FString& AsString) {
Latitude = GeoPoint.Latitude;
Longitude = GeoPoint.Longitude;
AsString = FString::Printf(TEXT("(%f:%f)"), Latitude, Longitude);
}
};
- An absolute path to a
UFUNCTION
is formed byModuleName.ClassName.FunctionName
.
results in the following Blueprint break:
USTRUCT(meta=(HasNativeMake="FunctionName"))
The make function is the antithesis to the break function. It takes the necessary properties and constructs an instance of the struct
. The default make function takes in all BlueprintReadWrite
properties to construct a new instance.
You can define a custom make function to construct an instance from a subset of properties, or something entirely different. For example:
USTRUCT(BlueprintType, meta=(HasNativeMake="ModuleName.GeoPointHelperLibrary.MakeGeoPoint"/* (1)! */))
struct FGeoPoint { /* Same body as in the example above. */ };
UCLASS()
class MODULENAME_API UGeoPointHelperLibrary : public UBlueprintFunctionLibrary {
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure)
static FGeoPoint MakeGeoPoint(int32 LongDegrees, int32 LongMinutes, int32 LongSeconds, int32 LatDegrees, int32 LatMinutes, int32 LatSeconds) {
FGeoPoint GeoPoint;
GeoPoint.Longitude = LongDegrees + LongMinutes / 60.0 + LongSeconds / 3600.0;
GeoPoint.Latitude = LatDegrees + LatMinutes / 60.0 + LatSeconds / 3600.0;
return GeoPoint;
}
};
- An absolute path to a
UFUNCTION
is formed byModuleName.ClassName.FunctionName
.
If you then try to make an FGeoPoint
in Blueprints, you'll see the following:
Constructor¶
Just like UCLASS
es, USTRUCT
s are required to have a default constructor. An obvious choice for the example struct
we have here is:
FGeoPoint(float Latitude = 0, float Longitude = 0)
: Latitude(Latitude), Longitude(Longitude) { }
Instantiation¶
USTRUCT
instances live on the stack. You can instantiate them like any other C++ struct
, and expect them to be destroyed when they go out of scope.
FGeoPoint GeoPoint(43.651070, -79.347015);
Serialization¶
USTRUCTS
also support automatic serialization and deserialization. They also support custom serialization logic through Serialize
and NetSerialize
.
USTRUCT(BlueprintType)
struct FGeoPoint {
GENERATED_BODY()
FGeoPoint();
/* Properties */
void Serialize(FArchive& Ar) {
Ar << Latitude << Longitude;
}
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) {
/* We could perhaps convert the floats to half-precision floats here */
/* This might not seem a lot, but over time, it could save a lot of bandwidth */
Ar << Latitude << Longitude;
return true;
}
};
Simply declaring these functions is not enough. You need to subclass a template type trait struct named TStructOpsTypeTraitsBase2<T>
to explicitly inform the engine about your functions.
template<>
struct TStructOpsTypeTraits<FGeoPoint> :
TStructOpsTypeTraitsBase2<FGeoPoint>
{
enum
{
WithSerializer = true,
WithNetSerializer = true
};
};