Unreal Engine C++ My Programming Standards

Class Organization

The class organization is important for keeping the header files readable.
Some of the following techniques can be useful:

Header File (.h)

  • Keep variables and functions grouped together separately in the header file.
  • Try not to split the default variable assignment between the header and cpp files.
  • Use const when declaring functions where possible.
  • Give functions relevant names.
  • Have single line separation between all functions and variables declared.
  • Avoid white space and incorrect line indentation.
  • Declare any enum, struct, or macro before the UCLASS macro.
  • Declare interface function overrides at the bottom of the class.
  • Comment functions and variables in the format shown below.
				
					/** Some comment describing the function being declared.
  * @Param InObject1, What is this variable for?
  * @Param InObject2, What is this variable for? */
void ExampleFunction(UObject* InObject1, UObject* InObject2);
				
			
				
					/** Description of what his variable is for. */
UPROPERTY(BlueprintReadWrite, EditAnywhere, Catagory = "Some Category")
bool bSomeBoolean;
				
			
 

Code File (.cpp)

  • Implement functions in the order they have been declared in the header file.
  • Try to avoid too many comments within functions unless it’s something that is not obvious.
  • In the constructor, it is best to declare default values for variables in the following way.
				
					UExampleComponent::UExampleComponent(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	, bExampleBoolean (false),
	, ExampleInteger (20)
{
    ExampleStaticMesh = CreateDefaultSubobject<UStaticMeshComponent>("ExampleMesh");
}
				
			

 

Naming Conventions

Naming conventions are important also so the code can be read in a way where class-default variables are clearly differentiated from local function variables etc.

File naming

  • Template classes are prefixed by “T”.
  • Interface classes are prefixed by “I”.
  • Actors should have “Actor” on the name. For Example: VRHandActor, VRPawnActor.
  • Components should have “Component” on the name. For Example: WidgetSplineComponent, StaticMeshComponent.
  • Enum classes should be named starting with “E“.
  • Structure classes should be named starting with “F“.

Header Declaration Naming

Any name of either a function or variable should give a clear idea of what the function/variable is for.

Functions

  • The function name should start with a capital letter.
  • Input variables into the function should start with “In“.
  • Output variables in the function should start with “Out“.
  • All boolean variables should start with “b“.

For Example:

				
					void ExampleFunction(UObject* InObject1, UObject& OutObject2, bool bSuccess);
				
			

Variables

  • Anything declared in the header file as a class variable should have a capital letter.
  • Anything declared in a function input should either have “In“, “Out“ or “b“ for boolean in the front.
  • Anything declared within a function should be lower case preceded by upper case letters.

For Example:

				
					// Header Class

public:

/** Description of what his variable is for. */
UPROPERTY(BlueprintReadWrite, EditAnywhere, Catagory = "Some Category")
UStaticMeshComponent* TestStaticMesh;

private:

int TestInteger;
boolean bTestBoolean;

// C++ Class

void ExampleFunction(UObject* InObject1, UObject& OutObject2, bool bSuccess)
{
    boolean bTestBoolean = false;
    int testInteger = 0;
    UStaticMeshComponent* testStaticMesh = nullptr;
}
				
			

 

Code Formatting

Some functions in code can be written in a much more readable and easier to maintain way.

If – Statements

Braces do not always have to be used. Shown in the example below.

				
					// This if statement is only calling one line of code, so the brackets are not needed.
if (bBooleanValue)
{
    SomeOtherFunction();
}
      
// The other way to write this...
if (bBooleanValue)
    SomeOtherFunction(); 
    
// If there is more lines of code then you must use the braces.
if (bBooleanValue)
{
    SomeOtherFunction();
    return;
} 
				
			

 

There are also better ways to write functions than using nested if statements.

				
					// These nested if statements work as intended but there is a better way to write this code.
if (bBooleanValue)
{
	if(SomeObject->IsValid() != nullptr)
	{
		const int32 numberOfLoops = 10;
		int32 numberOfSomeValue = 0;
		for(int32 i = 0; i < numberOfLoops; ++i) { if (SomeObject->SomeArray[i] == "SomeValue")
				numberOfSomeValue++;
		}
	}
}

// Instead of using the nested if statements take advantage of return nodes and separate functions.
if (!bBooleanValue)
    return;
    
if (!SomeObject->IsValid())
    return;
    
const int32 numberOfLoops = 10;
int32 numberOfSomeValue = 0;
for(int32 i = 0; i < numberOfLoops; ++i) { if (SomeObject->SomeArray[i] == "SomeValue")
		numberOfSomeValue++;
}
				
			

 

Too much information in a if – statement can throw off readability also and future changes can be made difficult.

				
					if (bSomeBoolean && (bSomeFloatValue <= 100.0f || bSomeOtherBoolean) && 
            bGameStarted && bPlayerStillHasPawn && !bGameOver)
    DoSomething();

// The if statement above can be re-written to make more sense.
const bool bHealthCheck = bSomeBoolean && (bSomeFloatValue <= 0.0f || bSomeOtherBoolean);
const bool bCanSpawn = bGameStarted && bPlayerStillHasPawn && !bGameOver;
if (bHealthCheck && bCanSpawn)
    DoSomething();
				
			

 

Switches

Switches are just more complex if statements for Enum/integer values, they are useful in many areas specifically for making a class modular with Enum values.

Knowing where and when to use switches can be tricky.

For Example:

				
					enum class ESomeEnum : uint8
{
    EnumValue1,
    EnumValue2,
    EnumValue3
}

void USomeClass::SomeFunction(ESomeEnum InEnumValue)
{
    // Example of a switch being used to do something different for each case.
    Switch(InEnumValue)
    {
        case ESomeEnum::EnumValue1:
            ExampleVariable = 23;
            OtherFunction();
        break;
        case ESomeEnum::EnumValue2:
            OtherFunction();
        break;
        case ESomeEnum::EnumValue3:
            OtherFunction();
        break;
        default:
        break;
    }
    
    // A much better way of writing the switch statement above would be the following.
    Switch(InEnumValue)
    {
        case ESomeEnum::EnumValue1:
            ExampleVariable = 23;
        case ESomeEnum::EnumValue2:
        case ESomeEnum::EnumValue3:
            OtherFunction();
        default:
        break;
    }
}
				
			

 

Duplicate Code

Code that shows up in a class more than once is classed as duplicate code, this is not a major issue when its function calls from a variable, but when it is a few or more lines of code that are being duplicated it is best to separate it into a function.

This is also important to do on code that is not duplicated but could be called on its own from outside of the class and have a use outside of its original function.

Example of where duplicate code can be removed.

				
					void USomeComponent::SomeFunction()
{
    const bool bCanUse = ExampleObject->GetCanUse();
    if (bCanUse)
    {
        DoSomething();
        UseCount++;
        return;
    }
    
    UseCount = StartingUseCount;
    bResetUseCount = true;
    ResetUseCount();
    UE_LOG(LogSomeComponent, Log, TEXT("Cannot use in SomeFunction resetting use count."));
}

void USomeComponent::SomeOtherFunction()
{
    RespawnPlayer();

    const bool bCanUse = ExampleObject->GetCanUse();
    if (bCanUse)
    {
        DoSomething();
        UseCount++;
        return;
    }
}
				
			

 

The code above can be rewritten in the following way to remove the duplicate code.

				
					void USomeComponent::SomeFunction()
{
    if (CheckIfCanUse())
        return;
    
    UseCount = StartingUseCount;
    bResetUseCount = true;
    ResetUseCount();
    UE_LOG(LogSomeComponent, Log, TEXT("Cannot use in SomeFunction resetting use count."));
}

void USomeComponent::SomeOtherFunction()
{
    RespawnPlayer();
    CheckIfCanUse();
}

bool USomeComponent()::CheckIfCanUse()
{
    const bool bCanUse = ExampleObject->GetCanUse();
    if (bCanUse)
    {
        DoSomething();
        UseCount++;
        return true;
    }
    
    return false;
}
				
			

 

Other Resources

Epic Games themselves have a coding standards post on their website which has a bunch of other really useful information to help write clean and efficient code.

https://docs.unrealengine.com/4.26/en-US/ProductionPipelines/DevelopmentSetup/CodingStandard/

4 thoughts on “Unreal Engine C++ My Programming Standards”

Leave a Comment

Your email address will not be published.