UE5 C++ Behavior Tree / Combat Slot AI keeps falling back to Patrol instead of Attack

Hi everyone,

I’m implementing a melee combat slot system entirely in UE5 C++ (no Blueprint logic except assigning assets), and I’ve reached a point where I’m stuck debugging the AI state transitions.

Current architecture

  • Unreal Engine 5
  • C++
  • AI Perception (Sight)
  • Behavior Tree + Blackboard
  • Custom BT Tasks
  • Custom BT Decorator
  • Custom AIController

I originally used a Vector ChaseTarget that was updated by a BT Service every 0.1s, but after many issues (MoveTo(Vector) constantly restarting and stale positions), I removed that system completely.

Now my setup is:

  • Each enemy claims a combat slot (Front, Back, Left, etc.) from a GameMode slot manager.
  • Every enemy spawns its own hidden SlotActor.
  • Every Tick, the SlotActor updates its position relative to the player’s location and facing direction.
  • The Behavior Tree uses MoveTo(SlotActor) instead of MoveTo(Player).

So there is no UpdateChaseTarget BT Service anymore.

Behavior Tree

Selector
|
|-- Attack
|     PlayerActor != nullptr
|     IsInAttackRange (custom decorator)
|     EnemyAttack
|
|-- Chase
|     PlayerActor != nullptr
|     MoveTo(SlotActor)
|
|-- Search
|     PlayerActor != nullptr
|     bCanSeePlayer == false
|     FindSearchLocation
|     MoveTo(SearchLocation)
|
|-- Patrol
      FindPatrolLocation
      MoveTo(PatrolLocation)

Attack decorator

Instead of checking distance to the player, I now check distance to the assigned SlotActor.

Distance = FVector::Dist(
    Enemy->GetActorLocation(),
    SlotActor->GetActorLocation());

return Distance <= AttackRange + SlotTolerance;

The decorator is set to:

  • bNotifyTick = true
  • Observer Aborts = Both

Current problem

The enemy correctly:

  • detects the player
  • claims the correct slot
  • moves behind / beside the player

However, once it reaches the slot, it usually does not attack.

Instead one of these happens:

  • It stands behind the player doing nothing.
  • After a few seconds it suddenly switches to the Patrol branch.
  • It walks to a random patrol location.
  • Then comes back to the slot.
  • Then repeats.

If I move the player, sometimes the enemy starts following again.

If I stand still, eventually Patrol takes over again.


Things I’ve already checked

  • PlayerActor remains valid.
  • SlotActor is valid.
  • SlotActor updates every Tick.
  • AI Perception detects the player correctly.
  • Attack decorator is evaluating every tick.
  • I removed the old Blackboard bool (bIsInAttackRange) and only use the custom decorator now.
  • There are no obstacles between the player and enemy.
  • Acceptable Radius on MoveTo(SlotActor) is 80.

What confuses me

If PlayerActor is still set, I would expect:

Attack
↓
Chase

to keep running.

Instead, the Selector eventually chooses:

Patrol

even though the player is standing directly in front of the enemy.


My questions

  1. Can MoveTo(SlotActor) be considered “finished” repeatedly because the SlotActor moves every Tick?
  2. Is using a continuously moving SlotActor as the MoveTo target a bad design?
  3. Would you recommend switching to an EQS-based solution (generate points around the player and reserve them) instead of updating a SlotActor every Tick?
  4. Is there something fundamentally wrong with my Behavior Tree flow that could cause Patrol to run while PlayerActor is still valid?

I’m happy to share code or a short video if that would help.