Creating Smart NPCs with AI
Welcome to Lesson 3! In this lesson, you'll learn how to create intelligent NPCs that use AI for decision-making, dialogue generation, and behavior adaptation. We'll build a comprehensive NPC system that brings your AI Dungeon Explorer to life with smart, responsive characters.
What You'll Build
In this lesson, you'll create an AI-Powered NPC System that includes:
- Intelligent NPCs with AI-driven decision making
- Dynamic Dialogue System with AI-generated conversations
- Adaptive Behavior that responds to player actions
- Personality System that creates unique character traits
- Memory System that tracks player interactions
Step 1: Understanding AI-Powered NPCs
Traditional vs AI-Enhanced NPCs
Traditional NPCs:
- Fixed dialogue trees and responses
- Scripted behaviors and interactions
- Limited personality and character depth
- Predictable and repetitive interactions
AI-Enhanced NPCs:
- Dynamic, context-aware dialogue generation
- Adaptive behaviors based on player actions
- Rich personality systems with consistent traits
- Memorable interactions that evolve over time
Core AI NPC Components
- Personality Engine: Defines character traits and behavioral patterns
- Dialogue Generator: Creates contextually appropriate conversations
- Decision Maker: Chooses actions based on personality and situation
- Memory System: Tracks and recalls past interactions
- Behavior Adapter: Modifies behavior based on player relationship
Step 2: Creating the NPC Personality System
Personality Traits and Characteristics
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public class PersonalityTraits
{
[Header("Core Traits")]
[Range(0, 100)] public int friendliness = 50;
[Range(0, 100)] public int intelligence = 50;
[Range(0, 100)] public int courage = 50;
[Range(0, 100)] public int humor = 50;
[Range(0, 100)] public int aggression = 50;
[Header("Communication Style")]
[Range(0, 100)] public int formality = 50;
[Range(0, 100)] public int verbosity = 50;
[Range(0, 100)] public int directness = 50;
[Header("Values and Beliefs")]
public List<string> values = new List<string>();
public List<string> fears = new List<string>();
public List<string> goals = new List<string>();
public string GetPersonalityDescription()
{
return $"A {GetTraitLevel(friendliness)} person who is {GetTraitLevel(intelligence)} and " +
$"tends to be {GetTraitLevel(courage)}. They communicate in a {GetTraitLevel(formality)} manner.";
}
string GetTraitLevel(int value)
{
if (value < 20) return "very low";
if (value < 40) return "low";
if (value < 60) return "moderate";
if (value < 80) return "high";
return "very high";
}
}
[System.Serializable]
public class NPCData
{
public string name;
public string role;
public string background;
public PersonalityTraits personality;
public List<string> dialogueTopics = new List<string>();
public Dictionary<string, int> relationshipScores = new Dictionary<string, int>();
public List<string> memories = new List<string>();
}
public class AINPC : MonoBehaviour
{
[Header("NPC Configuration")]
public NPCData npcData;
public Transform player;
public float interactionRange = 3f;
[Header("AI Settings")]
public AIDialogueGenerator dialogueGenerator;
public AIBehaviorController behaviorController;
[Header("Visual Components")]
public Animator animator;
public GameObject speechBubble;
public TextMesh speechText;
private bool isInteracting = false;
private string currentDialogue = "";
void Start()
{
InitializeNPC();
}
void InitializeNPC()
{
// Generate personality if not set
if (npcData.personality == null)
{
npcData.personality = GenerateRandomPersonality();
}
// Initialize relationship with player
if (!npcData.relationshipScores.ContainsKey("Player"))
{
npcData.relationshipScores["Player"] = 50; // Neutral
}
// Set up AI components
if (dialogueGenerator == null)
{
dialogueGenerator = gameObject.AddComponent<AIDialogueGenerator>();
}
if (behaviorController == null)
{
behaviorController = gameObject.AddComponent<AIBehaviorController>();
}
}
PersonalityTraits GenerateRandomPersonality()
{
PersonalityTraits traits = new PersonalityTraits();
// Generate random traits with some coherence
traits.friendliness = Random.Range(20, 80);
traits.intelligence = Random.Range(30, 90);
traits.courage = Random.Range(20, 80);
traits.humor = Random.Range(10, 70);
traits.aggression = Random.Range(10, 60);
// Communication style
traits.formality = Random.Range(20, 80);
traits.verbosity = Random.Range(30, 90);
traits.directness = Random.Range(20, 80);
// Generate values and goals
traits.values = GenerateRandomValues();
traits.fears = GenerateRandomFears();
traits.goals = GenerateRandomGoals();
return traits;
}
List<string> GenerateRandomValues()
{
string[] possibleValues = {
"Honor", "Knowledge", "Peace", "Justice", "Freedom", "Family", "Power", "Wisdom"
};
List<string> values = new List<string>();
int valueCount = Random.Range(2, 5);
for (int i = 0; i < valueCount; i++)
{
string value = possibleValues[Random.Range(0, possibleValues.Length)];
if (!values.Contains(value))
{
values.Add(value);
}
}
return values;
}
List<string> GenerateRandomFears()
{
string[] possibleFears = {
"Death", "Failure", "Betrayal", "Darkness", "Loneliness", "Weakness", "Change"
};
List<string> fears = new List<string>();
int fearCount = Random.Range(1, 4);
for (int i = 0; i < fearCount; i++)
{
string fear = possibleFears[Random.Range(0, possibleFears.Length)];
if (!fears.Contains(fear))
{
fears.Add(fear);
}
}
return fears;
}
List<string> GenerateRandomGoals()
{
string[] possibleGoals = {
"Protect the innocent", "Gain knowledge", "Find treasure", "Defeat evil",
"Help others", "Become stronger", "Explore the world", "Make friends"
};
List<string> goals = new List<string>();
int goalCount = Random.Range(1, 3);
for (int i = 0; i < goalCount; i++)
{
string goal = possibleGoals[Random.Range(0, possibleGoals.Length)];
if (!goals.Contains(goal))
{
goals.Add(goal);
}
}
return goals;
}
void Update()
{
if (player != null)
{
float distance = Vector3.Distance(transform.position, player.position);
if (distance <= interactionRange && !isInteracting)
{
ShowInteractionPrompt();
}
else if (distance > interactionRange && isInteracting)
{
HideInteractionPrompt();
}
}
}
void ShowInteractionPrompt()
{
// Show interaction UI
if (speechBubble != null)
{
speechBubble.SetActive(true);
}
}
void HideInteractionPrompt()
{
// Hide interaction UI
if (speechBubble != null)
{
speechBubble.SetActive(false);
}
}
public void StartInteraction()
{
if (!isInteracting)
{
isInteracting = true;
StartCoroutine(GenerateAndDisplayDialogue());
}
}
System.Collections.IEnumerator GenerateAndDisplayDialogue()
{
// Generate AI dialogue based on context
string dialogue = yield return StartCoroutine(dialogueGenerator.GenerateDialogue(npcData, GetCurrentContext()));
// Display dialogue
DisplayDialogue(dialogue);
// Update relationship based on interaction
UpdateRelationship();
// Store memory of interaction
StoreInteractionMemory();
}
DialogueContext GetCurrentContext()
{
DialogueContext context = new DialogueContext();
context.playerLevel = 1; // Get from player data
context.timeOfDay = System.DateTime.Now.Hour;
context.location = "Dungeon";
context.recentEvents = GetRecentEvents();
context.playerMood = GetPlayerMood();
return context;
}
List<string> GetRecentEvents()
{
// Get recent game events that might affect dialogue
List<string> events = new List<string>();
events.Add("Player entered dungeon");
events.Add("Recent monster encounters");
return events;
}
string GetPlayerMood()
{
// Determine player mood based on recent actions
return "Curious"; // Default mood
}
void DisplayDialogue(string dialogue)
{
currentDialogue = dialogue;
if (speechText != null)
{
speechText.text = dialogue;
}
// Animate NPC
if (animator != null)
{
animator.SetTrigger("Talk");
}
// Show dialogue for a duration
StartCoroutine(ShowDialogueForDuration(5f));
}
System.Collections.IEnumerator ShowDialogueForDuration(float duration)
{
yield return new WaitForSeconds(duration);
HideDialogue();
}
void HideDialogue()
{
if (speechText != null)
{
speechText.text = "";
}
isInteracting = false;
}
void UpdateRelationship()
{
// Update relationship based on interaction quality
int relationshipChange = CalculateRelationshipChange();
npcData.relationshipScores["Player"] = Mathf.Clamp(
npcData.relationshipScores["Player"] + relationshipChange, 0, 100);
}
int CalculateRelationshipChange()
{
// Calculate relationship change based on dialogue quality and NPC personality
int change = 0;
// Friendly NPCs are easier to please
if (npcData.personality.friendliness > 70)
{
change += Random.Range(1, 4);
}
else if (npcData.personality.friendliness < 30)
{
change += Random.Range(-2, 1);
}
return change;
}
void StoreInteractionMemory()
{
string memory = $"Interacted with player at {System.DateTime.Now:HH:mm}";
npcData.memories.Add(memory);
// Keep only recent memories
if (npcData.memories.Count > 10)
{
npcData.memories.RemoveAt(0);
}
}
}
Step 3: AI Dialogue Generation System
Creating the Dialogue Generator
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class DialogueContext
{
public int playerLevel;
public int timeOfDay;
public string location;
public List<string> recentEvents;
public string playerMood;
public string weather;
public string season;
}
public class AIDialogueGenerator : MonoBehaviour
{
[Header("AI Settings")]
public string apiKey = "";
public string baseUrl = "https://api.openai.com/v1/chat/completions";
[Header("Dialogue Settings")]
public int maxDialogueLength = 100;
public string[] dialogueTopics = {
"Greeting", "Farewell", "Question", "Information", "Story", "Advice", "Warning"
};
public IEnumerator GenerateDialogue(NPCData npcData, DialogueContext context)
{
string prompt = CreateDialoguePrompt(npcData, context);
// This would make an actual API call to your AI service
// For now, we'll simulate the response
yield return new WaitForSeconds(1f);
string dialogue = SimulateDialogueGeneration(npcData, context);
yield return dialogue;
}
string CreateDialoguePrompt(NPCData npcData, DialogueContext context)
{
return $@"Generate dialogue for an NPC in a fantasy game:
NPC Information:
- Name: {npcData.name}
- Role: {npcData.role}
- Background: {npcData.background}
- Personality: {npcData.personality.GetPersonalityDescription()}
- Values: {string.Join(", ", npcData.personality.values)}
- Fears: {string.Join(", ", npcData.personality.fears)}
- Goals: {string.Join(", ", npcData.personality.goals)}
Context:
- Player Level: {context.playerLevel}
- Time: {context.timeOfDay}:00
- Location: {context.location}
- Player Mood: {context.playerMood}
- Recent Events: {string.Join(", ", context.recentEvents)}
Relationship with Player: {npcData.relationshipScores.GetValueOrDefault("Player", 50)}/100
Requirements:
- Keep dialogue under {maxDialogueLength} characters
- Match the NPC's personality and communication style
- Be contextually appropriate for the situation
- Include personality quirks and traits
- Make it feel natural and engaging
- Consider the relationship level with the player
Generate a single dialogue response that the NPC would say to the player.";
}
string SimulateDialogueGeneration(NPCData npcData, DialogueContext context)
{
// Simulate AI-generated dialogue based on NPC personality
List<string> possibleDialogues = new List<string>();
// Generate dialogues based on personality traits
if (npcData.personality.friendliness > 70)
{
possibleDialogues.Add("Hello there, friend! It's wonderful to see you!");
possibleDialogues.Add("Welcome! I'm so glad you've come to visit!");
}
else if (npcData.personality.friendliness < 30)
{
possibleDialogues.Add("What do you want?");
possibleDialogues.Add("I don't have time for this...");
}
else
{
possibleDialogues.Add("Hello. How can I help you?");
possibleDialogues.Add("Good to see you. What brings you here?");
}
// Adjust based on intelligence
if (npcData.personality.intelligence > 80)
{
possibleDialogues.Add("I've been studying the ancient texts. Fascinating stuff!");
possibleDialogues.Add("The mysteries of this place are quite intriguing, don't you think?");
}
// Adjust based on courage
if (npcData.personality.courage > 80)
{
possibleDialogues.Add("I'm not afraid of what lies ahead. Are you?");
possibleDialogues.Add("Danger? Ha! I've faced worse than this!");
}
else if (npcData.personality.courage < 30)
{
possibleDialogues.Add("Please be careful out there... I worry about you.");
possibleDialogues.Add("I... I don't like to think about what might be lurking...");
}
// Adjust based on relationship
int relationship = npcData.relationshipScores.GetValueOrDefault("Player", 50);
if (relationship > 80)
{
possibleDialogues.Add("My dear friend, it's always a pleasure to see you!");
possibleDialogues.Add("You're like family to me now. How are you doing?");
}
else if (relationship < 20)
{
possibleDialogues.Add("I don't trust you. What are you really after?");
possibleDialogues.Add("Stay away from me. I don't want any trouble.");
}
// Select appropriate dialogue
if (possibleDialogues.Count > 0)
{
return possibleDialogues[Random.Range(0, possibleDialogues.Count)];
}
return "Hello there. How can I help you?";
}
}
Step 4: AI Behavior Controller
Creating Adaptive Behaviors
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class AIBehaviorController : MonoBehaviour
{
[Header("Behavior Settings")]
public float updateInterval = 1f;
public float behaviorChangeThreshold = 0.3f;
[Header("Current Behavior")]
public NPCBehavior currentBehavior;
public float behaviorIntensity = 0.5f;
private AINPC npc;
private Transform player;
private float lastUpdateTime;
void Start()
{
npc = GetComponent<AINPC>();
player = GameObject.FindGameObjectWithTag("Player")?.transform;
// Initialize with default behavior
currentBehavior = NPCBehavior.Idle;
}
void Update()
{
if (Time.time - lastUpdateTime >= updateInterval)
{
UpdateBehavior();
lastUpdateTime = Time.time;
}
}
void UpdateBehavior()
{
if (player == null) return;
// Calculate behavior based on context
NPCBehavior newBehavior = CalculateOptimalBehavior();
if (newBehavior != currentBehavior)
{
ChangeBehavior(newBehavior);
}
// Update behavior intensity
UpdateBehaviorIntensity();
}
NPCBehavior CalculateOptimalBehavior()
{
float distance = Vector3.Distance(transform.position, player.position);
int relationship = npc.npcData.relationshipScores.GetValueOrDefault("Player", 50);
// Calculate behavior based on distance and relationship
if (distance < 2f)
{
if (relationship > 70)
return NPCBehavior.Friendly;
else if (relationship < 30)
return NPCBehavior.Defensive;
else
return NPCBehavior.Cautious;
}
else if (distance < 5f)
{
if (relationship > 60)
return NPCBehavior.Approaching;
else
return NPCBehavior.Observing;
}
else
{
return NPCBehavior.Idle;
}
}
void ChangeBehavior(NPCBehavior newBehavior)
{
currentBehavior = newBehavior;
ApplyBehavior(newBehavior);
}
void ApplyBehavior(NPCBehavior behavior)
{
switch (behavior)
{
case NPCBehavior.Idle:
SetIdleBehavior();
break;
case NPCBehavior.Friendly:
SetFriendlyBehavior();
break;
case NPCBehavior.Defensive:
SetDefensiveBehavior();
break;
case NPCBehavior.Cautious:
SetCautiousBehavior();
break;
case NPCBehavior.Approaching:
SetApproachingBehavior();
break;
case NPCBehavior.Observing:
SetObservingBehavior();
break;
}
}
void SetIdleBehavior()
{
// Idle animations and positioning
if (npc.animator != null)
{
npc.animator.SetBool("IsIdle", true);
npc.animator.SetBool("IsFriendly", false);
npc.animator.SetBool("IsDefensive", false);
}
}
void SetFriendlyBehavior()
{
// Friendly animations and positioning
if (npc.animator != null)
{
npc.animator.SetBool("IsIdle", false);
npc.animator.SetBool("IsFriendly", true);
npc.animator.SetBool("IsDefensive", false);
}
// Face player
if (player != null)
{
Vector3 direction = (player.position - transform.position).normalized;
transform.rotation = Quaternion.LookRotation(direction);
}
}
void SetDefensiveBehavior()
{
// Defensive animations and positioning
if (npc.animator != null)
{
npc.animator.SetBool("IsIdle", false);
npc.animator.SetBool("IsFriendly", false);
npc.animator.SetBool("IsDefensive", true);
}
// Step back from player
if (player != null)
{
Vector3 direction = (transform.position - player.position).normalized;
transform.rotation = Quaternion.LookRotation(direction);
}
}
void SetCautiousBehavior()
{
// Cautious animations and positioning
if (npc.animator != null)
{
npc.animator.SetBool("IsIdle", false);
npc.animator.SetBool("IsFriendly", false);
npc.animator.SetBool("IsDefensive", false);
npc.animator.SetBool("IsCautious", true);
}
}
void SetApproachingBehavior()
{
// Approach player slowly
if (player != null)
{
Vector3 direction = (player.position - transform.position).normalized;
transform.position += direction * Time.deltaTime * 0.5f;
transform.rotation = Quaternion.LookRotation(direction);
}
}
void SetObservingBehavior()
{
// Observe player from distance
if (player != null)
{
Vector3 direction = (player.position - transform.position).normalized;
transform.rotation = Quaternion.LookRotation(direction);
}
}
void UpdateBehaviorIntensity()
{
// Adjust behavior intensity based on personality and context
float baseIntensity = 0.5f;
// Adjust based on personality traits
if (npc.npcData.personality.aggression > 70)
{
baseIntensity += 0.2f;
}
if (npc.npcData.personality.courage > 70)
{
baseIntensity += 0.1f;
}
behaviorIntensity = Mathf.Clamp(baseIntensity, 0f, 1f);
}
}
public enum NPCBehavior
{
Idle,
Friendly,
Defensive,
Cautious,
Approaching,
Observing
}
Step 5: Memory and Learning System
Creating NPC Memory
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
[System.Serializable]
public class NPCMemory
{
public string eventDescription;
public System.DateTime timestamp;
public string eventType;
public int importance;
public Dictionary<string, int> emotionalImpact;
public NPCMemory(string description, string type, int importance)
{
eventDescription = description;
timestamp = System.DateTime.Now;
eventType = type;
this.importance = importance;
emotionalImpact = new Dictionary<string, int>();
}
}
public class NPCMemorySystem : MonoBehaviour
{
[Header("Memory Settings")]
public int maxMemories = 50;
public float memoryDecayRate = 0.1f;
private List<NPCMemory> memories = new List<NPCMemory>();
private AINPC npc;
void Start()
{
npc = GetComponent<AINPC>();
}
public void AddMemory(string description, string type, int importance)
{
NPCMemory memory = new NPCMemory(description, type, importance);
memories.Add(memory);
// Remove old memories if we exceed the limit
if (memories.Count > maxMemories)
{
memories = memories.OrderByDescending(m => m.importance)
.ThenByDescending(m => m.timestamp)
.Take(maxMemories)
.ToList();
}
}
public List<NPCMemory> GetRelevantMemories(string context)
{
return memories.Where(m =>
m.eventDescription.ToLower().Contains(context.ToLower()) ||
m.eventType.ToLower().Contains(context.ToLower())
).OrderByDescending(m => m.importance)
.ThenByDescending(m => m.timestamp)
.Take(5)
.ToList();
}
public List<NPCMemory> GetRecentMemories(int count = 5)
{
return memories.OrderByDescending(m => m.timestamp)
.Take(count)
.ToList();
}
public List<NPCMemory> GetImportantMemories(int count = 3)
{
return memories.Where(m => m.importance > 7)
.OrderByDescending(m => m.importance)
.Take(count)
.ToList();
}
public void UpdateMemoryDecay()
{
foreach (var memory in memories)
{
// Reduce importance over time
memory.importance = Mathf.Max(0, memory.importance - memoryDecayRate);
}
// Remove memories with zero importance
memories.RemoveAll(m => m.importance <= 0);
}
public string GetMemorySummary()
{
if (memories.Count == 0)
{
return "I don't have any memories yet.";
}
var recentMemories = GetRecentMemories(3);
var importantMemories = GetImportantMemories(2);
string summary = "I remember ";
if (importantMemories.Count > 0)
{
summary += importantMemories[0].eventDescription;
if (importantMemories.Count > 1)
{
summary += $" and {importantMemories[1].eventDescription}";
}
}
else if (recentMemories.Count > 0)
{
summary += recentMemories[0].eventDescription;
}
return summary + ".";
}
}
Step 6: Testing and Debugging NPCs
NPC Testing Framework
using UnityEngine;
using System.Collections.Generic;
public class NPCTester : MonoBehaviour
{
[Header("Test Settings")]
public bool runAutomatedTests = true;
public int testIterations = 10;
[Header("Test Results")]
public List<NPCTestResult> testResults = new List<NPCTestResult>();
void Start()
{
if (runAutomatedTests)
{
RunNPCTests();
}
}
void RunNPCTests()
{
AINPC[] npcs = FindObjectsOfType<AINPC>();
foreach (var npc in npcs)
{
NPCTestResult result = TestNPC(npc);
testResults.Add(result);
}
AnalyzeNPCTestResults();
}
NPCTestResult TestNPC(AINPC npc)
{
NPCTestResult result = new NPCTestResult();
result.npcName = npc.npcData.name;
// Test personality coherence
result.personalityCoherence = TestPersonalityCoherence(npc);
// Test dialogue generation
result.dialogueQuality = TestDialogueQuality(npc);
// Test behavior adaptation
result.behaviorAdaptation = TestBehaviorAdaptation(npc);
// Test memory system
result.memoryFunctionality = TestMemorySystem(npc);
return result;
}
float TestPersonalityCoherence(AINPC npc)
{
// Test if personality traits are consistent
var traits = npc.npcData.personality;
float coherence = 0f;
// Check if traits make sense together
if (traits.friendliness > 70 && traits.aggression < 30)
{
coherence += 0.3f;
}
if (traits.intelligence > 70 && traits.verbosity > 50)
{
coherence += 0.2f;
}
if (traits.courage > 70 && traits.aggression > 50)
{
coherence += 0.2f;
}
// Check if values align with personality
if (traits.values.Contains("Honor") && traits.friendliness > 60)
{
coherence += 0.3f;
}
return Mathf.Clamp01(coherence);
}
float TestDialogueQuality(AINPC npc)
{
// Test dialogue generation quality
int testCount = 5;
int successfulGenerations = 0;
for (int i = 0; i < testCount; i++)
{
try
{
// Simulate dialogue generation
string dialogue = GenerateTestDialogue(npc);
if (!string.IsNullOrEmpty(dialogue) && dialogue.Length > 10)
{
successfulGenerations++;
}
}
catch
{
// Dialogue generation failed
}
}
return (float)successfulGenerations / testCount;
}
string GenerateTestDialogue(AINPC npc)
{
// Simulate dialogue generation for testing
return $"Hello, I'm {npc.npcData.name}. How can I help you?";
}
float TestBehaviorAdaptation(AINPC npc)
{
// Test if NPC behavior adapts to different situations
var behaviorController = npc.GetComponent<AIBehaviorController>();
if (behaviorController == null) return 0f;
// Test behavior changes
NPCBehavior initialBehavior = behaviorController.currentBehavior;
// Simulate different scenarios
SimulatePlayerApproach(npc);
System.Threading.Thread.Sleep(100);
NPCBehavior newBehavior = behaviorController.currentBehavior;
return initialBehavior != newBehavior ? 1f : 0f;
}
void SimulatePlayerApproach(AINPC npc)
{
// Simulate player approaching NPC
Transform player = GameObject.FindGameObjectWithTag("Player")?.transform;
if (player != null)
{
player.position = npc.transform.position + Vector3.forward * 2f;
}
}
float TestMemorySystem(AINPC npc)
{
var memorySystem = npc.GetComponent<NPCMemorySystem>();
if (memorySystem == null) return 0f;
// Test memory functionality
memorySystem.AddMemory("Test event", "Test", 5);
var memories = memorySystem.GetRecentMemories(1);
return memories.Count > 0 ? 1f : 0f;
}
void AnalyzeNPCTestResults()
{
float avgPersonalityCoherence = 0f;
float avgDialogueQuality = 0f;
float avgBehaviorAdaptation = 0f;
float avgMemoryFunctionality = 0f;
foreach (var result in testResults)
{
avgPersonalityCoherence += result.personalityCoherence;
avgDialogueQuality += result.dialogueQuality;
avgBehaviorAdaptation += result.behaviorAdaptation;
avgMemoryFunctionality += result.memoryFunctionality;
}
int npcCount = testResults.Count;
avgPersonalityCoherence /= npcCount;
avgDialogueQuality /= npcCount;
avgBehaviorAdaptation /= npcCount;
avgMemoryFunctionality /= npcCount;
Debug.Log($"NPC Test Results Analysis:");
Debug.Log($"Average Personality Coherence: {avgPersonalityCoherence:P}");
Debug.Log($"Average Dialogue Quality: {avgDialogueQuality:P}");
Debug.Log($"Average Behavior Adaptation: {avgBehaviorAdaptation:P}");
Debug.Log($"Average Memory Functionality: {avgMemoryFunctionality:P}");
// Provide recommendations
if (avgPersonalityCoherence < 0.7f)
{
Debug.LogWarning("Personality coherence issues detected. Consider improving personality generation logic.");
}
if (avgDialogueQuality < 0.8f)
{
Debug.LogWarning("Dialogue quality issues detected. Consider improving AI prompting.");
}
if (avgBehaviorAdaptation < 0.6f)
{
Debug.LogWarning("Behavior adaptation issues detected. Consider improving behavior logic.");
}
if (avgMemoryFunctionality < 0.9f)
{
Debug.LogWarning("Memory system issues detected. Consider improving memory implementation.");
}
}
}
[System.Serializable]
public class NPCTestResult
{
public string npcName;
public float personalityCoherence;
public float dialogueQuality;
public float behaviorAdaptation;
public float memoryFunctionality;
}
Next Steps
In the final lesson, you'll learn Prototype Completion and Polish - how to finish your AI Dungeon Explorer with professional touches, optimization, and deployment.
Key Takeaways
- AI can create rich, personality-driven NPCs with consistent behaviors
- Dynamic dialogue generation creates engaging, contextual conversations
- Memory systems make NPCs feel alive and responsive
- Testing frameworks ensure NPC quality and functionality
Resources for Further Learning
- Unity AI and Machine Learning
- AI Game Builder - Practice AI-assisted NPC creation
- Community Forums - Share your NPC implementations
Ready to polish your prototype? Let's move on to Lesson 4: Prototype Completion and Polish!