Code Style Guide

March 7, 2026 · View on GitHub

This document defines the coding standards for the Adastrea project. Following these guidelines ensures consistency and maintainability.

Table of Contents

General Principles

  1. Readability First: Code should be self-documenting
  2. Consistency: Follow existing patterns in the codebase
  3. Simplicity: Prefer simple solutions over clever ones
  4. Modularity: Keep systems decoupled and focused
  5. Performance: Optimize when necessary, not prematurely

C++ Style

Naming Conventions

Classes and Structs

// UObject-derived classes
UCLASS()
class ADASTREA_API UMyDataAsset : public UDataAsset
{
    // ...
};

// Actor-derived classes
UCLASS()
class ADASTREA_API AMyActor : public AActor
{
    // ...
};

// Structs
USTRUCT(BlueprintType)
struct FMyStruct
{
    // ...
};

// Enums
UENUM(BlueprintType)
enum class EMyEnum : uint8
{
    // ...
};

// Interfaces
UINTERFACE()
class UMyInterface : public UInterface
{
    // ...
};

class IMyInterface
{
    // ...
};

Rules:

  • U prefix for UObject-derived classes
  • A prefix for Actor-derived classes
  • F prefix for structs (including USTRUCT)
  • E prefix for enums
  • I prefix for interface implementations
  • PascalCase for all class names

Variables

// Member variables
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stats")
float MaxHealth;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stats")
bool bIsAlive;

// Local variables
int32 HealthValue = 100;
float DamageAmount = 25.0f;
bool bShouldRespawn = true;

// Constants
static const float kMaxSpeed = 1000.0f;
static constexpr int32 MAX_CREW = 100;

// Pointers
AActor* MyActor = nullptr;
UDataAsset* MyDataAsset = nullptr;

Rules:

  • PascalCase for member variables
  • camelCase for local variables (or PascalCase - be consistent)
  • Boolean variables prefixed with b
  • Constants: kConstantName or ALL_CAPS
  • Always initialize pointers to nullptr

Functions

// Blueprint-callable function
UFUNCTION(BlueprintCallable, Category="Combat")
void DealDamage(float DamageAmount);

// Blueprint-pure function (getter)
UFUNCTION(BlueprintPure, Category="Stats")
float GetHealth() const;

// Blueprint-implementable event
UFUNCTION(BlueprintImplementableEvent, Category="Events")
void OnHealthChanged(float NewHealth);

// Blueprint-native event
UFUNCTION(BlueprintNativeEvent, Category="AI")
void MakeDecision();
virtual void MakeDecision_Implementation();

// Regular function
void UpdateInternalState();

Rules:

  • PascalCase for function names
  • Descriptive verb-noun combinations (GetHealth, SetDamage, CalculateScore)
  • Use const for functions that don't modify state
  • Mark Blueprint-callable functions appropriately
  • BlueprintNativeEvent functions need _Implementation suffix in .cpp

Header File Structure

#pragma once

#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "MyDataAsset.generated.h"

// Forward declarations
class UOtherClass;
class AOtherActor;

/**
 * Brief one-line description of the class
 *
 * Detailed description explaining:
 * - What this class does
 * - When to use it
 * - How it integrates with other systems
 *
 * Usage Example:
 * - Create a Blueprint based on this class
 * - Configure properties in editor
 * - Use Blueprint functions to interact
 */
UCLASS(BlueprintType)
class ADASTREA_API UMyDataAsset : public UDataAsset
{
    GENERATED_BODY()

public:
    // ====================
    // Properties
    // ====================

    // Brief description of what this property does
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Basic Info")
    FText DisplayName;

    // ====================
    // Public Functions
    // ====================

    /**
     * Brief description of what this function does
     * @param ParamName Description of parameter
     * @return Description of return value
     */
    UFUNCTION(BlueprintCallable, Category="My Category")
    int32 CalculateValue(float Input) const;

protected:
    // ====================
    // Protected Members
    // ====================

    UPROPERTY()
    int32 InternalValue;

private:
    // ====================
    // Private Members
    // ====================

    void InternalHelperFunction();
};

Implementation File Structure

#include "MyDataAsset.h"
#include "Other/Dependencies.h"

UMyDataAsset::UMyDataAsset()
{
    // Initialize default values
    DisplayName = FText::FromString(TEXT("Default Name"));
    InternalValue = 0;
}

int32 UMyDataAsset::CalculateValue(float Input) const
{
    // Validate input
    if (Input < 0.0f)
    {
        UE_LOG(LogTemp, Warning, TEXT("Invalid input: %f"), Input);
        return 0;
    }

    // Calculate result
    int32 Result = FMath::RoundToInt(Input * InternalValue);

    return Result;
}

void UMyDataAsset::InternalHelperFunction()
{
    // Implementation
}

UPROPERTY Best Practices

Critical Rule: ALL UObject* pointers MUST have UPROPERTY() macro, even private ones. This is required for Unreal Engine's garbage collection to track object references. Without UPROPERTY, objects may be prematurely destroyed, causing crashes.

// ❌ WRONG - Missing UPROPERTY (will cause GC issues)
private:
    UDataAsset* MyData;

// ✅ CORRECT - All UObject* pointers must have UPROPERTY
private:
    UPROPERTY()
    UDataAsset* MyData;

// ✅ CORRECT - Even private pointers need UPROPERTY for GC tracking
protected:
    UPROPERTY()
    AActor* CachedActor;

// Editable in editor, read-only in Blueprints
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Config")
float MaxValue;

// Editable everywhere
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Config")
float CurrentValue;

// Visible but not editable
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Stats")
float CalculatedValue;

// With constraints
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stats",
          meta=(ClampMin="0", ClampMax="100", UIMin="0", UIMax="100"))
float HealthPercent;

// With multiline text
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Info",
          meta=(MultiLine=true))
FText Description;

// Array
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Items")
TArray<UItemDataAsset*> Items;

// Map
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Relations")
TMap<FName, int32> Relationships;

UFUNCTION Best Practices

// Callable function
UFUNCTION(BlueprintCallable, Category="Combat")
void Attack(float Damage);

// Pure function (no side effects, shows as pure node in Blueprint)
UFUNCTION(BlueprintPure, Category="Stats")
float GetHealth() const;

// BlueprintNativeEvent (can be overridden in Blueprint)
UFUNCTION(BlueprintNativeEvent, Category="AI")
void OnTargetDetected(AActor* Target);
virtual void OnTargetDetected_Implementation(AActor* Target);

// BlueprintImplementableEvent (implemented only in Blueprint)
UFUNCTION(BlueprintImplementableEvent, Category="Events")
void OnDamageReceived(float Damage);

Comments

// Single-line comment for brief notes
int32 Value; // Inline comment

/**
 * Multi-line Doxygen-style comment for classes, functions, and properties
 *
 * Detailed explanation of what this does, why it exists, and how to use it.
 *
 * @param InputValue The value to process
 * @param bShouldValidate Whether to validate the input
 * @return The processed result, or 0 if validation fails
 */
UFUNCTION(BlueprintCallable, Category="Processing")
int32 ProcessValue(int32 InputValue, bool bShouldValidate) const;

// TODO: Implement advanced feature
// FIXME: This has a known issue with negative values
// NOTE: This relies on initialization order

Blueprint Style

Naming Conventions

  • Blueprints: BP_SystemName_Purpose

    • BP_Ship_Fighter
    • BP_Station_TradeHub
    • BP_UI_MainMenu
  • Widgets: WBP_WidgetName

    • WBP_HUD_Main
    • WBP_Dialog_Confirmation
  • Data Assets: DA_Type_Name

    • DA_Ship_Pathfinder
    • DA_Faction_SolarisUnion
    • DA_Personnel_Captain

Organization

Content/
├── Blueprints/
│   ├── Ships/
│   │   ├── BP_Ship_Fighter
│   │   ├── BP_Ship_Freighter
│   │   └── BP_Ship_Capital
│   ├── Stations/
│   │   ├── BP_Station_Military
│   │   └── BP_Station_Trade
│   └── UI/
│       ├── WBP_HUD_Main
│       └── WBP_Menu_Settings
└── DataAssets/
    ├── Ships/
    ├── Factions/
    └── Personnel/

Blueprint Graphs

  1. Use Comment Boxes:

    • Group related nodes
    • Describe what each section does
    • Use consistent colors
  2. Keep Functions Small:

    • Max 20-30 nodes per function
    • Extract complex logic to separate functions
  3. Use Reroute Nodes:

    • Keep wires organized
    • Avoid crossing wires
  4. Name Variables Clearly:

    • Use categories to organize
    • Add tooltips for complex variables
  5. Consistent Layout:

    • Left to right flow
    • Top to bottom for branches
    • Align nodes neatly

Asset Naming

General Rules

[Prefix]_[AssetType]_[Descriptor]_[Variant]_[Suffix]

Examples:
T_Rock_Diffuse_01_D      (Texture, Diffuse)
M_Metal_Shiny            (Material)
MI_Metal_Shiny_Blue      (Material Instance)
SM_Crate_Large           (Static Mesh)
SK_Character_Hero        (Skeletal Mesh)
A_Hero_Idle              (Animation)
DA_Ship_Fighter          (Data Asset)
BP_Enemy_Boss            (Blueprint)

Common Prefixes

Asset TypePrefixExample
BlueprintBP_BP_Ship_Fighter
Data AssetDA_DA_Faction_Traders
MaterialM_M_Metal_Base
Material InstanceMI_MI_Metal_Blue
TextureT_T_Metal_Diffuse
Static MeshSM_SM_Crate
Skeletal MeshSK_SK_Character
AnimationA_A_Walk
SoundS_S_Explosion
ParticleP_P_Smoke
Widget BlueprintWBP_WBP_MainMenu

Texture Suffixes

  • _D - Diffuse/Base Color
  • _N - Normal Map
  • _R - Roughness
  • _M - Metallic
  • _AO - Ambient Occlusion
  • _H - Height Map
  • _E - Emissive

Documentation

When to Document

  • Always: Public APIs, classes, complex functions
  • Usually: Protected members, important algorithms
  • Rarely: Self-explanatory code, obvious getters/setters

How to Document

/**
 * Brief description (one line)
 *
 * Detailed explanation:
 * - What it does
 * - Why it exists
 * - How to use it
 * - Important caveats or gotchas
 *
 * @param ParamName Parameter description
 * @return Return value description
 * @see RelatedClass, RelatedFunction
 */

Documentation Files

  • System Guides: Assets/[System]Guide.md
  • Workflows: Assets/[System]Workflow.md
  • Templates: Assets/[System]Templates.md
  • API Reference: Assets/[System]APIReference.md

Code Review Checklist

Before submitting code:

  • Follows naming conventions
  • Has appropriate comments
  • Blueprint exposure is correct
  • Properties have categories
  • No compiler warnings
  • Code is formatted consistently
  • Documentation is updated
  • Examples are provided

Tools

Visual Studio:

  • Visual Assist (IntelliSense enhancement)
  • CodeMaid (code cleanup)
  • ReSharper C++ (refactoring)

VS Code:

  • C/C++ Extension Pack
  • Unreal Engine 4 Snippets
  • Blueprint syntax highlighting

Code Formatting

Use .editorconfig for consistent formatting:

  • Indent: 4 spaces
  • Line endings: LF (Unix)
  • Trim trailing whitespace
  • Final newline in files

References


Remember: Consistency is more important than perfection. When in doubt, follow existing patterns in the codebase.