# 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 `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 `GI_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.*