> Design NPC and enemy decision-making with finite state machines, behavior trees, steering behaviors, and A* pathfinding — engine-neutral algorithms that pair with the detected engine's navigation API. Use when building enemy AI, an FSM or behavior tree, steering/flocking, or pathfinding, or when the user mentions state machine, behavior tree, blackboard, A*, navmesh, seek, or patrol/chase.
npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill game-ai
Build believable NPC behavior from three separable layers: decide (what to
do), steer (how to move there), and path (how to route around the map).
Keep them decoupled — a behavior tree picks a target, the pathfinder produces
waypoints, steering follows them. This skill teaches the engine-neutral
algorithms; bind them to your engine via the related skills below.
group movement, or "find a path to the player".
reactive behaviors with priorities), or steering (smooth local movement).
navmesh agent.
When *not* to use: for the engine's concrete navmesh/agent API and baking,
use unity-navmesh, unreal-behavior-trees, or Godot's NavigationAgent2D/3D
(see that engine skill). For movement/collision feel, use physics-tuning. For
spawning waves along lanes, see the tower-defense genre skill.
transitions → FSM. Many behaviors, priorities, interruption, reuse → behavior
tree. Continuous "how strongly do I want each option" → utility scoring.
(target position, action). Steering or pathfinding turns intent into motion.
Fewer nodes = faster A*. Prefer the engine's navmesh for 3D; A* on a grid for
tile games.
advancing when close, so agents round corners.
tile, not every frame. Cache the path; only the waypoint index advances.
on corners, oscillate between states? Draw the path and current state on
screen while tuning.
# Each state is a small object with enter/update/exit. The machine owns "current".
class_name State
func enter(agent): pass
func update(agent, dt) -> State: return null # return a new state to transition
func exit(agent): pass
# --- Chase state: returns Patrol when the player escapes sight range ---
class Chase extends State:
func update(agent, dt) -> State:
if not agent.can_see(agent.target):
return Patrol.new() # transition by returning next state
agent.move_toward(agent.target.position, dt)
return null # null = stay in this state
# --- Driver: call once per frame ---
func tick(dt):
var next = current.update(self, dt)
if next != null:
current.exit(self); next.enter(self); current = next
Keep transition logic *inside* states (or in a table), never as a growing pile
of if flags. One state owns one behavior; that is what keeps an FSM readable.
# A node's tick() returns SUCCESS, FAILURE, or RUNNING (still working this frame).
enum Status { SUCCESS, FAILURE, RUNNING }
# Sequence: run children in order; stop at the first non-SUCCESS (logical AND).
func sequence_tick(children, agent, dt) -> int:
for child in children:
var s = child.tick(agent, dt)
if s != Status.SUCCESS:
return s # FAILURE or RUNNING short-circuits the sequence
return Status.SUCCESS
# Selector: try children until one succeeds or is RUNNING (logical OR / fallback).
func selector_tick(children, agent, dt) -> int:
for child in children:
var s = child.tick(agent, dt)
if s != Status.FAILURE:
return s # SUCCESS or RUNNING stops the search
return Status.FAILURE
A guard AI reads top-down: Selector[ Sequence[CanSeePlayer?, Chase], Patrol ]
— chase if visible, otherwise patrol. See references/behavior-trees.md for
leaf nodes, decorators (Inverter, Cooldown), and a blackboard.
# Seek: accelerate toward a target at full speed. Steering = desired - current.
func seek(pos, vel, target, max_speed, max_force) -> Vector2:
var desired = (target - pos).normalized() * max_speed
return (desired - vel).limit_length(max_force) # a force, not a teleport
# Arrive: like seek, but ramp speed down inside slow_radius so it stops cleanly.
func arrive(pos, vel, target, max_speed, max_force, slow_radius) -> Vector2:
var offset = target - pos
var dist = offset.length()
if dist < 0.001: return -vel # already there: kill drift
var ramped = max_speed * min(dist / slow_radius, 1.0)
var desired = offset / dist * ramped
return (desired - vel).limit_length(max_force)
# Per frame: vel += steering * dt; pos += vel * dt (always scale by dt)
# Match the heuristic to the movement. An ADMISSIBLE heuristic (never larger
# than the true remaining cost) keeps A* optimal.
def heuristic(a, b):
dx, dy = abs(a.x - b.x), abs(a.y - b.y)
# return dx + dy # Manhattan: 4-direction grids (no diagonals)
return (dx + dy) + (1.414 - 2) * min(dx, dy) # octile: 8-direction grids
# f(n) = g(n) + h(n): g = cost from start, h = heuristic to goal.
# Overestimating h is faster but no longer guarantees the shortest path.
The full A* loop (priority queue, came_from reconstruction, grid + waypoint
graphs) is in references/pathfinding.md.
when the target moves to a new tile; follow the cached waypoints in between.
hug walls and corners. Follow the path; advance the waypoint when within radius.
on a diagonal grid) returns fast but *non-shortest* paths. Pick the heuristic
that matches your allowed moves.
(walking, playing an animation) cause the tree to restart the action every
tick. Return RUNNING until the action completes.
if state == ... checks everywhererecreates the mess an FSM exists to prevent. Keep transitions in the state.
timeout that forces a repath or a state change.
references/pathfinding.md — complete A* (priority queue, reconstruction),grid vs waypoint graphs, when to defer to an engine navmesh.
references/behavior-trees.md — node taxonomy, leaf/decorator implementations,blackboard, and FSM-vs-BT selection.
unity-navmesh, unreal-behavior-trees — concrete engine AI/navigation APIs.physics-tuning — movement, collision response, and agent radius.procedural-gen — generating the graph/level the AI navigates.tower-defense, fps-shooter — genres that compose this skill.Guide users through a structured workflow for co-authoring documentation. Use when user wants to write documentation, proposals, technical specs, decision docs, or similar structured content. This workflow helps users efficiently transfer context, refine content through iteration, and verify the doc works for readers. Trigger when user mentions writing docs, creating proposals, drafting specs, or similar documentation tasks.
Intelligently organizes your files and folders across your computer by understanding context, finding duplicates, suggesting better structures, and automating cleanup tasks. Reduces cognitive load and keeps your digital workspace tidy without manual effort.
Generates creative domain name ideas for your project and checks availability across multiple TLDs (.com, .io, .dev, .ai, etc.). Saves hours of brainstorming and manual checking.
You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation.
Implements Manus-style file-based planning for complex tasks. Creates task_plan.md, findings.md, and progress.md. Use when starting complex multi-step tasks, research projects, or any task requiring >5 tool calls.
Creative research ideation and exploration. Use for open-ended brainstorming sessions, exploring interdisciplinary connections, challenging assumptions, or identifying research gaps. Best for early-stage research planning when you do not have specific observations yet. For formulating testable hypotheses from data use hypothesis-generation.
Comprehensive GitHub project management with swarm-coordinated issue tracking, project board automation, and sprint planning
Interview the user relentlessly about a plan or design until reaching shared understanding, resolving each branch of the decision tree. Use when user wants to stress-test a plan, get grilled on their design, or mentions "grill me".
Take gamedev-skills/game-ai from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.