Coding conventions for Unreal Engine 4 for C++, Blueprint and Python
These conventions focus mostly on source code comments as 90% of your code’s documentation will be written in this format.
For API documentation it should also be totally sufficient. Even if you need some external copy of your API documentation, e.g. because you’re writing a publicly available plugin, your primary source for this external copy should stem from your source code (e.g. generated via Doxygen), so they don’t easily get out of sync.
In addition to this you should have a separate archive for additional documents and diagrams that supplement the source code based docs.
Write self-documenting code wherever possible
// Bad:
t = s + l - b;
// Good:
TotalLeaves = SmallLeaves + LargeLeaves - SmallAndLargeLeaves;
Comments that merely repeat what is already expressed in code do not serve any purpose and should be omitted
// Bad:
// increment Leaves
++Leaves;
// Good:
// We know there is another tea leaf, because we ruled out all other options.
++Leaves;
Do not comment bad code, rewrite it
// Bad:
// total number of leaves is sum of
// small and large leaves less the
// number of leaves that are both
t = s + l - b;
// Good:
TotalLeaves = SmallLeaves + LargeLeaves - SmallAndLargeLeaves;
Never contradict the code
// Bad:
// never increment Leaves!
++Leaves;
// Good:
// We know there is another tea leaf, because we ruled out all other options.
++Leaves;
Comments that describe API should follow the following style
Type docs used for classes, structs, enums, namespaces:
/**
* GameMode is a subclass of GameModeBase that behaves like a multiplayer match-based game.
* It has default behavior for picking spawn points and match state.
* If you want a simpler base, inherit from GameModeBase instead.
*/
UCLASS()
class ENGINE_API AGameMode : public AGameModeBase
{
...
};
Single line docs
/** Returns true if the match state is InProgress or other gameplay state */
UFUNCTION(BlueprintCallable, Category="Game")
virtual bool IsMatchInProgress() const;
Multi-line docs with parameter descriptions. Parameter descriptions may be omitted (even for individual parameters) if they add no value.
/**
* Called from CommitMapChange before unloading previous level.
* Used for asynchronous level streaming
* @param PreviousMapName - Name of the previous persistent level
* @param NextMapName - Name of the persistent level being streamed to
*/
virtual void PreCommitMapChange(const FString& PreviousMapName, const FString NextMapName);
either above the variables/cases
/** Possible state of the current match, where a match is all the gameplay that happens on a single map */
namespace MatchState
{
// We are entering this map, actors are not yet ticking
extern ENGINE_API const FName EnteringMap;
// Actors are ticking, but the match has not yet started
extern ENGINE_API const FName WaitingToStart;
// Normal gameplay. Specific games will have their own state machine inside this state
extern ENGINE_API const FName InProgress;
// Match has ended so we aren't accepting new players, but actors are still ticking
extern ENGINE_API const FName WaitingPostMatch;
// We are transitioning out of the map to another location
extern ENGINE_API const FName LeavingMap;
// Match has failed due to network issues or other problems, cannot continue
extern ENGINE_API const FName Aborted;
}
or behind the values and left aligned:
/** Possible state of the current match, where a match is all the gameplay that happens on a single map */
namespace MatchState
{
extern ENGINE_API const FName EnteringMap; // We are entering this map, actors are not yet ticking
extern ENGINE_API const FName WaitingToStart; // Actors are ticking, but the match has not yet started
extern ENGINE_API const FName InProgress; // Normal gameplay. Specific games will have their own state machine inside this state
extern ENGINE_API const FName WaitingPostMatch; // Match has ended so we aren't accepting new players, but actors are still ticking
extern ENGINE_API const FName LeavingMap; // We are transitioning out of the map to another location
extern ENGINE_API const FName Aborted; // Match has failed due to network issues or other problems, cannot continue
}
Subsections, headings, etc. can use blocks of // single line comments
like this:
Headings:
//////////////////////////////////////////////////////////////////////////
/// Big Heading: always 74 characters wide
/// can have multiple lines
//////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
// Medium Heading: always 74 characters wide
// can have multiple lines
//------------------------------------------------------------------------
//-----------------------------
// Small Heading + single line
//-----------------------------
Long separators (e.g. for separating test cases in a single test file)
//////////////////////////////////////////////////////////////////////////
or
//------------------------------------------------------------------------
Virtual function override groups:
// - Parent Class
virtual void Foo() override;
virtual void Bar() override;
// --
Comments that are placed inline in function definitions and source files should usually use blocks of // single line comments
:
void FFoo::Bar()
{
// Number of shares must always be 42 because...
const int32 NumSharesToBuy = 42;
}
The UE4 reflection system allows to add metadata to uproperties and ufunctions. Most of the metadata is optional to use on a per-case basis, but we reccommend to always add the following data:
Prefix all static blueprint functions categories with your project or plugin name, e.g.
UFUNCTION(BlueprintCallable, Category = "Foo Plugin|Screen Rendering")
static UTexture* RenderMaterial(UMaterial* Material, int32 TextureSize);