← All Game Design Projects
Zero Protocol enemy AI runtime loop diagram: sense, select target, choose state, move and act, and feedback stages, supported by memory and tuning, encounter context, the Combat Director and reaction failsafes

Designing Combat AI That Stays Readable Under Pressure

Faction-aware targeting, shared state-machine families and a global attack budget give enemies distinct behaviour without letting group combat collapse into chaos.

Role Technical Designer
Project Zero Protocol
Studio Northline Works
Discipline AI / Systems
Design Goal

Give a roster of distinct enemy classes intentional, readable behaviour in group combat - systemic targeting, shared state-machine families and an authored attack budget, built as reusable components in Godot.

Working in Unreal taught me to think about AI as a set of separate responsibilities. State machines make an actor's current mode explicit. Behavior Trees route decisions according to conditions in the world. Tasks perform individual actions and own their completion.

When I built the AI for Zero Protocol - an isometric action-adventure thriller in production at Northline Works, built in Godot - I didn't try to recreate Unreal's tools node for node. I carried that separation of responsibility into a component-backed hierarchical state-machine system.

More than chase and attack

I wanted enemies to exist in the world rather than remain dormant until the player entered their detection radius. They can patrol or wander, notice nearby allies being attacked, investigate noise, search the last place they saw a target and eventually return to their previous activity.

The world also contains civilians and neutral animals. Enemies can attack them, the player can intervene to rescue them, and different factions can become involved in the same fight. This meant the AI couldn't just assume that the player was every actor's only possible target.

At the same time, the enemy roster needed to create substantially different combat problems. Ghouls are mindless melee pressure. Ranged casters maintain distance. Summoners create additional threats. Grunts combine ranged and melee attacks depending on the space between themselves and their target.

The system needed to support those differences without building an entirely separate AI architecture for every enemy.

Who to fight, what to do, when to commit

Zero Protocol separates three questions that are often allowed to blur together.

The target manager decides who an enemy currently wants to fight. It filters potential targets through a shared faction matrix, then scores valid candidates using visibility, distance, faction preference and temporary damage threat.

This allows different classes to interpret the same encounter differently. A nearby civilian can be an attractive target to a mindless ghoul, while a disciplined Raid enemy strongly prefers to remain focused on the player. Damage can temporarily redirect attention, but that threat decays instead of permanently overwriting the enemy's priorities.

A small stable preference is also generated for each enemy. When several civilians are equally valid, enemies spread between them rather than all selecting the same mathematical winner. Because the preference remains stable, that distribution doesn't produce constant target flickering.

Target selection pipeline diagram: gather targetables, reject invalid candidates, score the plausible set, check line of sight and apply stickiness, with class profiles and decaying damage aggro feeding the score

Once a target has been selected, the state machine determines what visible behaviour the enemy performs. Patrol, Alert, Chase, Hold, Attack, Recovery, Reposition, Search and Reaction are explicit states with their own entry, update and exit rules.

Those states act much like task leaves in an Unreal Behavior Tree. An attack state owns its windup and commitment. A reposition state owns its destination and movement. Search owns travel to the last-known position and the local sweep that follows. Exit rules shut down hitboxes, release attack permission and clear temporary control even when an action is interrupted by a stun, launch or death.

Melee chaser state machine diagram with Idle, Patrol/Wander, Alert, Chase, Hold, Attack, Search, Reaction, Recovery, Dying and Dead states
Ranged skirmisher state machine diagram with Idle, Alert, Approach, Engage, Attack, Reposition, Recovery, Reaction, Search, Return, Dying and Dead states

The Combat Director answers the final question: whether an enemy that wants to attack should be allowed to commit right now.

Fairness without passivity

The Combat Director permits up to three melee attacks and two ranged attacks against the player at once. It doesn't select targets or tell enemies where to move. Its only job is to control how much active pressure reaches the player.

Permission is requested at the point of commitment rather than during approach. An enemy denied permission doesn't freeze in a visible queue. Melee enemies drift around a wide standoff ring while facing the player, and ranged enemies continue aiming or repositioning. They remain threatening and spatially active without all attacking simultaneously.

Fights between non-player factions bypass this budget. So a group of enemies attacking civilians can stay messy and dangerous while the pressure aimed at the player stays authored and readable.

One grammar, different enemies

Most enemies belong to one of two reusable behaviour families: melee chasers or ranged skirmishers. They share targeting, navigation, perception, reactions, death handling and encounter integration. Individual classes replace only the decision points that create their particular combat identity.

A Raid Grunt uses the standard melee pursuit loop, but can branch into a fireball attack beyond ten metres when its cooldown, range and ranged attack permission allow it. Closing the distance returns it to melee behaviour.

A ranged Minion attempts to hold its preferred distance, but entering danger-close range triggers a separate decision between committing to melee or fleeing. At range it can choose between ordinary projectiles, a triple attack or summoning more enemies.

A Skeletal Hound replaces the normal melee arrival with a deliberate circle and pounce sequence. It closes quickly, orbits while waiting for the correct range and attack permission, then locks its landing point during a telegraphed leap.

The common grammar remains readable even when the decisions change. Alert, approach, commitment, recovery, reaction and death continue to mean the same things across the roster.

Three specialist decision flows: Skeletal Hound circle and pounce, Raid Minion range switching and cast selection, and Lava Man ability priority by range

Pursuit without cheating

When an enemy can see its target, it continually refreshes a last-known position. When sight is broken, it travels toward that stored position rather than continuing to track the live target through walls.

Different enemies remember for different lengths of time. Mindless ghouls reach the remembered point and quickly give up. Search-capable enemies can investigate the location, look around and sweep nearby navigation points before returning to their previous activity.

Arena encounters can deliberately override those rules by widening detection and extending memory. So the same enemy can handle a hunt in an open level or sustained pressure inside a sealed arena without needing a second implementation.

The design isn't trying to produce intelligence for its own sake. It constrains autonomy where those constraints improve the experience. Targets are selected systemically, but switching is sticky. Pursuit is persistent, but memory is finite. Enemies act independently, but player-facing attack cadence is budgeted.

The result is a shared combat grammar that makes different enemy classes appear intentional while keeping the fight readable under pressure.

Need a level or technical designer?

I can take a feature from intent and diagrams through implementation, iteration and balance.