- 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.
9.4 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_InteractionDetector → I_Interactable, SS_SaveManager → I_Persistable, Weapons → I_Damageable
Key Rules:
- Always check
DoesImplementInterfacebefore 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 useGame.prefix - All tags documented in
GI_GameTagRegistry - Never use
FNameorFStringfor 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 |
Implementation Patterns v1.0 — Reference when building blueprint specs into UE5 Blueprint assets.