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
- Open Unity Hub
- Click "New Project"
- Select "3D (Built-in Render Pipeline)"
- Name your project "AI City Simulator"
- 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
- Delete the default Main Camera
- Create a new GameObject → 3D Object → Plane (rename to "Ground")
- Scale the Ground to (10, 1, 10) for a larger play area
- 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
-
Create a Player GameObject:
- Add a Capsule (3D Object → Capsule)
- Add the PlayerController script
- Add a Rigidbody component
- Tag it as "Player"
-
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
-
Set up NavMesh:
- Go to Window → AI → Navigation
- Select the Ground object
- Click "Bake" to generate the NavMesh
-
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
- Play the scene and move around with WASD
- Approach NPCs to trigger conversations
- Observe AI behaviors - they should move according to their personalities
- 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
- Create a simple UI to show NPC stats
- Add particle effects for interactions
- Include sound effects for ambiance
- Add a minimap to track NPC locations
Build and Test
- Go to File → Build Settings
- Add your scene to the build
- Select your target platform
- 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:
- Add more AI personalities and behaviors
- Implement machine learning for adaptive AI
- Create procedural quests using AI
- Add multiplayer support for collaborative AI
- 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!