This prototype is a complete third-person Action RPG built in Unreal Engine 5.6, implemented primarily in C++ with Blueprint subclassing for asset configuration. The project brings together several systems I had previously studied independently — Gameplay Ability System, melee combat, enemy AI, Common UI, Enhanced Input, Motion Warping, projectiles and gameplay tags — into a single cohesive combat framework.
The gameplay loop includes melee combos, directional rolling, blocking and parrying, target locking, ranged enemies, a boss encounter with summoning abilities, and a survival mode built around enemy waves. The main goal was not simply to reproduce individual mechanics, but to understand how these systems fit together architecturally in a larger C++ gameplay project.
The project was built following the Unreal Engine 5 C++: Advanced Action RPG course by Vince Petrelli as a structural foundation, with my own C++ implementation, architectural decisions, debugging and corrections throughout.
Character Architecture
All gameplay characters derive from AWarriorBaseCharacter, which implements IAbilitySystemInterface together with the project’s combat and UI interfaces. The base character owns the shared UWarriorAbilitySystemComponent, UWarriorAttributeSet, a UMotionWarpingComponent, and soft-referenced startup data used to initialize abilities and attributes.
The hierarchy then splits into AWarriorHeroCharacter and AWarriorEnemyCharacter. The hero adds the camera system, input configuration, hero-specific combat component and UI component. Enemies add their own combat and UI components, world-space health widget, hand collision boxes and AI possession configuration.
This keeps the common GAS and character lifecycle code centralized while allowing hero and enemy behavior to diverge cleanly without filling a single base class with conditional logic.
Gameplay Ability System
UWarriorAbilitySystemComponent extends the engine ASC with a tag-driven input model. Abilities can behave as normal press-to-activate actions, toggleable abilities, or abilities that remain active only while the input is held. This lets the same routing layer support attacks, target lock, blocking and other gameplay actions without adding dedicated controller functions for every ability.
Weapon abilities are granted and removed dynamically through GrantHeroWeaponAbilities and RemoveGrantedHeroWeaponAbilities, keeping the active ability set synchronized with the equipped weapon. Ability lookup and activation are driven by Gameplay Tags rather than hard references between unrelated gameplay systems.
UWarriorAttributeSet manages health, rage, attack power, defense power and damage. PostGameplayEffectExecute clamps attributes, forwards changes to the UI layer through the pawn UI interface, and applies the shared death state tag when health reaches zero.
Native Gameplay Tags
The project uses Native Gameplay Tags as the common language between input, abilities, combat states, AI, UI and gameplay events. Tags are declared centrally in WarriorRPGTags.h using C++ namespaces that mirror the tag hierarchy.
Examples include Input::Attack::Light::Axe, Player::Status::TargetLocking, Enemy::Status::Unblockable, Shared::Event::SpawnProjectile and UI::WidgetStack::Hud. The result is a project where systems communicate through explicit gameplay state and events instead of direct dependencies wherever possible.
Combat Component Architecture
Weapon and hit logic is isolated from the Character classes through a component hierarchy built around UPawnCombatComponent. The base component owns the weapon registry and collision toggling, while UHeroCombatComponent and UEnemyCombatComponent implement the hero- and enemy-specific responses to successful hits.
The hero combat component resolves the equipped weapon and its damage at the current level, sends melee-hit gameplay events and applies hit pause. The enemy combat component evaluates block state, block geometry and unblockable attacks, and also supports body collision boxes for attacks performed with hands rather than weapons.
This component split keeps collision detection and weapon ownership separate from the abilities that orchestrate animations and gameplay flow.
Target Lock System
UHeroGameplayAbility_TargetLock implements target locking as a toggleable Gameplay Ability. Candidate enemies are collected through a box trace and the initial target is selected by distance. While active, the ability controls camera and locomotion orientation and displays a screen-space target widget over the locked enemy.
A custom UAbilityTask_ExecuteTaskOnTick provides the per-frame update required by the lock system without moving the behavior outside GAS. Target switching classifies candidates to the left or right using the Z component of the cross product relative to the current target.
The system also coordinates with other gameplay states: rotation interpolation is suppressed while the character is rolling or blocking, and a dedicated Enhanced Input mapping context temporarily overrides the normal look bindings during the lock session.
Block, Parry and Unblockable Attacks
Blocking is not treated as a simple boolean. Incoming enemy hits are evaluated in two stages: first the defender must have the Player::Status::Blocking tag, and then UWarriorFunctionLibrary::IsValidBlock verifies that the defender is actually facing the attacker using a dot-product test.
This prevents attacks from behind from being blocked simply because the block input is held. Attacks tagged with Enemy::Status::Unblockable bypass the block evaluation entirely, providing a clean data-driven mechanism for heavy attacks, boss finishers and other moves that must force a reaction.
Hit reaction direction is also centralized in the function library. Dot and cross products are used to classify impacts as front, left, right or back so abilities and animation montages can react consistently to the same combat event.
Directional Rolling and Motion Warping
Directional rolling is implemented as part of the same tag-driven ability architecture used by the rest of the combat system. Rolling becomes an explicit gameplay state that other systems can query — target lock, for example, temporarily stops driving character rotation while the roll is active.
AWarriorBaseCharacter owns a UMotionWarpingComponent, allowing combat abilities and animation montages to use Unreal’s Motion Warping system when movement or alignment has to be adjusted to gameplay targets rather than relying on fixed root motion alone.
Projectile and Ranged Combat
Ranged attacks use AWarriorProjectileBase, composed of a box collision root, Niagara component and UProjectileMovementComponent. The projectile receives a FGameplayEffectSpecHandle at spawn time, so the damage specification is created by the ability that owns the attack and travels with the projectile until impact.
The projectile spawn itself is synchronized to animation through the Shared::Event::SpawnProjectile gameplay event. The ability waits for that event and creates the projectile at the exact frame selected in the attack animation rather than relying on arbitrary timers.
On impact, block state is evaluated before the Gameplay Effect is applied. This means melee and ranged damage participate in the same shared defensive rules instead of implementing separate combat pipelines.
Enemy AI
Enemies are controlled by AWarriorAIController, which uses UAIPerceptionComponent with sight sense to detect the player and write the current target into the Blackboard. Behavior is built from custom C++ Behavior Tree tasks, decorators and services.
UCrowdFollowingComponent provides detour crowd avoidance so groups of enemies can navigate around each other, while the Environment Query System is used to find valid strafing positions around the player. Gameplay Tags such as Enemy::Status::Strafing and Enemy::Status::UnderAttack feed directly into Behavior Tree decisions.
Faction checks are handled through IGenericTeamAgentInterface rather than simple actor comparisons, giving the combat and AI systems a reusable definition of hostility.
Boss Summoning and Custom Ability Tasks
The boss encounter includes enemy summoning through the custom UAbilityTask_WaitSpawnEnemies. The task waits for a Gameplay Event, then asynchronously loads and spawns a configurable number of enemies at random NavMesh-reachable positions around an origin.
Asset loading is performed asynchronously through UAssetManager. Individual failed spawn locations are skipped rather than aborting the entire summon sequence, and the task exposes a failure delegate if no valid enemy could be created. Each task instance consumes only its first trigger event.
Spawning is server-authoritative, which keeps enemy creation consistent with the networking rules used by the rest of the combat framework.
Common UI Architecture
The UI is built around UWarriorUISubsystem and a persistent UWarriorPrimaryLayout. The primary layout exposes separate Common UI containers for HUD, modal screens, background content and toast layers, allowing screens to be pushed into explicit UI layers instead of being added directly to the viewport from arbitrary gameplay code.
UWarriorActivatableWidget defines the input mode required by each screen, while UWarriorUISettings stores the mapping between Gameplay Tags and widget classes. Widgets can therefore be resolved and opened by tag rather than hard-coded class references.
A custom UWarriorActionRouter prevents Common UI from applying an unwanted Menu input configuration in situations where gameplay control must remain active. This was one of the areas where integrating Common UI into a gameplay-heavy project required more than simply pushing widgets to a stack.
Reactive HUD and Enemy UI
The UI component hierarchy mirrors the combat hierarchy. UPawnUIComponent exposes shared health changes, UHeroUIComponent adds rage and equipped-weapon updates, and UEnemyUIComponent manages widgets associated with individual enemies.
Attribute changes are pushed from the GAS layer to UI delegates through interfaces rather than polled every frame. This keeps HUD updates reactive and prevents widgets from needing to know where the underlying AttributeSet is stored.
Survival Mode and Enemy Waves
The completed project also includes a survival game mode built around repeated enemy waves. This provides a useful integration test for the entire architecture: spawning, AI acquisition, navigation, combat abilities, damage, UI state and enemy cleanup all have to continue functioning as the number of active combatants changes over time.
For me, this was the most important part of the prototype. Individual systems can appear correct in isolation; repeated combat encounters expose lifecycle, state and ownership problems much more quickly.
Tech Stack
- Unreal Engine 5.6
- C++ with Blueprint subclassing for asset configuration
- Gameplay Ability System (GAS)
- Enhanced Input
- Common UI and Common Input
- Motion Warping
- Behavior Trees, Blackboard, AI Perception and Environment Query System
- Niagara
- Electronic Nodes and Blueprint Assist for Blueprint graph organization
Learning Source
Built following Unreal Engine 5 C++: Advanced Action RPG by Vince Petrelli on Udemy.