How to Build a Simple AI Game in Unity - Step-by-Step Tutorial

Creating your first AI-powered game in Unity might seem daunting, but it's actually more accessible than you think. In this comprehensive tutorial, we'll build a complete game with AI-driven NPCs, intelligent behaviors, and dynamic interactions.

By the end of this guide, you'll have a working game that demonstrates core AI concepts while being fun to play. Let's dive in!

What We'll Build

We're creating a "Smart City Simulator" where AI-controlled characters navigate, interact, and make decisions based on their environment. The game will feature:

  • AI NPCs with different personalities and behaviors
  • Dynamic pathfinding using Unity's NavMesh system
  • Decision-making systems that respond to player actions
  • Procedural dialogue generated by AI
  • Smart resource management for the city

Prerequisites

Before we start, make sure you have:

  • Unity 2022.3 LTS or newer
  • Basic C# knowledge (variables, functions, classes)
  • An OpenAI API key (free tier available)
  • 2-3 hours of development time

Step 1: Project Setup

Create a New Unity Project

  1. Open Unity Hub
  2. Click "New Project"
  3. Select "3D (Built-in Render Pipeline)"
  4. Name your project "AI City Simulator"
  5. Click "Create Project"

Install Required Packages

In Unity, go to Window → Package Manager and install:

  • AI Navigation (for pathfinding)
  • TextMeshPro (for UI text)
  • Input System (for player controls)

Step 2: Setting Up the Scene

Create the Basic Environment

  1. Delete the default Main Camera
  2. Create a new GameObject3D Object → Plane (rename to "Ground")
  3. Scale the Ground to (10, 1, 10) for a larger play area
  4. Add a new Camera and position it at (0, 10, -10) looking down at the ground

Add Some Basic Objects

Create a few simple objects to make our city more interesting:

// Create this script and attach it to an empty GameObject
using UnityEngine;

public class CityBuilder : MonoBehaviour
{
    public GameObject[] buildingPrefabs;
    public int numberOfBuildings = 20;

    void Start()
    {
        GenerateCity();
    }

    void GenerateCity()
    {
        for (int i = 0; i < numberOfBuildings; i++)
        {
            Vector3 randomPosition = new Vector3(
                Random.Range(-20f, 20f),
                0f,
                Random.Range(-20f, 20f)
            );

            GameObject building = GameObject.CreatePrimitive(PrimitiveType.Cube);
            building.transform.position = randomPosition;
            building.transform.localScale = new Vector3(
                Random.Range(1f, 3f),
                Random.Range(2f, 5f),
                Random.Range(1f, 3f)
            );

            // Add a random color
            building.GetComponent<Renderer>().material.color = 
                new Color(Random.value, Random.value, Random.value);
        }
    }
}

Step 3: Creating the AI NPC System

The Base AI Character

Let's create our first AI character with basic behaviors:

using UnityEngine;
using UnityEngine.AI;

public class AINPC : MonoBehaviour
{
    [Header("AI Settings")]
    public float moveSpeed = 3.5f;
    public float decisionInterval = 2f;
    public float interactionRange = 5f;

    [Header("Personality")]
    public string npcName;
    public AIPersonality personality;
    public int energy = 100;
    public int happiness = 50;

    private NavMeshAgent agent;
    private Transform target;
    private float lastDecisionTime;

    public enum AIPersonality
    {
        Friendly,
        Shy,
        Energetic,
        Lazy
    }

    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        agent.speed = moveSpeed;

        // Set random personality if not assigned
        if (personality == AIPersonality.Friendly)
        {
            personality = (AIPersonality)Random.Range(0, 4);
        }

        // Start AI behavior
        InvokeRepeating(nameof(MakeDecision), 1f, decisionInterval);
    }

    void Update()
    {
        // Move towards target if we have one
        if (target != null)
        {
            agent.SetDestination(target.position);
        }
    }

    void MakeDecision()
    {
        // AI decision-making based on personality
        switch (personality)
        {
            case AIPersonality.Friendly:
                SeekInteraction();
                break;
            case AIPersonality.Shy:
                AvoidPlayers();
                break;
            case AIPersonality.Energetic:
                ExploreRandomly();
                break;
            case AIPersonality.Lazy:
                FindRestSpot();
                break;
        }

        // Update energy and happiness
        UpdateStats();
    }

    void SeekInteraction()
    {
        // Look for nearby players or NPCs
        Collider[] nearby = Physics.OverlapSphere(transform.position, interactionRange);
        foreach (Collider col in nearby)
        {
            if (col.CompareTag("Player") || col.CompareTag("NPC"))
            {
                target = col.transform;
                break;
            }
        }
    }

    void AvoidPlayers()
    {
        // Move away from players
        Collider[] nearby = Physics.OverlapSphere(transform.position, interactionRange);
        foreach (Collider col in nearby)
        {
            if (col.CompareTag("Player"))
            {
                Vector3 direction = (transform.position - col.transform.position).normalized;
                target = new GameObject().transform;
                target.position = transform.position + direction * 10f;
                break;
            }
        }
    }

    void ExploreRandomly()
    {
        // Move to random locations
        Vector3 randomDirection = Random.insideUnitSphere * 20f;
        randomDirection.y = 0;
        target = new GameObject().transform;
        target.position = transform.position + randomDirection;
    }

    void FindRestSpot()
    {
        // Look for a quiet spot to rest
        Vector3 restSpot = transform.position + Random.insideUnitSphere * 5f;
        restSpot.y = 0;
        target = new GameObject().transform;
        target.position = restSpot;
    }

    void UpdateStats()
    {
        // Energy decreases over time
        energy = Mathf.Max(0, energy - 1);

        // Happiness changes based on interactions
        if (target != null)
        {
            happiness = Mathf.Min(100, happiness + 2);
        }
        else
        {
            happiness = Mathf.Max(0, happiness - 1);
        }
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            // Interact with player
            InteractWithPlayer();
        }
    }

    void InteractWithPlayer()
    {
        // Generate dialogue based on personality and stats
        string dialogue = GenerateDialogue();
        Debug.Log($"{npcName}: {dialogue}");

        // Update happiness from interaction
        happiness = Mathf.Min(100, happiness + 10);
    }

    string GenerateDialogue()
    {
        string[] friendlyGreetings = {
            "Hello there! Nice to meet you!",
            "Welcome to our city!",
            "How are you doing today?"
        };

        string[] shyGreetings = {
            "Oh... hello...",
            "I... I didn't see you there...",
            "Um... hi..."
        };

        string[] energeticGreetings = {
            "Hey! Let's go explore together!",
            "I'm so excited to see you!",
            "Come on, let's have some fun!"
        };

        string[] lazyGreetings = {
            "Oh... you're here...",
            "I was just resting...",
            "Do we have to do something?"
        };

        switch (personality)
        {
            case AIPersonality.Friendly:
                return friendlyGreetings[Random.Range(0, friendlyGreetings.Length)];
            case AIPersonality.Shy:
                return shyGreetings[Random.Range(0, shyGreetings.Length)];
            case AIPersonality.Energetic:
                return energeticGreetings[Random.Range(0, energeticGreetings.Length)];
            case AIPersonality.Lazy:
                return lazyGreetings[Random.Range(0, lazyGreetings.Length)];
            default:
                return "Hello!";
        }
    }
}

Step 4: Adding AI-Powered Dialogue

Integrating OpenAI for Dynamic Conversations

Now let's add real AI-powered dialogue using OpenAI's API:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class AIDialogueSystem : MonoBehaviour
{
    [Header("OpenAI Settings")]
    public string apiKey = "your-api-key-here";
    public string apiUrl = "https://api.openai.com/v1/chat/completions";

    [Header("Dialogue Settings")]
    public float responseDelay = 1f;
    public int maxConversationLength = 10;

    private List<ConversationMessage> conversationHistory = new List<ConversationMessage>();

    [System.Serializable]
    public class ConversationMessage
    {
        public string role;
        public string content;
    }

    public void StartConversation(AINPC npc, string playerMessage = "")
    {
        StartCoroutine(GenerateAIResponse(npc, playerMessage));
    }

    IEnumerator GenerateAIResponse(AINPC npc, string playerMessage)
    {
        // Build conversation context
        string systemPrompt = BuildSystemPrompt(npc);
        string userMessage = playerMessage.Length > 0 ? playerMessage : "Hello!";

        // Add to conversation history
        conversationHistory.Add(new ConversationMessage { role = "system", content = systemPrompt });
        conversationHistory.Add(new ConversationMessage { role = "user", content = userMessage });

        // Prepare API request
        var requestData = new
        {
            model = "gpt-3.5-turbo",
            messages = conversationHistory.ToArray(),
            max_tokens = 150,
            temperature = 0.7f
        };

        string jsonData = JsonUtility.ToJson(requestData);

        // Send request to OpenAI
        using (var request = new UnityEngine.Networking.UnityWebRequest(apiUrl, "POST"))
        {
            request.SetRequestHeader("Content-Type", "application/json");
            request.SetRequestHeader("Authorization", $"Bearer {apiKey}");

            byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);
            request.uploadHandler = new UnityEngine.Networking.UploadHandlerRaw(bodyRaw);
            request.downloadHandler = new UnityEngine.Networking.DownloadHandlerBuffer();

            yield return request.SendWebRequest();

            if (request.result == UnityEngine.Networking.UnityWebRequest.Result.Success)
            {
                ProcessAIResponse(request.downloadHandler.text, npc);
            }
            else
            {
                Debug.LogError("AI API Error: " + request.error);
                // Fallback to basic dialogue
                npc.GetComponent<AINPC>().InteractWithPlayer();
            }
        }
    }

    string BuildSystemPrompt(AINPC npc)
    {
        return $"You are {npc.npcName}, a {npc.personality} NPC in a city simulation game. " +
               $"Your energy level is {npc.energy} and happiness is {npc.happiness}. " +
               $"Respond naturally to the player, keeping responses under 50 words. " +
               $"Stay in character as a {npc.personality} person.";
    }

    void ProcessAIResponse(string jsonResponse, AINPC npc)
    {
        try
        {
            // Parse the JSON response (simplified)
            var response = JsonUtility.FromJson<AIResponse>(jsonResponse);
            string aiMessage = response.choices[0].message.content;

            // Display the AI response
            Debug.Log($"{npc.npcName}: {aiMessage}");

            // Update NPC stats based on conversation
            npc.happiness = Mathf.Min(100, npc.happiness + 5);

            // Add to conversation history
            conversationHistory.Add(new ConversationMessage { role = "assistant", content = aiMessage });

            // Limit conversation history
            if (conversationHistory.Count > maxConversationLength)
            {
                conversationHistory.RemoveAt(0);
            }
        }
        catch (System.Exception e)
        {
            Debug.LogError("Failed to parse AI response: " + e.Message);
        }
    }

    [System.Serializable]
    public class AIResponse
    {
        public Choice[] choices;
    }

    [System.Serializable]
    public class Choice
    {
        public Message message;
    }

    [System.Serializable]
    public class Message
    {
        public string content;
    }
}

Step 5: Creating the Player Controller

Simple Player Movement

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    [Header("Movement Settings")]
    public float moveSpeed = 5f;
    public float rotationSpeed = 10f;

    private Rigidbody rb;
    private Vector3 movement;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        // Get input
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        movement = new Vector3(horizontal, 0f, vertical).normalized;
    }

    void FixedUpdate()
    {
        // Move the player
        if (movement.magnitude > 0.1f)
        {
            rb.MovePosition(rb.position + movement * moveSpeed * Time.fixedDeltaTime);

            // Rotate towards movement direction
            Quaternion targetRotation = Quaternion.LookRotation(movement);
            rb.rotation = Quaternion.Slerp(rb.rotation, targetRotation, rotationSpeed * Time.fixedDeltaTime);
        }
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("NPC"))
        {
            // Start conversation with NPC
            AIDialogueSystem dialogueSystem = FindObjectOfType<AIDialogueSystem>();
            if (dialogueSystem != null)
            {
                dialogueSystem.StartConversation(other.GetComponent<AINPC>(), "Hello!");
            }
        }
    }
}

Step 6: Setting Up the Scene

Create the Complete Setup

  1. Create a Player GameObject:

    • Add a Capsule (3D Object → Capsule)
    • Add the PlayerController script
    • Add a Rigidbody component
    • Tag it as "Player"
  2. Create AI NPCs:

    • Add Capsules for each NPC
    • Add the AINPC script
    • Add a NavMeshAgent component
    • Tag them as "NPC"
    • Set different personalities for each
  3. Set up NavMesh:

    • Go to Window → AI → Navigation
    • Select the Ground object
    • Click "Bake" to generate the NavMesh
  4. Add the Dialogue System:

    • Create an empty GameObject
    • Add the AIDialogueSystem script
    • Set your OpenAI API key

Step 7: Testing and Refinement

Test Your AI Game

  1. Play the scene and move around with WASD
  2. Approach NPCs to trigger conversations
  3. Observe AI behaviors - they should move according to their personalities
  4. Check the console for AI-generated dialogue

Common Issues and Solutions

Issue: NPCs not moving

  • Check if NavMesh is properly baked
  • Ensure NavMeshAgent is attached
  • Verify the Ground object is marked as Navigation Static

Issue: AI dialogue not working

  • Verify your OpenAI API key is correct
  • Check internet connection
  • Look at the console for error messages

Issue: Performance problems

  • Limit the number of NPCs (start with 3-5)
  • Reduce decision-making frequency
  • Optimize AI behavior scripts

Step 8: Adding Advanced AI Features

Smart Resource Management

public class CityManager : MonoBehaviour
{
    [Header("City Resources")]
    public int food = 100;
    public int energy = 100;
    public int happiness = 50;

    [Header("AI Decision Making")]
    public float decisionInterval = 5f;

    void Start()
    {
        InvokeRepeating(nameof(MakeCityDecisions), 1f, decisionInterval);
    }

    void MakeCityDecisions()
    {
        // AI makes decisions about city management
        if (food < 30)
        {
            Debug.Log("AI Decision: Need more food! Building farms...");
            food += 20;
        }

        if (energy < 40)
        {
            Debug.Log("AI Decision: Low energy! Building power plants...");
            energy += 30;
        }

        if (happiness < 30)
        {
            Debug.Log("AI Decision: Citizens are unhappy! Building entertainment...");
            happiness += 25;
        }
    }
}

Dynamic Event System

public class AIEventSystem : MonoBehaviour
{
    [Header("Events")]
    public string[] randomEvents = {
        "A festival is happening in the city square!",
        "A new building has been constructed!",
        "The weather is beautiful today!",
        "A traveling merchant has arrived!",
        "The city is celebrating a special occasion!"
    };

    public float eventInterval = 30f;

    void Start()
    {
        InvokeRepeating(nameof(TriggerRandomEvent), eventInterval, eventInterval);
    }

    void TriggerRandomEvent()
    {
        string randomEvent = randomEvents[Random.Range(0, randomEvents.Length)];
        Debug.Log($"City Event: {randomEvent}");

        // Notify all NPCs about the event
        AINPC[] npcs = FindObjectsOfType<AINPC>();
        foreach (AINPC npc in npcs)
        {
            npc.happiness += Random.Range(5, 15);
        }
    }
}

Step 9: Polishing and Deployment

Add Visual Feedback

  1. Create a simple UI to show NPC stats
  2. Add particle effects for interactions
  3. Include sound effects for ambiance
  4. Add a minimap to track NPC locations

Build and Test

  1. Go to File → Build Settings
  2. Add your scene to the build
  3. Select your target platform
  4. Click Build to create your game

What You've Learned

Congratulations! You've successfully created an AI-powered game in Unity. Here's what we accomplished:

  • AI Behavior Systems: NPCs with different personalities and decision-making
  • Dynamic Dialogue: Real AI-generated conversations using OpenAI
  • Smart Pathfinding: NPCs that navigate intelligently
  • Resource Management: AI-driven city management
  • Event Systems: Dynamic events that affect the game world

Next Steps

Now that you have a working AI game, consider these enhancements:

  1. Add more AI personalities and behaviors
  2. Implement machine learning for adaptive AI
  3. Create procedural quests using AI
  4. Add multiplayer support for collaborative AI
  5. Integrate voice recognition for natural interactions

Resources and Further Learning

  • Unity AI Documentation: Learn more about Unity's AI systems
  • OpenAI API Guide: Explore advanced AI integration
  • Game AI Patterns: Study common AI game patterns
  • Unity Learn: Official Unity learning platform

Conclusion

Building AI games in Unity is an exciting journey that combines creativity with cutting-edge technology. The system we've created demonstrates the power of AI in game development, from simple behaviors to complex conversations.

Remember: The best AI games are those that feel alive and responsive. Keep experimenting, keep learning, and most importantly, keep having fun with your creations!

Ready to take your AI game to the next level? Check out our advanced tutorials on machine learning integration and procedural content generation. The future of game development is in your hands!