Loading
Group Project
Kila: Hourbound

Kila: Hourbound is a third-person action-adventure platformer built in Unreal Engine 5.6 for PC and Steam Deck. Set in the world of Aion, the game features melee combat, platforming, and a time-manipulation mechanic for puzzle solving and combat abilities. For this team project (C34 Teal Group), I built the gameplay systems and UI architecture, spanning four custom C++ modules and a global event subsystem.

Gameplay trailer

Kila: Hourbound Official Gameplay Trailer

Core contributions

I implemented four C++ modules and one global subsystem to support design and animation workflows in Blueprint:

  • GameCommonModule: Shared foundation defining attribute structs, damage payload contracts, ability haste interfaces, and scaling HUD base widgets.
  • BuffModule: Data-driven status effect system supporting custom stacking policies, duration timers, and reactive UI containers.
  • CoreAttributeModule: Actor attribute component with modifier pipelines, damage resolution, and health bar widgets for characters and bosses.
  • ActionModule: Gameplay Ability System built on GameplayTags with action charges, cooldown reductions, and hierarchical ability entities.
  • EventSubsystem: GameInstance subsystem providing dynamic multicast delegates for decoupled communication across systems.

Module architecture

The four modules form a layered dependency structure. GameCommonModule provides shared data types, BuffModule and CoreAttributeModule build on that foundation, and ActionModule integrates all three. The EventSubsystem routes cross-system notifications.

graph TD
    subgraph GameCommonModule["GameCommonModule (Foundation)"]
        AD[FAttributeData]
        DR[FDamageRequest / FDamageResult]
        AHI[IAbilityHasteListener]
        HWB[UHudWidgetBase]
        WTT[UWidgetToolTip]
        GC[UGameCommon Utils]
    end

    subgraph BuffModule["BuffModule (Buff/Debuff)"]
        BD[UBuffDefinition]
        BI[UBuffInstance]
        BM[UBuffModel]
        BC[UBuffComponent]
        BWC[UWidgetBuffContainer]
    end

    subgraph CoreAttributeModule["CoreAttributeModule (RPG Attributes)"]
        CAC[UCoreAttributeComponent]
        DAM[IDamageable]
        PHB[UWidgetPlayerHealthBar]
        BHB[UWidgetBossHealthBar]
    end

    subgraph ActionModule["ActionModule (GAS / Abilities)"]
        UA[UAction]
        UAC[UActionComponent]
        AE[AAbilityEntity]
    end

    subgraph EventSubsystem["EventSubsystem (Global Event Bus)"]
        ES[UEventSubsystem]
    end

    GameCommonModule --> BuffModule
    GameCommonModule --> CoreAttributeModule
    BuffModule --> CoreAttributeModule
    GameCommonModule --> ActionModule
    CoreAttributeModule --> ActionModule
    ES -.->|broadcasts| ActionModule
    ES -.->|broadcasts| CoreAttributeModule
    ES -.->|broadcasts| BuffModule

GameCommonModule foundation layer

This module defines common data structures, interfaces, and base widget classes used by other modules, containing definitions rather than gameplay rules.

Attribute and damage data types

RPG attributes are declared in EAttributeType: Health, MaxHealth, MovementSpeed, Defense, AttackDamage, ResistPhysic, DamageReductionPhysic, ResistMagic, DamageReductionMagical, and AbilityHaste. The struct FAttributeData bundles these values and implements operator overloads (+, -, +=, -=) for clean attribute arithmetic.

Damage calculations use two primary structs:

  • FDamageRequest packages BaseDamage, EDamageType (Physical, Magical, TrueDamage, DirectHeal), critical hit chances, multipliers, and block flags.
  • FDamageResult returns FinalDamage, before and after health values, combat flags, and attribute snapshots for VFX and UI reactions.

Modifiers use FAttributeModifier with four calculation modes: FlatAdd, PercentAdd, PercentMult, and Override, each storing a priority and source pointer for targeted removal.

Ability haste interface

IAbilityHasteListener and FAbilityHasteContext define how components listen to ability haste changes. Cooldown reduction follows the formula CDR = Haste / (Haste + 100). Per-action haste entries (FPerActionHasteModifier) allow buffs to target specific action categories, such as dashes.

HUD widget templates

UHudWidgetBase monitors viewport size every 0.1 seconds and scales child elements against a 1920x1080 baseline. UWidgetToolTip calculates dynamic height, wraps text lines, and stabilizes positioning to prevent edge-of-screen flicker.

BuffModule buff and debuff system

BuffModule provides a data-driven buff framework where behaviors are configured through UBuffDefinition assets and UBuffModel Blueprint subclasses.

Class diagram

classDiagram
    class UBuffDefinition {
        +int32 Id
        +FName BuffName
        +UTexture2D* Icon
        +int32 MaxStack
        +EBuffType BuffType
        +bool bIsForever
        +float Duration
        +float TickTime
        +EBuffTimeUpdate BuffTimeUpdate
        +EBuffRemoveStackUpdate BuffRemoveStackUpdate
        +TSubclassOf~UBuffModel~ OnCreate
        +TSubclassOf~UBuffModel~ OnRemove
        +TSubclassOf~UBuffModel~ OnTick
    }

    class UBuffInstance {
        +UBuffDefinition* BuffDefinition
        +AActor* Instigator
        +AActor* Target
        +float Duration
        +int32 CurrentStack
        +GetRemainingTime() float
        +GetRemainingPercentage() float
        +GetTimeStatus() EBuffTimeStatus
        +MarkAsRemoved()
    }

    class UBuffModel {
        +UBuffInstance* ParentBuffInstance
        +Apply()
        +OnCleanup()
    }

    class UBuffComponent {
        +TArray~UBuffInstance*~ BuffList
        +AddBuff(UBuffInstance*)
        +RemoveBuff(UBuffInstance*)
        +TickBuffAndRemove(float DeltaTime)
        +GetBuffsByTimeStatus()
        +GetBuffUIDisplayData()
    }

    class UWidgetBuffContainer {
        +TArray~EBuffType~ BuffTypeFilters
        +InitializeWithBuffComponent()
        +RefreshBuffDisplay()
    }

    class UWidgetBuffElement {
        +UBuffInstance* BuffInstance
        +UpdateDisplayData()
    }

    UBuffDefinition --> UBuffInstance : instantiates
    UBuffInstance --> UBuffModel : lifecycle callbacks
    UBuffComponent --> UBuffInstance : manages
    UWidgetBuffContainer --> UWidgetBuffElement : creates per buff
    UWidgetBuffElement --> UBuffInstance : displays
    UWidgetBuffContainer --> UBuffComponent : observes

Core architecture

UBuffDefinition specifies buff assets: icon, maximum stacks, base duration, periodic tick intervals, classification (Positive, Negative, Neutral), and model references for OnCreate, OnRemove, and OnTick hooks. UBuffModel exposes BlueprintNativeEvent methods for visual and mechanical scripting.

Buff Definition Blueprint
A UBuffDefinition data asset configured in the Unreal Editor, showing all exposed properties for designer authoring

At runtime, UBuffComponent manages an array of active UBuffInstance objects, tracking remaining durations, stack counts, and instigator references.

Stacking and time policies

Reapplying an existing buff resolves according to two policies:

  • EBuffTimeUpdate: Add adds incoming duration to the current timer, Replace resets to maximum duration, and Keep maintains the active timer.
  • EBuffRemoveStackUpdate: Clear removes all stacks simultaneously, while Reduce decrements stacks one at a time.

Buff lifecycle flow

flowchart TD
    A[AddBuffFromBuffDefinition] --> B{Buff already exists?}
    B -->|No| C[Create UBuffInstance]
    C --> D[Add to BuffList, sort by Priority]
    D --> F[Execute OnCreate BuffModel]
    F --> G[Broadcast BuffAddToBuffList]

    B -->|Yes| H{Stack < MaxStack?}
    H -->|Yes| I[Increment stack, update duration per policy]
    I --> K[Broadcast BuffAddToBuffListRepeat]
    H -->|No| L[Apply time update only]
    L --> K

    G --> M[TickComponent each frame]
    K --> M
    M --> N{TickTime elapsed?}
    N -->|Yes| P[Execute OnTick BuffModel, reset timer]
    N -->|No| R[Decrement Duration]
    P --> R
    R --> S{Expired or marked removed?}
    S -->|Yes| T[Execute OnRemove BuffModel]
    T --> U{RemoveStackUpdate policy}
    U -->|Clear| V[Remove entirely, destroy instance]
    U -->|Reduce| W{Stack <= 0?}
    W -->|Yes| V
    W -->|No| Y[Reset Duration, continue]
    S -->|No| M

Time tracking API

UBuffInstance exposes remaining time calculations through GetRemainingTime(), GetRemainingPercentage(), and status categories (Permanent, Active, AboutToExpire, Expired). UBuffComponent aggregates display payloads via GetBuffUIDisplayData() for HUD elements.

UI widget hierarchy

The buff HUD uses three layers: UWidgetBuffHudCanvas coordinates multiple UWidgetBuffContainer panels (filtered by positive or negative buff types), which instantiate UWidgetBuffElement widgets for individual buffs. Elements update stack counts and duration text, opening UWidgetBuffTip on hover.

Buff Creation Blueprint Components
Blueprint components for buff creation. Designers wire up UBuffDefinition assets and UBuffModel subclasses without C++

Debug console

UBuffConsoleCommands provides console triggers during development: AddBuff, RemoveBuff, ListBuffs, ClearAllBuffs, and ShowBuffInfo.

0:00
/0:00
Buff HUD and system demo. Applying, stacking, expiring, and removing buffs at runtime with live UI updates

CoreAttributeModule attribute system

This module tracks attribute state, resolves incoming damage and healing requests, and coordinates combat HUD elements.

Attribute component

UCoreAttributeComponent attaches to characters and monsters, storing:

  • AttributeDataBase: baseline editor-configured stats.
  • AttributeModifiers: active modifiers from buffs, equipment, or passives.
  • CachedFinalAttributes: recalculated attribute sums.

Modifiers evaluate in a fixed order: Flat additions first, then additive percentages, multiplicative percentages, and direct overrides.

Damage pipeline

flowchart LR
    A[FDamageRequest] --> B[ProcessDamageRequest]
    B --> C[Snapshot attributes before]
    B --> D{DamageType?}
    D -->|DirectHeal| E[ApplyHealing]
    D -->|Physical| F[Apply ResistPhysic + DamageReductionPhysic]
    D -->|Magical| G[Apply ResistMagic + DamageReductionMagical]
    D -->|TrueDamage| H[No reduction]
    F --> I{bCanBeCrit?}
    G --> I
    H --> I
    I -->|Yes| J[Roll CritChance]
    J -->|Crit| K[Apply CritMultiplier]
    J -->|Miss| L[Base damage]
    I -->|No| L
    K --> M[Apply to Health]
    L --> M
    M --> P[Snapshot attributes after]
    P --> Q{Health <= 0?}
    Q -->|Yes| R[KillingBlow = true]
    Q -->|No| S[KillingBlow = false]
    R --> T[FDamageResult]
    S --> T
    E --> T

The component broadcasts delegates for combat events: DamageProcessedEvent, HealthChangedEvent, CharacterDeathEvent, and OnAttributeModifierChanged.

Floating Damage Number Niagara
Floating damage numbers rendered via Niagara particle system, driven by DamageProcessedEvent delegates from the damage pipeline
0:00
/0:00
Floating damage number demo. Physical, magical, true damage, and healing with crit indicators

Damageable interface

Actors that take damage implement IDamageable, exposing AppendAttributeData() so damage processors can inspect incoming targets uniformly.

Health bar widgets

The module provides two health bar widgets:

  • UWidgetPlayerHealthBar: Binds to the player attribute component, displaying shield absorption, current health, and status indicators.
  • UWidgetBossHealthBar: Displays boss names, phase titles, portraits, and low-health indicators when reaching critical stages.
0:00
/0:00
Skill HUD and health bar system in action. Player health bar, boss health bar, and shield display

Debug tooling

UAttributeDebugSubsystem identifies actors with attribute components in the world, toggling an in-world debug overlay showing live health, resistance modifiers, and active stat alterations.

0:00
/0:00
Attribute debugger overlay. Live attribute values, modifier stacks, and health status rendered in world space

ActionModule ability system

ActionModule implements an ability system built around Unreal GameplayTags, using UAction for ability definitions, UActionComponent for runtime execution, and AAbilityEntity for spawned effects.

Class diagram

classDiagram
    class UAction {
        +FName ActionName
        +float CoolDown
        +int ActionLevel
        +int ActionCharge
        +FGameplayTagContainer GrantsTags
        +FGameplayTagContainer BlockedTags
        +FGameplayTagContainer ActionFeatureTags
        +StartAction(AActor*)
        +StopAction(AActor*)
        +CanActionStart(AActor*) bool
        +GetFinalCooldown() float
        +GetTotalAbilityHaste() float
        +AddPerActionHasteModifier(float, UObject*)
    }

    class UActionComponent {
        +TArray~UAction*~ Actions
        +FGameplayTagContainer ActiveGamePlayTags
        +float CachedAbilityHaste
        +AddAction(TSubclassOf~UAction~)
        +RemoveAction(TSubclassOf~UAction~)
        +StartActionByName(AActor*, FName)
        +ReduceCooldownByTag(FGameplayTag, float, bool)
    }

    class AAbilityEntity {
        +FName EntityName
        +float MaxLifetime
        +FDamageRequest BaseDamageRequest
        +float DamageScalingPerLevel
        +AAbilityEntity* ParentEntity
        +TArray~AAbilityEntity*~ ChildEntities
        +InitializeEntity(AActor*, UAction*)
        +SpawnChildEntity(TSubclassOf~AAbilityEntity~, FTransform, bool)
        +DealDamageToTarget(AActor*)
    }

    class IGameplayTagAssetInterface {
        <<interface>>
        +GetOwnedGameplayTags()
    }

    class IAbilityHasteListener {
        <<interface>>
        +OnAbilityHasteChanged()
        +GetCachedAbilityHaste() float
    }

    UActionComponent --> UAction : manages
    UActionComponent ..|> IGameplayTagAssetInterface
    UActionComponent ..|> IAbilityHasteListener
    UAction --> AAbilityEntity : spawns
    AAbilityEntity --> AAbilityEntity : parent-child hierarchy

Action ability definitions

Key properties on UAction include:

  • Gameplay tags: GrantsTags apply to the owner during execution, BlockedTags prevent activation when present, and ActionFeatureTags categorize the skill.
  • Cooldown and haste: Cooldowns combine global ability haste with per-action bonuses: FinalCooldown = BaseCooldown * (1 - CDR).
  • Stored charges: Abilities configure multiple charges before cooldown kicks in.
  • Level progression: ActionLevel supports skill progression trees; level 0 abilities cannot be cast.
Action Blueprint
A UAction subclass configured in Blueprint. Designers set up tags, cooldowns, charges, and level scaling without C++

Action component manager

UActionComponent manages abilities on an actor, implementing IGameplayTagAssetInterface for tag queries and IAbilityHasteListener for stat synchronizations. It handles tag-based cooldown reductions, tag-filtered haste adjustments, and ability lifecycle events.

Skill Widget UAssets
Skill widget UAssets in the content browser. Reusable UI templates for ability cooldown display and charge indicators
Skill Component Widget Blueprint
Skill component widget Blueprint. Wiring UActionComponent data into the HUD for real-time cooldown and charge visualization

Spawned ability entities

AAbilityEntity represents actors spawned by abilities (projectiles, ground markers, persistent zones). Features include:

  • Hierarchical child spawning with depth limits.
  • Source tracking linking entities back to their instigator and parent action.
  • Damage request templates scaled automatically by ability level.
  • Overridable lifecycle and collision hooks (OnEntitySpawned, OnEntityBeginOverlap, OnEntityHit).
0:00
/0:00
Action system demo. Ability cooldowns, charge stacking, and hierarchical entity spawning with skill debugger

EventSubsystem global event bus

UEventSubsystem centralizes global delegates across the project:

// From any UObject with a world context
EventSystem->OnCountdownTick.AddDynamic(this, &AMyClass::HandleTick);

// Explicit world context version
GetEvent(GetWorld())->CountDownFinished.Broadcast();

Event categories

  • Time and world pacing: Delegates manage dilation transitions (OnGlobalTimeDilationChanged), countdown clocks, and targeted time slow mechanics on specific actors.
  • Character states: GlobalCharacterDeathEvent alerts encounter logic, and combo delegates track melee animation attack chains.
  • UI triggers: RequestWidgetVisibilityChange lets gameplay actors request HUD updates without holding direct widget pointers.
  • Level flow: LevelOpenUp signals when scene transition barriers are clear.

Design decisions

Blueprint extensibility

Methods are exposed as BlueprintNativeEvent or BlueprintCallable, allowing designers to author new abilities, buffs, and damage triggers in Blueprint without recompiling C++ source.

GameplayTag classification

GameplayTags replace hardcoded enums for action gating and batch modifications, letting designers group abilities into families like dashes or spells dynamically.

Modifier source tracking

Attribute modifiers and status effects retain references to their origin actor or component, ensuring clean removal when buffs expire.

Delegate-driven decoupling

Systems exchange data through dynamic multicast delegates. Combat processors broadcast damage results, while health bars and Niagara damage counters listen independently.

Built-in console tooling

Buff and attribute components include console command handlers and world-space debug overlays to inspect stats, modifier stacks, and cooldowns during testing.