Coding conventions for Unreal Engine 4 for C++, Blueprint and Python
Actor.h
for AActor)ActorTests.cpp
.spec.cpp
, e.g. Actor.spec.cpp
// U prefix = UObject subclass (see below)
// + OUU prefix = Scope: Open Unreal Utilities
// + CoreLibrary = Name of the class within the scope
class UOUUCoreLibrary : public UBlueprintFunctionLibrary {}
Unreal Header Tool enforces the following prefixes for reflected types:
Prefix | Type |
---|---|
T | Template classes |
U | UObject child classes |
A | AActor child classes |
S | SWidget child classes |
E | Enums (only typenames, enum items have no prefix!) |
I | UInterface classes |
F | Structs and all classes not covered by the rules above |
In addition to this the following prefix convention can be found in the engine:
Prefix | Type |
---|---|
C | Concepts for TModels trait |
UMyPluginLibrary
, URegexLibrary
, etc.Starting with UE5, Epic has renamed a lot of internal namespaces. We encourage following this pattern that is based on those new namespaces:
// General pattern
namespace GameOrPlugin::Module::Private::Foo::Bar {}
The elements have the following meanings:
GameOrPlugin
is a project identifier. We reccommend using the game project or plugin name. Unreal Engine code uses UE
as namespace root.Module
should be your code module. This may be omitted for games that have a single game module.Private
is optional. It should be added for namespaces that contain implementation details which are not interesting for public API.
Often times this will contain nested template definitions that are not meant to be used directly.Foo::Bar
are optional nested namespaces. Use any names that make sense to you. We reccommend only going two levels deep from this point.// UE tuple implementation details. In UE4 this was called UE4Tuple_Private
namespace UE::Core::Private::Tuple {}
// Online plugins/modules from UE engine code. This contains types like FOnlineServicesRegistry.
namespace UE::Online {}
// Implementation details of UE::Online namespace members
namespace UE::Online::Private {}
// Array utility functions from Runtime module of 'Open Unreal Utilities' plugin
namespace OUU::Runtime::ArrayUtils {}
// Game project namespace for 'PuzzlePlatformer' game
namespace PuzzlePlatformer{}
Type
. Unless you have a single unambiguous type, in which case you can use the single letter T
.
// good - the type names state clearly what purpose they serve
template<typename ElementType, typename AllocatorType>
class TArray {};
// good - it's unambiguous that T is the target type after the cast
template<typename T>
T* Cast(UObject* Object);
// bad - what are T and U?
template<typename T, typename U>
class TArray {};
// bad - at the class header it's clear that Element is a type template parameter.
// This information is lost in the template body, so suffixing with 'Type' is required.
template<typename Element, typename Allocator>
class TArray {};
TInlineAllocator::NumInlineElements
template<typename ElementType, template<typename> typename TContainerType>
class TMyContainerWrapper {};
Type aliases defined via typedefs or using declarations follow the regular type naming conventions with some exceptions / addendums
// good
using FOptionTextFilter = TTextFilter< TSharedPtr<FAvailableStringTable> >;
using FAudioSamplePool = TMediaObjectPool<FAudioSample>;
// bad
using TOptionTextFilter = TTextFilter< TSharedPtr<FAvailableStringTable> >;
using AudioSamplePool = TMediaObjectPool<FAudioSample>;
// good
template <typename AttributeType>
using TVertexAttributeIndicesArray = TAttributeIndicesArray<AttributeType, FVertexID>;
// bad
template <typename AttributeType>
using FVertexAttributeIndicesArray = TAttributeIndicesArray<AttributeType, FVertexID>;
template <typename AttributeType>
using VertexAttributeIndicesArray = TAttributeIndicesArray<AttributeType, FVertexID>;
template<typename ElementType, int32 NumElements>
{
// good
using IteratorType = TNetworkSimBufferIterator<TNetworkSimContiguousBuffer<ElementType, NumElements>, ElementType>
// bad
using IteratorType = TNetworkSimBufferIterator<TNetworkSimContiguousBuffer<ElementType, NumElements>, ElementType>
}
// good
using FDataPtr = TSharedPtr<TArray<uint8>>;
using IAssetTreeItemPtr = TSharedPtr<IAssetTreeItem>;
// bad
using TDataPtr = TSharedPtr<TArray<uint8>>;
using AssetTreeItemPtr = TSharedPtr<IAssetTreeItem>;
class Foo : public Bar, public IFooBarInterface
{
// good
using Super = Bar;
// bad - only use super for "primary" super classes
using Super = UObject;
// bad - not even for implemented interfaces
using Super = IFooBarInterface;
}
// good
template <> struct TUnsignedIntType<4> { using Type = uint32; };
struct FHairStrandsMeshTrianglePositionFormat { using Type = FVector4; }
// bad - Type alias outside of a type trait context
using Type = TSharedPtr<TArray<uint8>>;
// good
bool bIsPendingDestruction;
FSkin Skin;
// bad
bool IsOutThere; // missing prefix
bool bPendingDestruction; // missing verb
Shadowed fields/variables are not allowed. MSVC allows variables to be shadowed from an outer scope, but GCC will not allow this, with good reason: It makes references ambiguous to a reader and hides mistakes.
For example, there are four variables with the name Count
in this member function:
int32 Count = 42;
class FSomeClass
{
public:
void Func(const int32 Count)
{
for (int32 Count = 0; Count != 10; ++Count)
{
// Use Count
}
}
private:
int32 Count = 1000;
};
Blueprint
K2_
as a prefix. This is highly discouraged, because the meaning of this prefix is very opaque (it’s derived from Kismet 2, the old internal name of Blueprint visual scripting back from UDK days).Native
UAnimInstance::BlueprintUpdateAnimation
and UAnimInstance::NativeUpdateAnimation
for comparisonOnRep_VariableName
, e.g. OnRep_HealthPointsHandle
prefix (see Events and Delegates)Server_
, Client_
or Multicast_
Parameters that are passed in or in and out should be prefixed with In or InOut respectively.
bool Trace(FHitResult& OutHitResult);
When calling those functions, you should use the OUT
macro to mark parameters that are passed by reference and initialized by the function:
FHitResult HitResult;
Foo->Trace(OUT HitResult);
The following rules apply to all types of delegates in Unreal C++ (single cast delegate, multicast delegates, dynamic multicast delegates and events).
FComponentOverlapEvent OnComponentOverlapped;
// general rule: F + Name + Event/Delegate, e.g.
FComponentOverlapEvent
FComponentOverlapDelegate
FOnComponentOverlapped OnComponentOverlapped;
FLoadDelegate; // used for all kinds of loading events
FGuiRequestDelegate; // used for various GUI request events
// etc
When in doubt, you should still create individual delegate types, esp. because it makes changing signatures easier.
// good - it's clear when the individual delegates are called
// this would still be true if only a single one of these delegates would exist
FLoadDelegate OnLoadRequestSubmitted;
FLoadDelegate OnLoadStarted;
FLoadDelegate OnLoadProgress;
FLoadDelegate OnLoadCompleted;
// bad - it's hard to tell when exactly this is called
FLoadDelegate OnLoad;
void HandleExplosion(); // good: clear
void OnExplosion(); // bad: OnExplosion is the name of the event itself, but should not be used for functions bound to it
void DealExplosionDamage(); // bad: This may be called in HandleOnExplosion, but it's usually cleaner to have a separate function for delegate binding
Delegates with parameters must document their parameters. If the usage of a parameter changes, a new delegate type should be created.
Documentation of parameters should happen at delegate type level:
// good - add inline comment
DECLARE_DELEGATE_OneParam(FOnFileLoaded, FString /*FilePath*/);
// good - dynamic delegates force you to add parameter names anyways
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFileLoaded, FString, FilePath);
// better - adding function style comment
/**
* Delegate that will be called when a file was loaded
* @param FString: System file path of the newly loaded file
*/
DECLARE_DELEGATE_OneParam(FOnFileLoaded, FString);
Putting this info at instance level is good too, but should exactly match the type info. The basic expectation what parameters contain which information should be the same for all delegate instances!
See this example:
//-----------------------
// BAD CODE. DO NOT USE!
//-----------------------
// Delegate used for file system operations.
DECLARE_DELEGATE_OneParam(FFileOperationDelegate, FString);
/**
* Called when a file was loaded from the file system
* @param FString - System file path of the newly loaded file
*/
FFileOperationDelegate OnFileLoaded;
/**
* Called when a file was checked out from source control
* @param FString - Source control path relative to workspace root
*/
FFileOperationDelegate OnFileCheckedOut;
In the above example the contents of the string parameter changed from one delegate instance to the other, which may cause some issues if you bind functions to both delegates.
To demonstrate the result of the conventions above, take the following practical example of commonly needed functions/events in a game character. Please note that the functions are written inline and some boilerplate code is omitted for brevity.
class AFooCharacter : public ACharacter
{
public:
// Notify external objects about the character being killed
FKillEvent OnKilled;
// The only instance where a function may be called On...
// is one that exposes a delegate.
FSimpleMulticastDelegate& OnDamageReceived()
{
return DamageReceivedEvent;
}
// Alternative: Expose the event via function that takes in a callback.
// This has the advantage of not exposing internal state.
// This pattern is most commonly seen in Slate events.
void OnDamageReceived(const FSimpleDelegate& InCallback)
{
DamageReceivedEvent.Add(InCallback);
}
protected:
// Called Handle...
// -> we immediately know this is caused from an external event
// most likely on a different object
void HandleComponentHit(float Velocity)
{
// calulate damage from hit
// [...]
Server_DealDamage(Damage);
}
UFUNCTION(Server)
void Server_DealDamage(float Damage)
{
float HealthBefore = Health;
Health -= Damage;
OnDamageReceived.Broadcast();
if (Health <= 0 && HealthBefore > 0)
{
Kill(Damage);
}
}
// Kills this character
// Active verb -> not an event handler, not an event
void Kill(float Damage)
{
// [...]
Multicast_NativeHandleKilled();
}
// Event handler from an own event.
// Can either be bound to the delegate object or invoked explicitly (this is the case here).
// To differentiate native and Blueprint functions, this one is prefixed with Native.
//
// Note that the Handle and Native prefixes are combined with the Multicast_ prefix.
// Priority of prefixes is always:
// - 1: Net (Server_, Client_ Multicast_, OnRep_)
// - 2: BP/C++ (Blueprint, Native)
// - 3: Delegate (Handle)
UFUNCTION(Multicast)
void Multicast_NativeHandleKilled()
{
TriggerRagdoll(Damage);
PlayAnimation(GetDeathAnimation());
// [...]
BlueprintHandleKilled();
OnKilled.Broadcast(Damage);
}
// Blueprint version of NativeHandleKilled().
// To differentiate native and Blueprint functions, this one is prefixed with Blueprint instead.
UFUNCTION(BlueprintImplementableEvent)
void BlueprintHandleKilled();
// There's no c++ version of this function, so the Blueprint prefix may be omitted.
UFUNCTION(BlueprintImplementableEvent)
void TriggerRagdoll(float Damage);
// Native events cannot have different names in C++ and blueprint, because they are the SAME function.
// Therefore it must not get any prefix.
UFUNCTION(BlueprintNativeEvent)
UAnimationSequence* GetDeathAnimation()
{
// [...]
}
private:
// Internal event that is exposed via OnDamageRecevied() function above.
// No strict naming conventions, because it's not part of the API.
// Based on samples from the engine, I would reccommend replacing the usual On prefix with and Event suffix like so:
FSimpleMulticastDelegate DamageReceivedEvent;
}