Files
UE5-Modular-Game-Framework/docs/developer/implementation-patterns.md
Lefteris Notas fee12b115f Refactor GameplayTag documentation and implementation
- Updated references from GI_GameTagRegistry to DA_GameTagRegistry in architecture overview and implementation patterns documentation.
- Added new Blueprint specification for GI_StarterGameInstance, detailing its purpose, configuration, and integration pattern.
- Introduced DA_GameTagRegistry Blueprint specification, centralizing GameplayTag management and providing functions for tag validation and logging.
- Created documentation for the Starter GameInstance, outlining its role in the project setup and how other systems can integrate with it.
2026-05-20 14:31:52 +03:00

12 KiB

Common UE5 Blueprint Implementation Patterns

Purpose: This document catalogs the recurring Blueprint implementation patterns used throughout the framework. When implementing a blueprint spec into an actual UE5 Blueprint asset, reference these patterns for consistent, correct implementation.


Pattern 1: Interface-First Communication

When to Use: Any time System A needs to call a function on System B without knowing System B's concrete class.

Blueprint Implementation:

[System A Blueprint]
  TargetActor = GetOwner()  // or any actor reference
  bImplements = DoesImplementInterface(TargetActor, I_Interactable)
  Branch(bImplements)
    True → Call I_Interactable::ExecuteInteraction(TargetActor, Self)
    False → Return / Log Warning

Framework Examples: BPC_InteractionDetectorI_Interactable, SS_SaveManagerI_Persistable, Weapons → I_Damageable

Key Rules:

  • Always check DoesImplementInterface before calling
  • Never cast to concrete class — use the interface
  • Default return values (false/0.0f/empty) handle missing implementations gracefully

Pattern 2: Event Dispatcher Binding (UI Display)

When to Use: Any system that needs to react to state changes in another system.

Blueprint Implementation:

[HUD Widget Blueprint — Event Construct]
  PlayerPawn = GetOwningPlayerPawn()
  HealthComponent = PlayerPawn.FindComponentByClass(BPC_HealthSystem)
  Bind Event to HealthComponent.OnHealthChanged
    → Custom Event: OnHealthChangedHandler(OldHealth, NewHealth, Delta)
    → ProgressBar.SetPercent(NewHealth / MaxHealth)

[HUD Widget Blueprint — Event Destruct]
  Unbind Event from HealthComponent.OnHealthChanged

Framework Examples: WBP_HUDController binds to OnHealthChanged, GS_CoreGameState binds to OnGamePhaseChanged

Key Rules:

  • Always unbind in Event Destruct to prevent dangling references
  • Never use Event Tick for polling — always bind to dispatchers
  • Normalize values (0.0-1.0) before sending to UI for smooth interpolation

Pattern 3: Subsystem Lookup (Global Service Resolution)

When to Use: Any system that needs a global service (save, audio, UI, input).

Blueprint Implementation:

[Any Blueprint]
  GameInstance = GetGameInstance()
  Cast to GI_GameFramework → Framework
  SubsystemRef = Framework.GetSubsystem(Tag)  // or FL_GameUtilities.GetSubsystemSafe()
  IsValid(SubsystemRef)?
    True → Call Subsystem function
    False → Log warning, return gracefully

Framework Examples: Any system calling SS_SaveManager.Save(), SS_AudioManager.PlaySound()

Key Rules:

  • Always use FL_GameUtilities.GetSubsystemSafe() — returns null instead of crashing
  • Check validity of the returned subsystem before calling functions
  • Subsystems are singletons — no need to cache, just look up when needed

Pattern 4: Data Asset Configuration

When to Use: Any system that needs configurable data (item stats, weapon balance, encounter rules).

Blueprint Implementation:

[Component Configuration]
  Variable: ItemData
  Type: DA_ItemData (Object Reference → Primary Data Asset)
  Instance Editable: ✓
  Expose on Spawn: ✓
  Category: "Config"

[Runtime Usage]
  ItemData.ItemTag        → GameplayTag
  ItemData.DisplayName    → Text for UI
  ItemData.Weight         → Float for carry capacity
  ItemData.StackLimit     → Integer for stack management

Framework Examples: All systems that reference DA_* Data Assets

Key Rules:

  • Data Assets are read-only at runtime — never modify them
  • Use Soft Object References for async loading large Data Asset collections
  • Register Data Assets with Primary Asset Manager for streaming support

Pattern 5: Timer-Based Ticks (Not Event Tick)

When to Use: Any repeating operation that doesn't need per-frame precision (regen, decay, scan).

Blueprint Implementation:

[BeginPlay]
  Set Timer by Event
    Event: RegenTick
    Time: 0.1 (seconds)
    Looping: ✓

[Custom Event: RegenTick]
  CurrentStamina = Min(MaxStamina, CurrentStamina + RegenRate * 0.1)
  Fire OnStaminaChanged
  If CurrentStamina >= MaxStamina:
    Clear Timer (RegenTimerHandle)

Framework Examples: BPC_HealthSystem regen, BPC_StaminaSystem drain, BPC_StressSystem decay, BPC_InteractionDetector scan

Key Rules:

  • Use 0.1s intervals for smooth but performant updates
  • Store timer handles and clear them on component destroy
  • Never use Event Tick unless absolutely necessary (physics, camera)

Pattern 6: State Machine with Enum Switch

When to Use: Any system that has distinct, mutually exclusive states.

Blueprint Implementation:

[Function: SetState(NewState)]
  OldState = CurrentState
  Switch on NewState:
    Case StateA:
      // StateA setup logic
    Case StateB:
      // StateB setup logic
    Case StateC:
      // StateC setup logic
  CurrentState = NewState
  Fire OnStateChanged(OldState, NewState)

Framework Examples: BP_DoorActor (Closed/Opening/Open/Closing/Locked/Barricaded), BP_WeaponBase (Holstered/Equipping/Ready/Firing/Reloading), BPC_AIStateMachine (Patrol/Search/Combat/Flee)

Key Rules:

  • Store previous state for transition detection
  • Fire dispatcher on every state change
  • Block operations that don't make sense in current state

Pattern 7: Validation Before Action

When to Use: Any function that modifies state and can fail.

Blueprint Implementation:

[Function: AddItem(ItemData, Quantity)]
  // Validate
  FreeSlot = FindFreeSlot()
  If FreeSlot < 0:
    Fire OnInventoryFull
    Return E_InventoryOperationResult::InventoryFull

  OverWeight = CheckWeight(ItemData.Weight * Quantity)
  If OverWeight:
    Return E_InventoryOperationResult::WeightCapacityExceeded

  // Execute
  SetSlot(FreeSlot, ItemData, Quantity)
  RecalculateWeight()
  Fire OnItemAdded
  Return E_InventoryOperationResult::Success

Framework Examples: BPC_InventorySystem.AddItem, BPC_StaminaSystem.DrainStamina, BPC_HidingSystem.EnterHideSpot

Key Rules:

  • Validate ALL conditions before making ANY changes
  • Return descriptive result codes, not just true/false
  • Fire error dispatchers with human-readable failure reasons

Pattern 8: RepNotify for Networked State

When to Use: Any replicated variable that needs local effects when changed remotely.

Blueprint Implementation:

[Variable]
  CurrentHealth: Float
  Replication: RepNotify
  OnRep Function: OnRep_CurrentHealth

[Function: OnRep_CurrentHealth]
  Fire OnHealthChanged (with cached old value)
  // This runs on clients when server changes the value
  // Local effects (UI update, effects) fire here

Framework Examples: All replicated variables in GS_CoreGameState, BPC_InventorySystem.Slots

Key Rules:

  • RepNotify fires on clients, NOT on server (server handles effects in the setter)
  • Cache old value before replication to use in OnRep
  • Single-player games can ignore replication entirely — framework is SP-first

Pattern 9: Tag-Driven Filtering

When to Use: Any time you need to filter or categorize (items, interactions, states, narrative).

Blueprint Implementation:

[Function: HasTag(Actor, Tag)]
  TagContainer = Actor.GetGameplayTagContainer()  // or via interface
  Return TagContainer.HasTag(Tag)

[Function: FindItemsByTag(Tag)]
  FilteredArray = []
  For Each Slot in Inventory.Slots:
    If Slot.Entry.ItemData.ItemTag.MatchesTag(Tag):
      Add to FilteredArray
  Return FilteredArray

Framework Examples: Every system — tags replace booleans and strings everywhere

Key Rules:

  • Framework tags use Framework. prefix; project tags use Game. prefix
  • All tags documented in DA_GameTagRegistry
  • Never use FName or FString for state — always GameplayTags

Pattern 10: Linked Actor Notifications

When to Use: When one actor's state change should trigger other actors (door opens → lights turn on).

Blueprint Implementation:

[Variable: LinkedActors]
  Type: Array of Actor
  Instance Editable: ✓
  Category: "Connections"

[On Door Opened]
  For Each LinkedActor in LinkedActors:
    bImplements = DoesImplementInterface(LinkedActor, I_Toggleable)
    If bImplements:
      Call I_Toggleable::SetState(LinkedActor, True)

Framework Examples: BP_DoorActor notifying lights/traps, BP_PuzzleDeviceActor unlocking connected doors

Key Rules:

  • Use interfaces (I_Toggleable, I_Adjustable) for linked actor communication
  • Designer sets LinkedActors in editor — no code changes needed
  • Order of linked actor notification may matter — test edge cases

Quick Reference: Which Pattern for What?

Need Pattern
Call a function on unknown actor type Pattern 1: Interface-First
React to another system's state change Pattern 2: Dispatcher Binding
Access global services (save, audio, UI) Pattern 3: Subsystem Lookup
Configure system behavior without code Pattern 4: Data Asset
Repeating operation at interval Pattern 5: Timer-Based Tick
Manage mutually exclusive states Pattern 6: State Machine
Operation that can fail Pattern 7: Validation Before Action
Network state synchronization Pattern 8: RepNotify
Filter/categorize by type Pattern 9: Tag-Driven Filtering
Chain reactions between actors Pattern 10: Linked Actor Notifications

Multiplayer Networking Patterns

Pattern 11: Server Authority Gate

When to Use: Every function that modifies replicated state.

Blueprint Implementation:

[Function: ApplyDamage(DamageEvent)]
  Switch HasAuthority
    Authority:
      → Execute authoritative logic (damage calc, health modification)
      → Fire dispatchers
    Remote:
      → Return (client cannot modify replicated state directly)
      → Client calls Server_ RPC to request the change

Key Rule: Never modify a replicated variable without HasAuthority() check.

Pattern 12: Server RPC Wrapper

When to Use: Client needs to request a state change from the server.

Blueprint Implementation:

[Client Event Graph — Input: Interact pressed]
  → Call Server_Interact(TargetActor)  // RPC: Run on Server, Reliable

[Server RPC: Server_Interact(TargetActor)]
  Switch HasAuthority
    Authority:
      → Validate distance, conditions
      → Call I_Interactable.Execute_OnInteract(TargetActor, Instigator)
      → State changes replicate automatically

Naming: All Server RPCs prefixed with Server_.

Pattern 13: OnRep Dispatcher Relay

When to Use: Any replicated variable that should trigger the same effects as single-player.

Blueprint Implementation:

[Variable: CurrentHealth]
  Replication: Replicated Using OnRep_CurrentHealth

[Function: OnRep_CurrentHealth]
  → Broadcast OnHealthChanged(OldHealth, CurrentHealth, Delta)
  → UI, audio, effects react exactly as in single-player path
  → No multiplayer-specific code in consumer systems

Key Rule: OnRep_ fires the SAME dispatcher the SP mutation code fires. Zero consumer changes needed.

Pattern 14: Client Prediction with Correction

When to Use: Actions that need instant feedback (firing, using items, interacting).

Blueprint Implementation:

[Client: Input → Fire pressed]
  → Client prediction: play fire animation, muzzle flash, reduce ammo display
  → Call Server_StartFire()

[Server: Server_StartFire()]
  → Validate weapon state, ammo, cooldown
  → If valid: execute authoritative fire logic, consume ammo
  → If invalid: log warning, return (client will be corrected via OnRep)

[Client: OnRep_Ammo]
  → If server count matches client prediction: no visible change
  → If different: correct ammo display, stop fire animation

Pattern 15: Multicast for Cosmetic Events

When to Use: Server needs to trigger a cosmetic-only event on all clients.

Blueprint Implementation:

[Server: Explosion triggered]
  → Multicast_PlayExplosionFX(Location, Radius)
  → All clients play VFX, SFX, camera shake locally
  → No replication of individual particles

Key Rule: Multicast is for cosmetic only — never use for state changes. State changes replicate via variable RepNotify.


Implementation Patterns v1.0 — Reference when building blueprint specs into UE5 Blueprint assets.