Skip to main content
C# for Games – Complete Beginner to Advanced Guide
CHAPTER 11 Beginner

Enemy AI and NPC Programming

Updated: May 16, 2026
35 min read

# CHAPTER 11

Enemy AI and NPC Programming

1. Introduction

A game without opposition lacks challenge, and a world without NPCs (Non-Player Characters) feels dead. Programming entities that appear to "think" for themselves is the domain of Artificial Intelligence (AI). However, game AI is not true machine learning; it is an elaborate illusion built on strict rules and math. In this chapter, we will demystify Enemy AI programming. We will learn how to detect the player using distance calculations, move the enemy using directional vectors, and organize complex behaviors (like switching from Patrol to Chase to Attack) using the industry-standard State Machine pattern.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Use Vector math to calculate the distance between two objects.
  • Program an enemy to look at and move toward a target.
  • Implement a Finite State Machine (FSM) using an enum and switch.
  • Understand the basics of NavMesh Pathfinding.
  • Manage transition logic between AI states.

3. Detection (Vector Distance)

How does an enemy know the player is nearby? We measure the distance.
  • Engines provide a math function to find the length of a line between two points: Vector3.Distance(PointA, PointB).
csharp
123456
float distanceToPlayer = Vector3.Distance(transform.position, player.position);

if (distanceToPlayer < 10.0f)
{
    Console.WriteLine("I see the player!");
}

4. Chase Logic (Directional Vectors)

Once the enemy sees the player, how do they move toward them?
  1. 1. Find the Direction: Target Position - My Position.
  1. 2. Normalize it (make its length 1.0) so the enemy doesn't warp at hyper speed.
  1. 3. Multiply by Speed and DeltaTime.
csharp
12
Vector3 direction = (player.position - transform.position).normalized;
transform.Translate(direction * speed * Time.deltaTime);

5. Pathfinding (The NavMesh)

If there is a brick wall between the enemy and the player, the basic math above will cause the enemy to endlessly moonwalk into the wall.
  • Modern engines use a NavMesh (Navigation Mesh). The engine pre-calculates all the walkable floor space.
  • You give the enemy a "NavMesh Agent" component. You simply write agent.SetDestination(player.position), and the C++ engine code calculates a complex path *around* the wall instantly!

6. The State Machine (Organizing the Brain)

If your AI script is just a massive list of if / else statements, it will become an unreadable, buggy mess. We fix this using a State Machine. An AI can only be in ONE state at a time. We define these states using an enum.
csharp
12345678910111213141516171819202122
public enum AIState { Patrol, Chase, Attack }

class EnemyAI
{
    public AIState currentState = AIState.Patrol;

    void Update()
    {
        switch (currentState)
        {
            case AIState.Patrol:
                DoPatrolLogic();
                break;
            case AIState.Chase:
                DoChaseLogic();
                break;
            case AIState.Attack:
                DoAttackLogic();
                break;
        }
    }
}

7. Visual Learning: State Machine Flow

txt
123456789
          [ State: PATROL ]
                 |                  ^
   (Distance < 10m)                 | (Distance > 15m)
                 v                  |
          [ State: CHASE ] ---------+
                 |                  ^
   (Distance < 2m)                  | (Distance > 2m)
                 v                  |
          [ State: ATTACK ] --------+

8. Best Practices

  • Null Checks on Target: The most common AI bug occurs when the player dies and the game deletes the Player object. The Enemy's Update() loop then runs, asks "What is the distance to the Player?", realizes the Player doesn't exist, and the entire game crashes with a NullReferenceException. ALWAYS check if (player != null) before running AI logic!

9. Common Mistakes

  • Expensive Math in Update: Calculating complex pathfinding or Line-of-Sight Raycasts 60 times a second for 50 enemies will destroy your framerate. A smart programmer uses a Timer to only evaluate the State transitions every 0.25 seconds, while leaving only the basic movement in the 60fps Update loop.

10. Mini Project: Build a Zombie Chase Script

Objective: Combine distance checks, state switching, and movement.
csharp
1234567891011121314151617181920212223242526272829303132
class Zombie
{
    public Transform player; // Link in editor
    public float speed = 3.0f;
    public enum State { Idle, Chase }
    public State zombieState = State.Idle;

    void Update()
    {
        if (player == null) return; // Safety check!

        float dist = Vector3.Distance(transform.position, player.position);

        // TRANSITIONS
        if (zombieState == State.Idle && dist < 8.0f)
            zombieState = State.Chase;
        else if (zombieState == State.Chase && dist > 10.0f)
            zombieState = State.Idle;

        // BEHAVIORS
        switch (zombieState)
        {
            case State.Idle:
                // Stand still
                break;
            case State.Chase:
                Vector3 dir = (player.position - transform.position).normalized;
                transform.Translate(dir * speed * Time.deltaTime);
                break;
        }
    }
}

11. Practice Exercises

  1. 1. What math formula is used to find a Vector pointing directly from point A to point B?
  1. 2. What C# keyword is used to define a custom list of named integers, perfect for defining States like Patrol, Chase, and Attack?

12. MCQs with Answers

Question 1

An enemy AI tries to chase the player, but gets stuck endlessly walking into a crate that is blocking the direct path. What system must be implemented to allow the AI to calculate a path around obstacles?

Question 2

When organizing an AI's behavior, why is an enum and a switch statement preferred over a massive block of nested if/else statements?

13. Interview Questions

  • Q: Explain the mathematical process of finding a directional Vector from an Enemy to a Player. Why must this Vector be "Normalized" before it is multiplied by speed?
  • Q: Walk me through the implementation of a Finite State Machine in C#. How do you handle the transitions between states without hardcoding massive if blocks?
  • Q: A QA tester reports that the game crashes instantly every time the player is killed by an enemy. Identify the missing safeguard in the Enemy's C# script that caused the NullReferenceException.

14. FAQs

Q: Can I use Behavior Trees instead of State Machines? A: Yes! While State Machines are perfect for simple enemies (like a Mario Goomba), AAA games with highly complex AI (like soldiers taking cover and reloading) use Behavior Trees. These are usually constructed using visual node editors or advanced C# plugins, rather than raw switch statements.

15. Summary

In Chapter 11, we gave our game the spark of intelligence. We used Vector3.Distance to give our enemies vision, and calculated normalized direction vectors to give chase. We solved the problem of spaghetti code by organizing our AI into a Finite State Machine using an enum and a switch block, ensuring clean transitions between Idle and Hostile behaviors. Finally, we learned the importance of Null checking to prevent crashes.

16. Next Chapter Recommendation

Our enemies can move and think, but they slide across the floor like statues. It's time to bring them to life. Proceed to Chapter 12: Animations and Effects Programming.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·