PlayKit.ai

NPC Conversations

Quickly implement intelligent game NPCs using PlayKit_NPC

NPC Conversations

PlayKit_NPC is a MonoBehaviour component designed specifically for game NPCs. It automatically manages conversation history, letting you focus on character design rather than low-level implementation.

Unlike PlayKit_AIChatClient which requires manual history management, PlayKit_NPC automatically records conversations, making it ideal for game character dialogue scenarios.

Before You Begin

Add NPC Client

PlayKit_NPC is a MonoBehaviour component that can be added directly to an NPC's GameObject.

Method 1: Add in Inspector

  1. Select the NPC's GameObject
  2. Click Add Component
  3. Search for and add PlayKit_NPC

Method 2: Add via Code

using PlayKit_SDK;
using PlayKit_SDK.Public;
using UnityEngine;

public class NPCController : MonoBehaviour
{
    private PlayKit_NPC npcClient;

    void Start()
    {
        npcClient = gameObject.AddComponent<PlayKit_NPC>();
    }
}

Method 3: Use Factory Method

var npc = GetComponent<PlayKit_NPC>();
PlayKitSDK.Populate.CreateNpc(npc, "gpt-4");

Basic Conversation

Set Character

Use SetCharacterDesign() to define the NPC's personality and behavior:

using Cysharp.Threading.Tasks;
using PlayKit_SDK;
using PlayKit_SDK.Public;
using UnityEngine;

public class SimpleNPC : MonoBehaviour
{
    private PlayKit_NPC npc;

    void Start()
    {
        npc = GetComponent<PlayKit_NPC>();

        npc.SetCharacterDesign(@"
            You are a magic shop owner named Ellie.
            You run the best magic shop in the village.
            You are friendly and love chatting with adventurers.
            You are an expert in various magic items and always happy to give advice.
        ");
    }

    public async UniTask TalkToNPC(string playerMessage)
    {
        string response = await npc.Talk(
            playerMessage,
            this.GetCancellationTokenOnDestroy()
        );

        if (response != null)
        {
            Debug.Log($"Ellie: {response}");
            DisplayDialog(response);
        }
    }

    void DisplayDialog(string text)
    {
    }
}

Simple Conversation Example

var npc = GetComponent<PlayKit_NPC>();

npc.SetCharacterDesign("You are a friendly villager");

// First turn
string reply1 = await npc.Talk("Hello!");
// NPC might respond: "Hello! Nice to meet you. How can I help?"

// Second turn - NPC remembers previous conversation
string reply2 = await npc.Talk("What's interesting to see in this village?");
// NPC will respond based on conversation context

Streaming Conversation

Use TalkStream() for typewriter effect, displaying NPC response character by character:

using System;
using UnityEngine.UI;

public class StreamingNPC : MonoBehaviour
{
    [SerializeField] private Text dialogText;

    private PlayKit_NPC npc;
    private string currentDialog = "";

    void Start()
    {
        npc = GetComponent<PlayKit_NPC>();
        npc.SetCharacterDesign("You are a mysterious traveling merchant");
    }

    public async UniTask TalkToNPCStreaming(string playerMessage)
    {
        currentDialog = "";
        dialogText.text = "";

        await npc.TalkStream(
            message: playerMessage,
            onChunk: (chunk) =>
            {
                currentDialog += chunk;
                dialogText.text = currentDialog;
            },
            onComplete: (fullResponse) =>
            {
                Debug.Log($"Full response: {fullResponse}");
            },
            cancellationToken: this.GetCancellationTokenOnDestroy()
        );
    }
}

History Management

View Conversation History

var npc = GetComponent<PlayKit_NPC>();

// Get history
PlayKit_ChatMessage[] history = npc.GetHistory();

// Print history
foreach (var msg in history)
{
    Debug.Log($"[{msg.Role}] {msg.Content}");
}

// Or use built-in formatted print
npc.PrintPrettyChatMessages("NPC Conversation History");

Clear History

// Clear all history (including system message)
npc.ClearHistory();

// Need to reset character after clearing
npc.SetCharacterDesign("You are a friendly NPC");

Undo Last Message

// Undo last message
bool success = npc.RevertHistory();

if (success)
{
    Debug.Log("Last message undone");
}

Undo Multiple Messages

// Undo last 3 messages
int removed = npc.RevertChatMessages(3);
Debug.Log($"Removed {removed} messages");

Manually Add Messages

// Manually add messages to history
npc.AppendChatMessage("user", "Player said this");
npc.AppendChatMessage("assistant", "NPC responded this");

Save and Load Conversations

Save NPC conversation history for persistent NPC state.

Save Conversation

using System.IO;

public class NPCSaveSystem : MonoBehaviour
{
    private PlayKit_NPC npc;

    public void SaveNPCState()
    {
        // Get serialized history data
        string saveData = npc.SaveHistory();

        // Save to file
        string path = Path.Combine(
            Application.persistentDataPath,
            "npc_merchant_history.json"
        );
        File.WriteAllText(path, saveData);

        Debug.Log($"NPC state saved to: {path}");
    }
}

Load Conversation

public class NPCLoadSystem : MonoBehaviour
{
    private PlayKit_NPC npc;

    void Start()
    {
        npc = GetComponent<PlayKit_NPC>();

        // Try to load previous conversation
        LoadNPCState();
    }

    public void LoadNPCState()
    {
        string path = Path.Combine(
            Application.persistentDataPath,
            "npc_merchant_history.json"
        );

        if (File.Exists(path))
        {
            string saveData = File.ReadAllText(path);

            // Load history
            bool success = npc.LoadHistory(saveData);

            if (success)
            {
                Debug.Log("NPC state restored");
                Debug.Log($"History length: {npc.GetHistoryLength()}");
            }
            else
            {
                Debug.LogError("Failed to load NPC state");
            }
        }
    }
}

Check NPC Status

Check If Talking

var npc = GetComponent<PlayKit_NPC>();

if (npc.IsTalking)
{
    Debug.Log("NPC is thinking of a response...");
    // Disable interaction button
}
else
{
    // NPC is idle, can talk
}

Check If Ready

if (npc.IsReady)
{
    // NPC is initialized and ready to talk
    await npc.Talk("Hello");
}
else
{
    Debug.LogWarning("NPC not ready yet");
}

Get History Length

int messageCount = npc.GetHistoryLength();
Debug.Log($"Current conversation has {messageCount} messages");

// Limit history length to control costs
if (messageCount > 20)
{
    // Clean up old messages or summarize history
}

Practical Examples

Shop NPC

using Cysharp.Threading.Tasks;
using PlayKit_SDK;
using PlayKit_SDK.Public;
using UnityEngine;
using UnityEngine.UI;

public class ShopkeeperNPC : MonoBehaviour
{
    [SerializeField] private Text dialogText;
    [SerializeField] private Button talkButton;
    [SerializeField] private InputField playerInput;

    private PlayKit_NPC npc;

    void Start()
    {
        npc = GetComponent<PlayKit_NPC>();

        // Set shopkeeper character
        npc.SetCharacterDesign(@"
            You are a blacksmith named Tom.
            You sell weapons and armor.
            You are straightforward and speak concisely.
            When customers ask about items, you recommend suitable equipment.
        ");

        talkButton.onClick.AddListener(() => OnTalkButtonClicked().Forget());

        // Show initial greeting
        ShowGreeting().Forget();
    }

    async UniTaskVoid ShowGreeting()
    {
        string greeting = await npc.Talk(
            "New customer arrived",
            this.GetCancellationTokenOnDestroy()
        );

        dialogText.text = $"Tom: {greeting}";
    }

    async UniTaskVoid OnTalkButtonClicked()
    {
        string playerMessage = playerInput.text;
        if (string.IsNullOrEmpty(playerMessage)) return;

        talkButton.interactable = false;
        playerInput.text = "";

        dialogText.text = $"You: {playerMessage}\n\nTom is thinking...";

        string response = await npc.Talk(
            playerMessage,
            this.GetCancellationTokenOnDestroy()
        );

        if (response != null)
        {
            dialogText.text = $"You: {playerMessage}\n\nTom: {response}";
        }

        talkButton.interactable = true;
    }
}

Multi-NPC Conversation System

public class MultiNPCManager : MonoBehaviour
{
    private Dictionary<string, PlayKit_NPC> npcs = new Dictionary<string, PlayKit_NPC>();
    private PlayKit_NPC currentNPC;

    void Start()
    {
        CreateNPC("merchant", "You are a merchant who sells various goods");
        CreateNPC("guard", "You are a city gate guard responsible for protecting the city");
        CreateNPC("wizard", "You are a wise mage who masters various magical knowledge");
    }

    void CreateNPC(string npcId, string role)
    {
        GameObject npcObj = new GameObject($"NPC_{npcId}");
        npcObj.transform.SetParent(transform);

        PlayKit_NPC npc = npcObj.AddComponent<PlayKit_NPC>();
        npc.SetCharacterDesign(role);

        npcs[npcId] = npc;
    }

    public async UniTask TalkTo(string npcId, string message)
    {
        if (!npcs.ContainsKey(npcId))
        {
            Debug.LogError($"NPC '{npcId}' doesn't exist");
            return;
        }

        currentNPC = npcs[npcId];

        string response = await currentNPC.Talk(
            message,
            this.GetCancellationTokenOnDestroy()
        );

        Debug.Log($"{npcId}: {response}");
    }

    public void SaveAllNPCs()
    {
        foreach (var kvp in npcs)
        {
            string saveData = kvp.Value.SaveHistory();
            PlayerPrefs.SetString($"npc_{kvp.Key}", saveData);
        }

        PlayerPrefs.Save();
        Debug.Log("All NPC states saved");
    }

    public void LoadAllNPCs()
    {
        foreach (var kvp in npcs)
        {
            string key = $"npc_{kvp.Key}";
            if (PlayerPrefs.HasKey(key))
            {
                string saveData = PlayerPrefs.GetString(key);
                kvp.Value.LoadHistory(saveData);
            }
        }

        Debug.Log("All NPC states loaded");
    }
}

Reply Predictions

Generate suggested player responses after NPC replies. Useful for creating dialogue option UIs.

Enable Auto Predictions

var npc = GetComponent<PlayKit_NPC>();

// Enable automatic prediction generation
npc.GenerateReplyPrediction = true;
npc.PredictionCount = 4;  // 2-6 predictions

Subscribe to Prediction Event

void Start()
{
    var npc = GetComponent<PlayKit_NPC>();
    npc.OnReplyPredictionGenerated += HandlePredictions;
}

void HandlePredictions(string[] predictions)
{
    foreach (var option in predictions)
    {
        CreateDialogueButton(option);
    }
}

Manual Generation

// Generate predictions manually
string[] predictions = await npc.GenerateReplyPredictionsAsync(
    count: 3,
    cancellationToken: this.GetCancellationTokenOnDestroy()
);

foreach (var prediction in predictions)
{
    Debug.Log($"Suggested: {prediction}");
}

Performance Optimization

Limit History Length

Long conversation history increases API costs and response time:

public class NPCWithLimitedHistory : MonoBehaviour
{
    private PlayKit_NPC npc;
    private const int MAX_HISTORY = 15;

    void Start()
    {
        npc = GetComponent<PlayKit_NPC>();
        npc.SetCharacterDesign("You are a friendly NPC");
    }

    public async UniTask TalkWithLimit(string message)
    {
        // Check history length
        if (npc.GetHistoryLength() > MAX_HISTORY)
        {
            // Remove oldest conversations (keep system message)
            int toRemove = npc.GetHistoryLength() - MAX_HISTORY;
            npc.RevertChatMessages(toRemove);
        }

        // Normal conversation
        string response = await npc.Talk(
            message,
            this.GetCancellationTokenOnDestroy()
        );

        Debug.Log(response);
    }
}

Reset Long Conversations

For long-running NPCs, periodically reset conversations:

public void ResetNPCConversation()
{
    // Save current system prompt
    string systemPrompt = npc.CharacterDesign;

    // Clear history
    npc.ClearHistory();

    // Reset character
    npc.SetCharacterDesign(systemPrompt);

    Debug.Log("NPC conversation reset");
}

Best Practices

  1. Use NPC Client: For game characters, prefer PlayKit_NPC over manual history management
  2. Detailed character design: Clearly define NPC personality, background, and behavior in system prompts
  3. Check status: Use IsTalking to avoid sending new messages during ongoing conversations
  4. Save state: Save conversation history for important NPCs to provide continuous game experience
  5. Limit history length: Periodically clean up or summarize conversation history to control costs
  6. Error handling: Always check if return value is null
  7. Use streaming responses: Use TalkStream in dialogue UIs for better user experience

Next Steps