Loading
Personal Project
Charming Craft

CharmingCraft is a top-down action-adventure game built in Unreal Engine 5 with C++, drawing inspiration from Minecraft Dungeons and Albion Online. The project includes an action framework with item-bound skills, a data-driven buff pipeline, a Chain of Responsibility damage model, behavior tree AI with Environment Query System (EQS), modular building and farming, DataAsset-driven crafting, dynamic level streaming with persistent item drops, and a circular save slot manager.

Loford Town with houses and NPCs
The town of Loford, featuring player-built houses and roaming NPCs in the open world

Combat and ability framework

Item skill system

The item skill system adapts Albion Online’s design, where equipment items carry multiple skill options across nine dedicated slots: USE, SHIFT, SHIFT_INTERACT, RIGHT_CLICK, HOTBAR_CAST, INTERACT, PASSIVE, ON_HIT, and ON_ATTACK. Players choose which skill to bind from each item’s pool, allowing two identical weapons to function differently based on loadout choices.

Item skill reference from Albion Online
The Albion Online equipment layout that informed CharmingCraft’s skill binding design

The container class UItemDynamicSkill manages available skills on an item. PASSIVE slots apply persistent stat modifiers, ON_HIT triggers effects upon weapon collision (such as frost or poison), and ON_ATTACK triggers when an attack begins.

When a player selects an ability in the interface, the choice is saved to ItemMeta.BindItemDynamicSkill as a map of slot types to UNativeAction instances. At runtime, UCraftActionComponent reads these bindings and triggers them in response to player input or combat events.

Item skill selection UI
The in-game item skill UI showing available skills per slot, allowing players to customize their loadout
0:00
/0:00
Weapon skill selection demo showing the player browsing and binding different skills to equipment slots

Action and combat system

The combat framework uses a custom action model built around Unreal GameplayTags. UNativeAction serves as the base class, with UNativeItemAction adding stack requirements, UNativeEquipmentAction adding spawned equipment actor support, and subclasses like UNativeStandardMeleeAction and UNativeStandardRangedAction implementing specific weapon attacks.

graph TD
    subgraph Action Hierarchy
        NA[UNativeAction]
        NIA[UNativeItemAction]
        NEA[UNativeEquipmentAction]
        NSMA[UNativeStandardMeleeAction]
        NSRA[UNativeStandardRangedAction]
        NOHA[UNativeOnHitAction]
    end

    NA --> NIA
    NIA --> NEA
    NIA --> NOHA
    NEA --> NSMA
    NEA --> NSRA

    subgraph Action Lifecycle
        CS[CanStart] --> SA[StartAction]
        SA --> IR[IsRunning]
        IR --> STA[StopAction]
        STA --> CD[StartCoolDown]
        CD --> CF[CooldownFinished]
    end

Melee actions trace weapon arcs each frame through StartMeleeActionTrace() and MeleeActionTick(), applying damage on contact via OnActionHit(). Ranged actions spawn ABaseActionActor projectiles that support custom OnHitActions, OnSpawnActions, and OnTickActions, allowing projectiles to execute secondary effects like ground fire on impact.

Damage calculation follows the Chain of Responsibility pattern. FHitData packages physical, magic, and true damage alongside critical hit flags and OnHitBuffList. UDamageChain passes this payload through successive handlers: PhysicalDamageHandler calculates armor reduction, MagicDamageHandler evaluates magic defense, TrueDamageHandler passes unmitigated damage, and BuffApplicationHandler applies status effects. This structure allows adding new damage calculations without changing existing handlers.

Swords use a five-part modular assembly (ASwordEntityActorP5) that renders blade, fuller, guard, hilt, and pommel as separate mesh components, enabling visual customization through UMaterialInstanceDynamic.

Buff and attribute system

Designers define buffs through UBuffData assets and UBaseBuffModel Blueprint subclasses. Runtime state (remaining duration, stack counts, and instigator references) lives in UBuffInfo, managed by UBuffHandlerComponent.

Buff system data-driven configuration
Data-driven buff configuration in the editor, showing how designers author buff properties, stacking policies, and lifecycle models without C++

Stacking and timing rules are configured per buff: EBuffTimeUpdate controls whether reapplying refreshes duration, accumulates time, or maintains the current timer. EBuffRemoveStackUpdate chooses between clearing all stacks or decrementing individually. UModifyPropertyBuffModel adjusts FPlayerAttribute deltas directly to handle stat modifiers or damage over time.

Entity stats are tracked by UDAttributeComponent, covering health, mana, attack power, armor, resistances, critical chance, move speed, level, and experience. This component is shared between players and NPCs.

AI and creature behavior

Creatures use behavior trees and EQS queries. The skeleton enemy evaluates terrain to maintain ranged distance from the player and retreats to cast self-healing when health drops below a threshold. The Crimson Furbeak roams peacefully until attacked, chases its aggressor, and returns to roaming once the target escapes its chase radius.

0:00
/0:00
Skeleton enemy using EQS to maintain distance from the player, executing ranged attacks and self-healing behavior
0:00
/0:00
Crimson Furbeak neutral creature demonstrating roaming, aggro on hit, chase behavior, and return to roaming when losing aggro

Creatures inherit from ANativeCreature, which implements IDamageable, IMouseInteractInterface, and ILootableInterface. ANativeCreatureAIController initializes the assigned behavior tree on possession, with hit response montages mapped through EDamageResponse and tethering logic that returns creatures to their spawn origins.

0:00
/0:00
Entity drop items demo showing creatures dropping loot on death with animated item pickups featuring rotation and bobbing effects

Loot generation reads from UDropTableData assets specifying item types, drop chances, and quantities. Dropped loot spawns as ADropItem actors with bobbing animations.

Sandbox systems

Building system

The building system uses the Strategy pattern through UBaseBuildModel. UBuildModuleManager runs ray traces from the camera to the cursor, enforces 100-unit grid snapping, validates placement with FPlaceValidation, and updates an AFrameActor preview outline.

Equipping a tool activates its corresponding build model. UToolBuildModel manages structural tools like hoes and pickaxes, while USeedsBuildModel handles crop planting. Structures placed in the world persist across level streaming boundaries, and buildings can link to interior sub-levels via soft object references.

0:00
/0:00
Building system demo showing block placement with grid snapping, visual validation, and persistence across scene transitions

Farming system

Farming builds upon the placement pipeline. The hoe activates HoeBuildModel to display a tilling grid overlay, converting valid soil into farm plots. Equipping seeds switches to USeedsBuildModel, consuming inventory items and initiating crop growth managed by ABlockEntityActor.

0:00
/0:00
Farming system demo showing the full workflow of tilling land with a hoe, planting seeds, and watering crops

Crafting system

Crafting relies on UBaseRecipeEntry DataAssets that define recipe categories, ingredients, and outputs. At startup, URecipeRegistry indexes recipes into a container collection.

Crafting system UI
The crafting panel UI showing recipe categories, ingredient requirements, and output preview

UBaseCraftHandler verifies ingredient availability with OnCheckRecipeIngredientMatch, then broadcasts OnCraftProcessStart and OnCraftProcessFinish to drive UI progress bars and sound effects.

0:00
/0:00
Full crafting system demo showing recipe browsing, ingredient validation, and item creation

Resource gathering

Resource nodes integrate with Unreal Engine’s Chaos destruction framework. Mining a ore node or chopping a tree triggers physics fractures on the mesh before spawning drop items into the world.

Resource gathering with Chaos system
Player gathering gems with Unreal Engine’s Chaos destruction system, showing physics-based fracturing on resource break

World management and persistence

Scene transitions and streaming

The world system divides spaces into open regions, instanced interiors, and dungeons. UWorldManager coordinates streaming levels via ULevelStreamingDynamic, loading world maps lazily using TSoftObjectPtr<UWorld>.

Map selection UI
Map selection interface showing available regions with chunk-based world preview
0:00
/0:00
Map selection UI with animated chunk transitions and per-map descriptions for each region

Players travel between zones through ACraftWarpPoint actors. The world manager manages visibility toggles and active player assignments. Items dropped on the ground persist across world changes.

0:00
/0:00
Item persistence demo showing dropped items surviving across different scene transitions

Individual terrain chunks (ALandChunk) manage local streaming states (LOADED, PENDING_UNLOADED, UNLOADED) alongside biome definitions, resource pools, and spawn budgets.

Save system and slot management

UGameSaveManager stores save slots in a doubly linked list (TDoubleLinkedList<FSaveSlotInfo>) for circular navigation in the UI. Each entry includes slot metadata and containers for player inventory, world status, quest progress, and scene states.

Objects implementing ISerializable provide SerializeToJson() and DeserializeFromJson() implementations, allowing components to join the save pipeline independently.

0:00
/0:00
Main menu save system showing player save selection with circular navigation and new character creation

System architecture

Subsystems are owned by UCharmingCraftInstance. A central UGameEventHandler exposes multicast delegates across inventory, building, combat, and world events to minimize direct coupling between managers and actors.

graph TD
    subgraph Game Instance
        GI[UCharmingCraftInstance]
    end

    subgraph Core Managers
        SM[UGameSaveManager]
        WM[UWorldManager]
        BM[UBuildModuleManager]
        CM[UCameraManager]
        RR[URecipeRegistry]
        DH[UNativeDungeonHandler]
        PM[UPlayerModeManager]
    end

    subgraph Event Bus
        GEH[UGameEventHandler]
    end

    subgraph Entity Systems
        PC[ANativePlayerCharacter]
        NC[ANativeCreature]
        DI[ADropItem]
    end

    subgraph Shared Components
        AC[UCraftActionComponent]
        IC[UInventoryComponent]
        EC[UEquipmentComponent]
        DA[UDAttributeComponent]
        BC[UBuffHandlerComponent]
    end

    GI --> SM
    GI --> WM
    GI --> BM
    GI --> CM
    GI --> RR
    GI --> DH
    GI --> PM
    GI --> GEH

    GEH -.->|broadcasts| PC
    GEH -.->|broadcasts| NC
    GEH -.->|broadcasts| BM
    GEH -.->|broadcasts| WM

    PC --> AC
    PC --> IC
    PC --> EC
    PC --> DA
    PC --> BC

    NC --> AC
    NC --> IC
    NC --> DA
    NC --> BC

Design decisions

Data-driven configuration

Core features (buff behaviors, recipes, drop tables, item skills, and creature stats) are authored in DataAssets or Blueprint subclasses, keeping balance adjustments out of C++ code.

Event bus decoupling

UGameEventHandler routes notifications across world management, combat, and building. Systems broadcast and subscribe to events rather than keeping direct references to each other.

Strategy pattern for gameplay tools

Placing objects, resolving damage chains, and applying buffs all use polymorphic models (UBaseBuildModel, UDamageHandler, and UBaseBuffModel). New behaviors are introduced as subclasses without editing the dispatch loops.

Shared entity components

Players and creatures share the same attribute, buff, and action components, ensuring damage calculations and status effects work uniformly across all character types.

Interface-based serialization

The ISerializable interface standardizes JSON serialization across saveable actors, isolating file I/O from individual actor mechanics.