Building AI NPCs in Unity
NPC intelligence in games is typically constructed in layers. The bottom layer handles movement and pathfinding: getting the character from point A to point B while avoiding obstacles. The middle layer handles perception: what does the NPC know about the world around it? The top layer handles decision-making: given what the NPC perceives, what should it do next? Each layer can be implemented independently and combined to create characters that feel alive and responsive.
Step 1: Design the NPC Behavior Model
Before writing code, decide which behavioral architecture fits your NPC's role in the game. The two most common approaches are finite state machines (FSMs) and behavior trees, and the choice depends on how many distinct behaviors the NPC needs.
Finite state machines work well for NPCs with a small number of clearly defined states. A guard NPC might have three states: Patrol, Chase, and Attack. Each state defines what the NPC does, and transitions between states are triggered by specific conditions (player enters vision range, player escapes to safe distance). FSMs are simple to implement, easy to debug, and perform well. Their limitation is that adding new states creates exponential transition complexity: 5 states can have up to 20 transitions, and managing that graph becomes unwieldy.
Behavior trees solve the scaling problem by organizing decisions in a hierarchical structure. A root node evaluates child nodes in priority order, and each child can be a condition check, an action, or another sub-tree. This makes it straightforward to add new behaviors without touching existing ones. A guard behavior tree might have branches for "respond to combat alert," "investigate suspicious sound," "patrol route," and "idle," evaluated in that priority order. The tree naturally handles interruption: if a combat alert fires while the NPC is investigating a sound, the higher-priority branch takes over immediately.
For web games where NPC count might be high and CPU budgets are tight, FSMs are the safer default. Reserve behavior trees for central characters who need complex, nuanced behavior. A game with 20 patrol guards and 2 boss enemies might use FSMs for the guards and behavior trees for the bosses.
Step 2: Implement Navigation with NavMesh
Unity's NavMesh system provides pathfinding out of the box. The NavMesh is a simplified representation of walkable surfaces in your scene, baked from the scene geometry. NavMeshAgent components attached to NPC GameObjects use this mesh to calculate paths and move along them with obstacle avoidance.
To set up basic navigation, mark your floor and platform geometry as "Navigation Static" in the Inspector, then open the Navigation window (Window, then AI, then Navigation) and bake the NavMesh. Adjust the agent radius, height, and step height to match your NPC character dimensions. Add a NavMeshAgent component to your NPC prefab, and in your script, call agent.SetDestination(targetPosition) to move the NPC to any reachable point.
For patrol behavior, create an array of waypoint transforms and cycle through them. When the NPC reaches one waypoint (check agent.remainingDistance against a threshold), set the next waypoint as the destination. For chase behavior, update the destination to the player's position each frame. The NavMeshAgent handles pathfinding, steering, and obstacle avoidance automatically.
Dynamic obstacles require NavMeshObstacle components on objects that move at runtime, such as doors, destructible walls, or other NPCs. Set these to "Carve" mode so the NavMesh updates around them. Be aware that frequent NavMesh updates have a CPU cost, which matters for WebGL builds where the budget is tight. If your game has many moving obstacles, consider using NavMeshObstacle without carving and relying on the agent's local avoidance instead.
Step 3: Build Perception Systems
Perception systems determine what an NPC "knows" about the game world. Without perception, NPCs either know everything (omniscient AI that feels unfair) or nothing (unresponsive AI that feels broken). A well-tuned perception system creates the illusion of awareness while keeping behavior believable.
Vision is the most common perception channel. Implement a vision cone using a combination of distance check, angle check, and raycast. First, check if the target (usually the player) is within the NPC's vision range. Then check if the angle between the NPC's forward direction and the direction to the target is within the field of view (typically 90-120 degrees for a humanoid NPC). Finally, cast a ray from the NPC to the target to check for obstructions. Only if all three checks pass does the NPC "see" the target.
Hearing provides a secondary awareness channel. When the player performs a loud action (running, shooting, breaking objects), broadcast an "audio event" at that position with a specified range. Any NPC within range whose hearing sensitivity meets the threshold receives the event and can react, typically by entering an "investigate" state and moving toward the sound's origin. This creates natural gameplay dynamics where players learn to move quietly.
An awareness system ties perception into a continuous metric rather than a binary detected/not-detected state. The NPC maintains an awareness value (0 to 1) that increases when the player is visible and decreases over time when not visible. Different awareness thresholds trigger different behaviors: 0.3 might trigger "suspicious" (NPC looks toward the player's last known position), 0.7 might trigger "alert" (NPC moves to investigate), and 1.0 triggers "engaged" (NPC enters combat). This creates a stealth gradient that feels more realistic than instant detection.
Step 4: Add Decision-Making Logic
Beyond simple state transitions, advanced NPC AI uses decision-making systems that evaluate multiple options and choose the best action given current circumstances. Two practical approaches for game NPCs are utility AI and goal-oriented action planning (GOAP).
Utility AI assigns a numerical score to each possible action based on the current game state. A hungry NPC scores "find food" higher. A threatened NPC scores "flee" or "fight" higher depending on its strength relative to the threat. The NPC always chooses the highest-scoring action, and the scoring functions can incorporate multiple factors with weighted curves. This produces emergent behavior: the NPC makes contextually appropriate decisions without explicit transition rules for every state combination.
GOAP defines goals (desired world states) and actions (operations that change the world state). The NPC identifies its highest-priority goal, then plans a sequence of actions that transforms the current world state into the goal state. For example, an NPC with the goal "be fed" and the actions "go to kitchen," "pick up food," and "eat food" will chain those actions automatically. GOAP is more complex to implement but produces naturally intelligent behavior sequences without manually scripting every combination.
For most web games, utility AI provides the best balance of behavioral richness and implementation simplicity. Define 5-8 possible actions per NPC type, each with a scoring function that takes 2-3 inputs, and the result is NPCs that feel thoughtful without requiring a PhD in AI to debug.
Step 5: Integrate Machine Learning for Adaptive Behavior
Unity's Inference Engine (formerly Sentis) enables a fundamentally different approach to NPC intelligence: rather than coding rules, you train a model on gameplay data and let the NPC learn from experience. The trained model runs locally within the game using ONNX format, with no server connection required.
The most practical application for web games is adaptive difficulty. Train a model on gameplay sessions from players of varying skill levels. The model learns to associate player behavior patterns (reaction time, accuracy, movement efficiency) with skill estimates. At runtime, the model evaluates the current player's behavior and adjusts NPC parameters accordingly: increasing aggression and accuracy against skilled players, reducing them against struggling players. This keeps the game challenging but not frustrating, adapting to each individual player without explicit difficulty settings.
Another application is imitation learning, where NPCs learn to mimic human play styles. Record gameplay data from human testers, train a model to predict human actions given the same game state, and use that model to drive NPC behavior. The result is NPCs that play in a human-like manner rather than following rigid algorithmic patterns. This works well for racing game opponents, fighting game sparring partners, and strategy game adversaries.
For WebGL builds, keep ML models small and run inference sparingly. A model with 50,000 parameters runs comfortably in browser CPU inference. A model with 5 million parameters will cause noticeable frame drops. Run inference every 0.5-1.0 seconds rather than every frame, and cache the results between evaluations. This amortizes the CPU cost while still providing responsive AI behavior.
Step 6: Optimize for WebGL Performance
NPC AI can be CPU-intensive, and WebGL's single-threaded environment means all AI computation shares the frame budget with rendering, physics, and audio. Profile your AI systems early and set clear performance budgets.
Stagger NPC updates so not all NPCs evaluate their AI on the same frame. If you have 20 NPCs, divide them into 4 groups of 5 and update one group per frame. Each NPC still updates every 4 frames (at 60 FPS, that is every 67 milliseconds), which is fast enough for believable behavior but spreads the CPU load evenly.
Use spatial partitioning to limit perception checks. Instead of every NPC checking distance to every player and every other NPC each frame, use a grid or quadtree to quickly identify nearby entities. An NPC only needs to run vision and hearing checks against entities in adjacent cells, not the entire scene.
Cache pathfinding results. If an NPC's destination has not changed, do not recalculate the path. NavMeshAgent handles this internally, but custom pathfinding solutions should implement path caching with invalidation only when the destination moves beyond a threshold distance or the path becomes blocked.
Disable AI for off-screen NPCs. NPCs that the player cannot see or interact with do not need full AI simulation. Reduce them to simplified state updates (advance patrol timer, check if player enters a large trigger zone) and restore full AI when they become relevant. This can reduce active AI load by 50-80% in games with large levels and many NPCs.
Start with the simplest AI architecture that meets your design needs, then add complexity only where players will notice. A well-tuned FSM with good perception feels smarter to players than a poorly tuned behavior tree with machine learning. Layer complexity where it matters and keep the performance budget in mind for WebGL deployment.