Files
UE5-Modular-Game-Framework/docs/developer/01-core-foundation.md
Lefteris Notas b2b6e1e7c7 Add developer documentation for systems 11-16, including architecture overview and implementation patterns
- Created detailed documentation for systems 102-135 covering achievements, settings, polish, data assets, input management, and state management.
- Added INDEX.md for easy navigation of developer reference materials.
- Introduced architecture-overview.md to provide a high-level understanding of the framework's structure and communication methods.
- Compiled implementation-patterns.md to outline common UE5 Blueprint patterns for consistent development practices.
2026-05-19 14:13:51 +03:00

22 KiB

01 — Core Foundation Systems (Systems 01-07)

Category Purpose: The foundation layer provides the application kernel, service resolution, shared utilities, interfaces, game rules, session state, and the base data asset from which all items inherit. Everything else in the framework depends on this layer.


System Index

# System Asset Type Role
01 GI_GameTagRegistry Data Asset Central GameplayTag namespace registry & validation
02 FL_GameUtilities Function Library Static utility functions (safe casts, math, tags, debug)
03 I_InterfaceLibrary Interface Collection All 9 framework interfaces for decoupled communication
04 GI_GameFramework Game Instance Application kernel; owns subsystems, manages game phases
05 GM_CoreGameMode Game Mode Session rules, player spawn, chapter transitions, death routing
06 GS_CoreGameState Game State Shared session state: chapter, objectives, play time
07 DA_ItemData Data Asset Base item data asset — all items derive from this template

Category Data Flow

┌──────────────────────────────────────────────────────────────────┐
│                      APPLICATION BOOT SEQUENCE                   │
│                                                                  │
│  GI_GameFramework.Init()                                         │
│    ├─► InitPlatformServices()                                    │
│    ├─► SS_ Subsystems auto-initialize (UE GameInstanceSubsystem) │
│    ├─► SS_SettingsSystem.PopulateDefaults()                      │
│    ├─► SS_SaveSystem.GetSlotManifest()                           │
│    └─► SetGamePhase(MainMenu) → Broadcast OnGamePhaseChanged     │
│                                                                  │
│  GM_CoreGameMode (spawned by engine)                             │
│    ├─► Reads default class references                            │
│    ├─► Spawns PC_CoreController, PS_CorePlayerState              │
│    ├─► Creates GS_CoreGameState                                  │
│    └─► If save data: LoadFromSlot → SetGamePhase(InGame)         │
│       └─► If new game: TransitionToChapter(first chapter)        │
│                                                                  │
│  GS_CoreGameState.BeginPlay()                                    │
│    ├─► Bind to GI_GameFramework.OnGamePhaseChanged               │
│    └─► Tick: accumulate ElapsedPlayTime when InGame              │
│                                                                  │
│  ALL SYSTEMS query GameplayTags via GI_GameTagRegistry            │
│  ALL SYSTEMS use FL_GameUtilities for safe casts & logging       │
│  ALL SYSTEMS communicate via I_InterfaceLibrary interfaces       │
│  ALL ITEMS are defined as DA_ItemData Data Assets                │
└──────────────────────────────────────────────────────────────────┘

01 — GI_GameTagRegistry: Central GameplayTag Registry

What It Does: This is a documentation anchor Data Asset, not a runtime tag database. The actual GameplayTag definitions live in DefaultGameplayTags.ini, but this asset serves as the single place where all framework tag namespaces are documented and validated. Every system that uses GameplayTags references them from this registry's documented namespaces.

How It Works Internally:

  • At OnAssetLoaded, it calls GetAllRegisteredTags() to verify the project has tags registered
  • If zero tags exist, it logs a warning — this catches misconfigured projects early
  • ValidateTag(Tag) checks if a given tag exists in the engine's tag table using RequestGameplayTag()
  • ExportTagNamespace(Prefix) iterates registered tags matching a namespace prefix for tooling/docs
  • The documented namespaces cover: Player.State.*, Interaction.*, Item.*, Narrative.*, Objective.*, AI.*, Save.*, Achievement.*, Environment.*, DeathSpace.*

Implementation Patterns:

  • Create as UPrimaryDataAsset → Blueprint Data Asset
  • Functions are BlueprintCallable / Pure — no tick, no events, no state
  • LogAllTags() and ExportTagNamespace() are Editor-only via #if WITH_EDITOR
  • Framework tags use Framework. prefix; project tags use Game. prefix to avoid collisions

Integration Points:

  • Read by: Every system that does HasTag() / MatchesTag() comparisons
  • No outgoing calls — this is purely passive

Edge Cases:

  • If the tag table is empty at load, a warning fires but the game still runs (tags can be added later)
  • Duplicate tags across namespaces cause RequestGameplayTag() to return the first match — validate in editor
  • Raw string comparisons are forbidden — always use HasTag() or MatchesTag()

02 — FL_GameUtilities: Shared Function Library

What It Does: A static Blueprint Function Library providing safe, reusable utility functions available from any Blueprint without needing an object reference. Handles subsystem lookups, math, tag operations, text formatting, screen projection, and debug logging.

How It Works Internally:

  • Pure static class — no variables, no tick, no event dispatchers, no state
  • Null-safe subsystem retrieval: GetSubsystemSafe() wraps GetSubsystem() in a validity check and returns None instead of crashing
  • Typed casts: GetGameFramework() and GetPlayerController() cast to framework-specific types and log errors on failure
  • Actor utilities: FindComponentByInterface() iterates components checking DoesImplementInterface(); FindNearestActorWithTag() sphere-overlaps actors and filters by GameplayTag
  • Math: RemapFloat(), LerpClamped(), VectorToAngle2D(), AngleDifference() — standard blueprint-safe math
  • Tag utilities: HasGameplayTag() checks actor's tag container safely (returns false if no container); MakeTagFromString() converts strings with validation
  • Text: FormatTime() converts seconds to HH:MM:SS; Pluralise() handles singular/plural; TruncateText() clips with ellipsis
  • Screen: WorldToScreenSafe() projects world position to screen and returns bIsOnScreen (false if behind camera)
  • Debug: All debug functions are wrapped in #if !UE_BUILD_SHIPPING — automatically stripped from shipping builds

Implementation Patterns:

  • All functions are BlueprintCallable or BlueprintPure
  • Functions requiring world context take WorldContextObject as first parameter (UE convention for static libraries)
  • Debug functions use DO_CHECK preprocessor — no manual removal needed

Integration Points:

  • Called by: Every Blueprint in the project (context menu → category name)
  • No outgoing calls except to GI_GameFramework and PC_CoreController via typed cast functions

Edge Cases:

  • GetSubsystemSafe() returning null must be handled by callers — no default fallback
  • MakeTagFromString() returns invalid tag if string isn't registered — caller must validate
  • Math functions assume float inputs — NaN protection uses FMath::IsNaN() internally

03 — I_InterfaceLibrary: All Framework Interfaces

What It Does: Defines all 9 Blueprint Interfaces that form the backbone of cross-system communication. Rather than dozens of scattered interface files, all interfaces are documented in one collection. Interfaces are the preferred communication method — they decouple caller from callee's concrete class.

How It Works Internally — The 9 Interfaces:

Interface Functions Implemented By
I_Interactable OnInteract, OnFocusBegin/End, GetInteractionPrompt, CanInteract, GetInteractionType Doors, pickups, devices, NPCs
I_Inspectable StartInspect/EndInspect, RotateInspect, GetInspectData, HasInspectInfo Items with 3D examine mode
I_Damageable TakeDamage, Heal, IsAlive, GetMaxHealth/GetCurrentHealth, OnDeath, GetDamageModifier Player, enemies, destructibles
I_Holdable OnPickup/OnDrop, GetHoldTransform, IsHeld, OnReleasedFromHold Physics objects, puzzle items
I_Lockable TryLock/TryUnlock, IsLocked, GetRequiredKeyTag, OnLockStateChanged Doors, containers, safes
I_UsableItem UseItem, CanUseItem, GetUseDuration, OnItemUsed Consumables, tools, key items
I_Persistable OnSave/OnLoad, GetSaveTag, NeedsSave Any actor that saves state
I_Toggleable Toggle, SetState, GetCurrentState, GetStateLabel, OnStateChanged Lights, switches, machines
I_Adjustable Adjust, SetValue, GetCurrentValue, GetMinValue/GetMaxValue, OnValueChanged Dials, valves, sliders

Communication Pattern:

  1. Caller uses DoesImplementInterface or Cast to Interface to check
  2. If interface exists, calls the interface function
  3. Implementor handles the logic internally — caller never knows the concrete class
  4. Interface default return values (typically false or 0.0f) handle missing implementations gracefully

Implementation Patterns:

  • Each interface function has a single clear responsibility
  • Implementors should NEVER call interface functions on themselves (use internal functions instead)
  • New interfaces extend the collection; child interfaces keep base interfaces stable
  • Multiple interfaces can be implemented by one actor (e.g., a door implements I_Interactable, I_Toggleable, I_Lockable, I_Persistable)

Edge Cases:

  • Calling an interface function on an actor that doesn't implement it returns the default value (no crash)
  • OnInteract returning false triggers a "blocked" feedback sound — caller handles this
  • CanInteract provides BlockReason text for UI — always provide a reason, not just false

04 — GI_GameFramework: Application Kernel

What It Does: The single persistent object that lives for the entire application session. It owns all SS_ GameInstanceSubsystems, manages save slot ownership, provides the canonical service resolver (GetSubsystem()), and tracks the top-level game phase (MainMenu → Loading → InGame → Paused → Cutscene → DeathLoop → AltDeathSpace → Credits → PostGame).

How It Works Internally:

State Machine — Game Phases:

MainMenu → Loading → InGame ⇄ Paused
                          ↓
                       Cutscene
                          ↓
                       DeathLoop → AltDeathSpace
                          ↓
                       Credits → PostGame
  • SetGamePhase(Phase) updates CurrentGamePhase and broadcasts OnGamePhaseChanged
  • Phase transitions are the single point of authority — all systems react to dispatcher, not direct checks
  • SessionFlags (Map of GameplayTag → Boolean) stores transient flags that don't persist to disk

Boot Sequence (Init):

  1. Call parent Init()
  2. InitPlatformServices() — routes to platform-specific stubs based on PlatformType (Steam/PS/Xbox/Switch/Generic)
  3. All SS_ Subsystems are auto-initialized by UE's GameInstanceSubsystem system
  4. SS_SettingsSystem.PopulateDefaults() loads persistent settings
  5. SS_SaveSystem.GetSlotManifest() checks existing saves
  6. SetGamePhase(MainMenu) — game starts at main menu
  7. Broadcast OnPlatformReady

Service Resolution:

  • GetSubsystem(Tag) maps a GameplayTag to a subsystem class, then calls GetSubsystem()
  • This is the canonical entry point for any system needing a global service
  • Returns null and logs warning if tag isn't mapped — caller must handle null

Implementation Patterns:

  • SetActiveSlot(SlotIndex) designates save slot without triggering load — slot is just an integer
  • bFirstLaunch stays true until onboarding flow clears it — controls intro sequence
  • PlatformType can be overridden via command-line: -Platform=Steam
  • Add new subsystems to the init list — they auto-initialize via UE's native system

Integration Points:

  • Owns: All SS_ Subsystems
  • Broadcasts: OnGamePhaseChangedGM_CoreGameMode, WBP_HUDController, WBP_MenuFlowController
  • Broadcasts: OnPlatformReady → all platform-dependent systems
  • Called by: Any Blueprint via GetSubsystem()

Edge Cases:

  • Calling SetGamePhase() with the same value should not fire dispatcher (prevents infinite loops)
  • GetSubsystem() during PreInit() returns null — systems must handle early-access gracefully
  • For multi-world / split-screen: this is a single-instance singleton per application session

05 — GM_CoreGameMode: Session Rules & Player Spawning

What It Does: Sets the rules of the game session: which pawn, controller, player state, HUD, and game state classes to use. Manages chapter transitions, win/loss/death routing, and coordinates with the narrative system for story progression.

How It Works Internally:

Default Class Setup:

  • DefaultPawnClass — the GASP-based player pawn
  • DefaultControllerClassPC_CoreController
  • DefaultPlayerStateClassPS_CorePlayerState
  • DefaultGameStateClassGS_CoreGameState
  • DefaultHUDClassWBP_HUDController

Chapter Transition Flow:

  1. TransitionToChapter(ChapterTag) is called (by narrative trigger or menu)
  2. Sets CurrentChapterTag on GM
  3. Calls GI_GameFramework.SetGamePhase(Loading)
  4. Opens/streams the level associated with ChapterTag
  5. On level loaded: syncs chapter to GS_CoreGameState, fires BPC_NarrativeSystem.OnChapterStart
  6. Calls SetGamePhase(InGame)

Death Handling Flow:

  1. BPC_DeathHandlingSystem dispatches player death
  2. HandlePlayerDead() is called
  3. Disables pause (bPauseAllowed = false)
  4. Sets phase to DeathLoop
  5. Decision: if Alt Death Space is configured → TriggerEnding(AltDeathSpace), else → load checkpoint via SS_SaveSystem, respawn player

Implementation Patterns:

  • All default class references are exposed as instance-editable variables — no hardcoded classes
  • bPauseAllowed is a runtime flag that menu widgets check before pausing (false during cutscenes/death)
  • CurrentChapterTag is a GameplayTag — chapters are data-driven, not hardcoded strings
  • TriggerEnding(EndingTag) passes the ending condition to BPC_EndingAccumulator

Integration Points:

  • Owns/Spawns: Player Controller, Player State, Game State, HUD
  • Listens to: BPC_DeathHandlingSystem (death events), BPC_NarrativeSystem (chapter triggers)
  • Calls: GI_GameFramework.SetGamePhase(), SS_SaveSystem.LoadCheckpoint()
  • Broadcasts: OnChapterTransition, OnGameOverTriggered

Edge Cases:

  • TransitionToChapter during loading should be ignored (check phase before transitioning)
  • HandlePlayerDead must be idempotent — calling it twice shouldn't double-stack death states
  • For multiplayer: extend to derive from replicated GameMode base class

06 — GS_CoreGameState: Shared Session State

What It Does: A replicated game state singleton visible to all players. Tracks session-level data: elapsed play time, active chapter, narrative phase, encounter status, and active objectives. UI widgets and narrative systems read from here rather than querying individual systems.

How It Works Internally:

Tracked State:

  • ElapsedPlayTime — accumulates in Tick only when GamePhase is InGame
  • ActiveChapterTag — current story chapter (set by GM_CoreGameMode)
  • ActiveNarrativePhase — sub-chapter phase (set by NarrativeStateSystem)
  • bEncounterActive — whether an AI encounter is running (set by EncounterDirector)
  • ActiveObjectiveTags — array of active objective tags (set by ObjectiveSystem)

State Change Pattern (all variables follow this):

  1. Setter function updates internal variable
  2. Broadcasts corresponding event dispatcher
  3. UI/replication OnRep functions mirror the broadcast for network clients

Dispatcher Protocol:

  • OnElapsedPlayTimeUpdated — fires every ~1 second tick (throttled for performance)
  • OnChapterChanged — UI updates chapter display, narrative systems react
  • OnNarrativePhaseChanged — sub-chapter transitions
  • OnEncounterActiveStateChanged — HUD shows encounter status, atmosphere shifts
  • OnObjectiveTagsChangedWBP_ObjectiveDisplay refreshes

Implementation Patterns:

  • Intentional lightweight — a singleton data holder, not a logic processor
  • All modification happens through dedicated setter functions (never direct variable write)
  • Objective tags array: add/remove check for duplicates before modifying
  • Time accumulation is gated by GamePhase check — stops during pause/loading/cutscenes
  • For multiplayer: all variables are Replicated Using with OnRep notification

Integration Points:

  • Written by: GM_CoreGameMode, BPC_ObjectiveSystem, BPC_EncounterDirector
  • Read by: WBP_HUDController, WBP_ObjectiveDisplay, BPC_RunHistoryTracker
  • Listens to: GI_GameFramework.OnGamePhaseChanged (to gate time accumulation)

Edge Cases:

  • Rapid chapter changes in same tick must fire dispatcher for each — use queue if necessary
  • Empty objective list removal from empty list should not crash
  • Time accumulation stops correctly on phase change to Paused/Loading/Cutscene

07 — DA_ItemData: Base Item Data Asset

What It Does: The single source of truth for every item in the game. Each item is one DA_ItemData Primary Data Asset — no item data lives in Blueprint logic. This is the base class; all items inherit this template and fill in their specific values.

How It Works Internally:

Core Properties (every item has these):

  • ItemTag (GameplayTag) — unique identifier, must be registered in GI_GameTagRegistry
  • DisplayName, Description (FText) — player-facing text
  • Icon (Texture2D soft reference) — UI icon
  • WorldMesh (StaticMesh soft reference) — 3D mesh when dropped in world
  • Weight (Float) — carry weight units
  • StackLimit (Integer) — max per-slot stack count
  • ItemType (E_ItemType) — classification: Weapon, Ammo, Consumable, KeyItem, Document, Collectible, Tool, Resource, Misc

Conditional Sub-Data (shown based on ItemType via EditCondition):

  • EquipmentData — shown when ItemType is Weapon or Tool (slot type, stats)
  • ConsumableData — shown when ItemType is Consumable (health restore, stress reduce)
  • InspectData — shown when bHasInspectMode is true (3D rotation anchors)
  • AmmoData — shown when ItemType is Ammo (ammo type tag, per-pickup count)

Combination Logic:

  • CombinesWith — array of ItemTags this can combine with
  • CombineResult — the ItemTag produced on successful combination
  • Both fields are read by BPC_ItemCombineSystem to validate and execute combinations

Data-Driven Design:

  • This is a pure data container — no runtime functions, no tick, no events
  • ValidateItemData() exists as an Editor-only function for content team validation
  • CustomProperties map (Name → String) provides per-project extensibility without modifying the base class

Implementation Patterns:

  • Create one asset per item: name convention DA_Item_[ShortName]
  • All ItemTag values must be registered in tag table before use
  • bIsKeyItem forces bCanBeDropped = false — key items can't be discarded
  • StackLimit = 1 for weapons, tools, key items; larger for ammo, consumables, resources
  • Assets registered with Primary Asset Manager (label: Item) for async loading

Integration Points:

  • Read by: BPC_InventorySystem (every property), BPC_ConsumableSystem (ConsumableData), BPC_EquipmentSlotSystem (EquipmentData), BPC_AmmoResourceSystem (AmmoData), BPC_ItemCombineSystem (CombinesWith/CombineResult), BPC_InspectItemSystem (InspectData)
  • Displayed by: WBP_InventoryMenu (via InventorySystem), WBP_InteractionPromptDisplay (DisplayName for pickup prompt)

Edge Cases:

  • Two items with same ItemTag causes confusion — editor validation must enforce uniqueness
  • Key items with bCanBeDropped = true is prevented by ValidateItemData() forcing it false
  • Empty DisplayName/Description should be caught by validation — not crash, just warn
  • Consumable items with all effect values = 0 are technically valid but useless — validation should warn

Initialization Order (BeginPlay Sequence)

1. GI_GameFramework.Init()        ← Engine creates GameInstance
2.   ├─ InitPlatformServices()
3.   ├─ SS_ Subsystems init       ← UE auto-initializes GameInstanceSubsystems
4.   ├─ SS_SettingsSystem loads
5.   └─ SetGamePhase(MainMenu)

6. GM_CoreGameMode.InitGame()     ← Engine creates GameMode when level loads
7.   ├─ Sets default classes
8.   ├─ Spawns GS_CoreGameState
9.   └─ Spawns Player Controller + Pawn

10. GS_CoreGameState.BeginPlay()
11.  ├─ Binds to OnGamePhaseChanged
12.  └─ Starts time tick

13. DA_ItemData assets are loaded on demand ← Asset Manager async loads
14. FL_GameUtilities calls available immediately ← Static library
15. GI_GameTagRegistry.OnAssetLoaded() ← Fires when data asset loads

Common Implementation Patterns in This Category

  1. Tag-Driven Everything: No booleans for state, no string comparisons. Everything is a GameplayTag compared via HasTag() / MatchesTag().
  2. Dispatcher-Based Phase Changes: Systems don't poll CurrentGamePhase — they bind to OnGamePhaseChanged and react.
  3. Data Assets for Content: All item definitions, platform configs, and settings live in Data Assets, never hardcoded in Blueprints.
  4. Interface-First Communication: Systems query for interface implementation before calling — no direct class casting.
  5. Null-Safe Subsystem Lookup: Always use FL_GameUtilities.GetSubsystemSafe() — never call engine GetSubsystem() directly.
  6. Session Flags vs Saved Data: GI_GameFramework.SessionFlags for transient state; I_Persistable for data that survives sessions.

Developer Reference v1.0 — 01 Core Foundation Systems. Companion to docs/blueprints/01-core/ specs.