U-Tad UI Project
English | Leer en Español
Skill Tree System Implementation
Overview
A complete skill tree system with node-based progression, prerequisite validation, visual connection lines, and data-driven configuration.
Architecture: Blueprint Decision
Blueprint Implementation
Components: WBP_SkillTree, WBP_SkillNode, WBP_SkillNodeTooltip
Reasoning:
- Iteration speed: UI layout and styling require rapid visual iteration
- Designer-friendly: Artists/designers can modify UI without C++ knowledge
Core Systems
1. Data Layer (SkillTreeTypes.h)
Structures:
FSkillNodeData: Skill definition row for skill data table (name, cost, prerequisites, effects, starting flag)FSkillNodeRuntime: Runtime state wrapperESkillNodeState: State enum (Locked, Available, Selected, Purchased)
Decision: Data Table-driven design allows non-programmers to configure skills without recompiling.
2. Logic Layer (SkillTreeComponent)
Responsibilities:
- Skill tree initialization from DataTable
- State management (prerequisites, affordability checks)
- Selection validation and purchase confirmation
- Starting skills auto-purchase
- Skill point management
- Effect application hooks
Key Features:
- Purchase order sorting: Ensures prerequisites are purchased before dependents
- Cascade deselection: Removing a prerequisite deselects dependent skills
- Starting skills: Marked in DataTable, auto-purchased at init, preserved on reset
- Stat aggregation: Provides
GetTotal[Stat]Bonus()functions to sum all purchased skills' bonuses
3. Stats Integration Layer (CharacterStatsComponent)
Responsibilities:
- Automatic stat calculation from purchased skills
- Character stat management (Health, Damage, Speed)
- Real-time updates when skills are purchased
- Movement speed integration
How it works:
- Finds SkillTreeComponent on same actor at BeginPlay
- Listens to
OnSkillPurchasedevent - Iterates all purchased skills, sums flat + percentage bonuses
- Applies formula:
FinalStat = Base + FlatBonus + (Base * PercentBonus / 100) - Auto-updates CharacterMovementComponent speed
- Broadcasts stat change events for UI updates
Decision: Separate stats component keeps concerns separated - SkillTreeComponent handles progression, CharacterStatsComponent handles stat application. Makes both reusable independently.
4. Visualization Layer (SkillTreeConnectionsWidget)
Responsibilities:
- Automatic line drawing between prerequisite nodes (You only need to place nodes in the UI!)
- Dynamic line coloring based on skill states
- Event-driven refresh on state changes
Technical Approach:
- Generic search: Works with any widget hierarchy so designers/artist are not limited when creating the skill tree widget (Canvas, Overlay, Grid)
- NativePaint override: Direct Slate rendering for performance
- Delegate binding: Auto-refreshes when skills change state
Decision: Single self-contained widget eliminates manual line management and Blueprint complexity.
5. UI Layer (Blueprints)
WBP_SkillNode & WBP_SkillNodeTooltip:
- Displays skill icon, state, cost
- Handles click events (select/deselect)
- Stores SkillID for runtime lookup
- Custom tooltip support to display skill details
- Real time state updates via event binding allows real-time visual feedback
WBP_SkillTree:
- Contains manually-placed skill nodes
- References SkillTreeComponent
- Hosts SkillTreeConnectionsWidget which draws lines between nodes automatically
- Binds to component events for UI updates
Decision: Manual node placement allows custom layouts (linear, radial, branching) per designer preference.
Key Design Decisions
1. Skill prerequisites in DataTable
Alternative: Hard-coded in C++ or DataAssets references Chosen: DataTable array Reason: Designers can modify skill dependencies without programmer intervention and without recompiling
2. Component vs Subsystem
Alternative: Game Instance Subsystem Chosen: Actor Component Reason: Supports multiple skill trees per player (e.g., combat tree, crafting tree) and easier save/load integration
3. Single Connections Widget
Alternative: Individual line widgets per connection with manual management Chosen: One widget draws all lines automatically Reason: Eliminates array management, better performance, automatic coordinate handling
4. Separate Stats Component
Alternative: Apply stats directly in SkillTreeComponent Chosen: Separate CharacterStatsComponent Reason: Separation of concerns - SkillTreeComponent is reusable for any progression system, CharacterStatsComponent specifically handles character stats. Allows skill trees without stat bonuses, or stats without skill trees.
Technical Highlights
- Event-driven architecture: Component broadcasts state changes, UI responds automatically
- State machine validation: Prevents invalid transitions (e.g., can't select locked skills)
- Automatic stat application: CharacterStatsComponent listens to skill purchases, recalculates stats, applies to character
- Geometry-aware rendering: Lines adapt to any widget positioning system
- Data-driven configuration: Zero code changes and no need to compile for new skills or tree layouts
- Reset functionality: Refunds non-starting skills, maintains starting points
- Modular design: Skill progression, stats, and visualization are independent




