Intelligent NPCs
Studio:Playpower LabsRole:Unity Developer
3D world where players talk to NPCs by voice. Whisper, GPT and AWS Polly handle speech recognition, contextual replies, voice output and LLM-driven quests. Built solo.
Tech Stack
- Unity
- C#
- OpenAI GPT
- AWS Polly
- OpenAI Whisper
- VFX Graph
gallery
what i did
A 3D prototype where players explore fantasy environments, talk to generative AI-driven NPCs by voice, and complete quests the LLM manages.
You can download the Windows build to try full voice input (the web build supports text only).
Key Contributions
- Connected OpenAI Whisper, GPT and AWS Polly in Unity, so NPCs understand the player's speech, reply in context, and speak the reply aloud.
- The pipeline transcribes the player's voice, sends it to the LLM, and turns the result into both speech and NPC behaviour.
- NPCs give out mini-quests because live gameplay state is included in the LLM prompt.
- Built solo, as both engineer and technical designer.
- Tested AI conversation inside a real gameplay loop, not a standalone chat window.
- Kept system boundaries clean, to show the architecture could grow into a full game.
- NPC personas (name, backstory, personality traits) are ScriptableObject assets, so traits are reusable data, not tied to specific prefabs.
- One modular NPC prefab covers every variant: patrolling, quest-giving and stationary.
- NPC identity is separate from behaviour, which keeps assets manageable as the cast grows.
Interactions and quests are defined by C# interfaces, so objects, NPCs and AI components work together without depending on each other's concrete classes.
IQuest
- Each quest has its own logic behind a generic interface.
- This lets an LLM-driven NPC advance a quest from the player's natural-language responses.
public interface IQuest
{
bool IsActive { get; }
void OnQuestStart();
void OnQuestEnd();
void OnPlayerResponse(string response);
}IInteractable
One activation contract for everything the player can interact with: NPCs, chests and doors.
public interface IInteractable
{
void Interact();
}Abstract base classes hold shared state logic, so it isn't duplicated across similar interactions while subclasses still add their own behaviour.
Abstract Door System
public abstract class DoorBehaviour : MonoBehaviour
{
private Animator animator;
private int animationHash;
private bool isOpen;
public event Action DoorStateChanged;
protected void OpenDoor()
{
if (isOpen) return;
if (animator) animator.SetBool(animationHash, true);
DoorStateChanged?.Invoke();
isOpen = true;
}
private void OnTriggerExit(Collider other)
{
if (!isOpen) return;
if (animator) animator.SetBool(animationHash, false);
DoorStateChanged?.Invoke();
isOpen = false;
}
}- The base class handles animation syncing and open/closed state.
- Subclasses such as
InteractableDoorandAutomaticDooronly define what opens them. - A
DoorStateChangedevent lets other systems add sounds or logic without touching the door code.