◉GEMINI LABJP
●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — deep-research-pro-preview-12-2025 shuts down 10/23, 6 days left. Move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — deep-research-pro-preview-12-2025 shuts down 10/23, 6 days left. Move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…
Articles/API / SDK
◈ API / SDK/2026-03-19Intermediate

Unity × Gemini API: Give NPCs Real Intelligence — Game Development Guide

Integrate Gemini API into Unity for intelligent NPC conversations. Dynamic dialogue beyond scripts, contextual responses, consistent character personality. Setup through production deployment.

UnityGemini API249NPCAI9Game DevelopmentC#

Unity × Gemini API: Intelligent NPC Conversations

Talk to a villager three times, hear the same line come back, and the world flattens into painted scenery. No amount of extra dialogue branches quite repairs that feeling.

An NPC that answers in its own words, reads the state of the world, and holds its personality across a conversation is a different experience altogether — and wiring Gemini API into Unity puts it within reach. Here's the build, end to end.

Why Gemini for Game NPCs

  • Ultra-Fast Responses (1.2 sec latency ideal for gaming)
  • Multimodal Capable (future: face expression recognition)
  • Cost-Effective (low API costs per response)
  • Character Consistency (system prompts maintain personality)

Gemini API Setup

Google Cloud Configuration

  1. Visit Google Cloud Console
  2. Create new project ("UnityGameAI")
  3. Enable Gemini API
  4. Generate API key (Credentials → New Key)

Unity API Key Management

using UnityEngine;
 
[CreateAssetMenu(menuName = "AI/Gemini Config")]
public class GeminiConfig : ScriptableObject
{
    [SerializeField]
    private string apiKey;
 
    public string ApiKey => apiKey;
}

Important: In production, access APIs through backend server, not client directly.

Gemini API Client Implementation

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Text;
using Newtonsoft.Json;
 
public class GeminiAPIClient : MonoBehaviour
{
    [SerializeField]
    private GeminiConfig config;
 
    private const string API_URL =
        "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent";
 
    public IEnumerator SendMessage(string userMessage, System.Action<string> onResponse)
    {
        var request = new GeminiRequest
        {
            contents = new[] {
                new Content {
                    role = "user",
                    parts = new[] { new Part { text = userMessage } }
                }
            }
        };
 
        string jsonData = JsonConvert.SerializeObject(request);
        byte[] postData = Encoding.UTF8.GetBytes(jsonData);
 
        string url = $"{API_URL}?key={config.ApiKey}";
        using (var www = new UnityWebRequest(url, "POST"))
        {
            www.uploadHandler = new UploadHandlerRaw(postData);
            www.downloadHandler = new DownloadHandlerBuffer();
            www.SetRequestHeader("Content-Type", "application/json");
 
            yield return www.SendWebRequest();
 
            if (www.result == UnityWebRequest.Result.Success)
            {
                var response = JsonConvert.DeserializeObject<GeminiResponse>(www.downloadHandler.text);
                string responseText = response.candidates[0].content.parts[0].text;
                onResponse?.Invoke(responseText);
            }
            else
            {
                Debug.LogError($"Error: {www.error}");
                onResponse?.Invoke("Error occurred");
            }
        }
    }
 
    // Serializable structures for API
    [System.Serializable]
    public class GeminiRequest { public Content[] contents; }
 
    [System.Serializable]
    public class Content
    {
        public string role;
        public Part[] parts;
    }
 
    [System.Serializable]
    public class Part { public string text; }
 
    [System.Serializable]
    public class GeminiResponse { public Candidate[] candidates; }
 
    [System.Serializable]
    public class Candidate { public Content content; }
}

NPC Character System

Base NPC Class

using UnityEngine;
using System.Collections.Generic;
 
public class NPCCharacter : MonoBehaviour
{
    [SerializeField]
    private string npcName;
 
    [SerializeField]
    private string characterPrompt;
 
    [SerializeField]
    private GeminiAPIClient apiClient;
 
    private List<(string role, string message)> conversationHistory;
 
    private void Start()
    {
        conversationHistory = new List<(string, string)>();
    }
 
    public void SendMessage(string userMessage)
    {
        conversationHistory.Add(("user", userMessage));
 
        var sb = new System.Text.StringBuilder();
        sb.AppendLine(characterPrompt);
        sb.AppendLine("\n【Conversation History】");
 
        foreach (var (role, msg) in conversationHistory)
        {
            sb.AppendLine($"{(role == "user" ? "Player" : npcName)}: {msg}");
        }
 
        StartCoroutine(apiClient.SendMessage(sb.ToString(), OnResponseReceived));
    }
 
    private void OnResponseReceived(string response)
    {
        conversationHistory.Add(("npc", response));
        // Display in UI
        DisplayDialogue(response);
    }
 
    private void DisplayDialogue(string text)
    {
        // Show NPC response in dialogue panel
        var panel = GetComponent<DialoguePanel>();
        panel.ShowMessage(npcName, text);
    }
}

Character Personality Definition

Key to realistic NPCs: Detailed system prompts

You are "Frodia," a wise 150-year-old wizard living in the eastern tower.

Personality: Calm, thoughtful, occasionally humorous
Catchphrase: "Hmm, I see..." "Do you understand?"

World Knowledge:
- Magic uses four elements (fire, water, wind, earth)
- Humans and elves coexist but have tensions
- Dark magic is forbidden

Conversation Rules:
1. Always respond in character
2. Use world knowledge
3. Keep responses 1-3 sentences
4. Answer player questions helpfully

Example:
Player: "I want to learn fire magic"
Frodia: "Good intention, young one. Fire magic harnesses flame element.
        First learn the basics. Will you practice?"

This level of detail ensures consistent, contextual responses.

Cost Optimization

Conversation History Management

private const int MAX_HISTORY_PAIRS = 5;
 
private string BuildConversationContext(string userMessage)
{
    // Keep only last 5 exchanges to limit tokens
    int startIndex = Mathf.Max(0, conversationHistory.Count - MAX_HISTORY_PAIRS * 2);
 
    var sb = new System.Text.StringBuilder();
    sb.AppendLine(characterPrompt);
 
    for (int i = startIndex; i < conversationHistory.Count; i++)
    {
        var (role, msg) = conversationHistory[i];
        sb.AppendLine($"{(role == "user" ? "Player" : npcName)}: {msg}");
    }
 
    sb.AppendLine($"Player: {userMessage}");
    return sb.ToString();
}

Response Caching

private Dictionary<string, string> responseCache = new();
 
public IEnumerator SendMessage(string userMessage, System.Action<string> onResponse)
{
    if (responseCache.TryGetValue(userMessage, out string cached))
    {
        onResponse?.Invoke(cached);
        yield break;
    }
 
    // API call... then cache result
    responseCache[userMessage] = response;
}

Fallback Responses

private string GetFallbackResponse(string userMessage)
{
    if (userMessage.Contains("hello"))
        return "Hello there, traveler. What brings you?";
    else if (userMessage.Contains("quest"))
        return "Quests... check the bulletin board.";
    else
        return "Hmm... (contemplating)";
}

Practical Examples

Merchant NPC

public class MerchantNPC : NPCCharacter
{
    [SerializeField]
    private List<ItemData> inventory;
 
    protected override string BuildCharacterPrompt()
    {
        var sb = new System.Text.StringBuilder();
        sb.AppendLine("You are Marco, a merchant. Shrewd, business-minded.");
        sb.AppendLine("\nCurrent Inventory:");
 
        foreach (var item in inventory)
            sb.AppendLine($"- {item.name}: {item.price} gold");
 
        sb.AppendLine("\nRecommend items to the customer.");
        return sb.ToString();
    }
}

Quest NPC

public class QuestGiverNPC : NPCCharacter
{
    [SerializeField]
    private List<QuestData> availableQuests;
 
    // Similar implementation:  Dynamic quest offers based on inventory
}

UI Integration

public class DialoguePanel : MonoBehaviour
{
    [SerializeField] private Text characterNameText;
    [SerializeField] private Text dialogueText;
 
    public void ShowMessage(string characterName, string message)
    {
        characterNameText.text = characterName;
        StartCoroutine(TypeMessage(message));
    }
 
    private IEnumerator TypeMessage(string message)
    {
        dialogueText.text = "";
        foreach (char c in message)
        {
            dialogueText.text += c;
            yield return new WaitForSeconds(0.05f);
        }
    }
}

Best Practices

  1. Character Consistency: Detailed, specific system prompts
  2. Response Length: Limit to 1-3 sentences for game pacing
  3. Token Management: Cache, limit history, use fallbacks
  4. Error Handling: Network failures need graceful degradation
  5. Testing: Thoroughly test NPC personalities before deployment

Security Notes

  • Never embed API keys in client code
  • Use backend proxy for production
  • Rate limit API calls per player
  • Validate all inputs before API calls

Wrapping up

Gemini API-powered NPCs transform game immersion. With proper configuration, your NPCs become genuinely intelligent conversationalists, not dialogue tree puppets.

Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

◈ API / SDK2026-10-14
When Your config Raises "Extra inputs are not permitted", Check the Spelling of Every Key
If a google-genai call fails with a pydantic ValidationError (extra_forbidden) the moment you pass a config, it is almost always a one-letter typo. Here is the output I reproduced, a small function that checks keys before the call, and how to tell why response.text came back as None.
◈ API / SDK2026-10-08
When a Deprecation Has No Shutdown Date: Re-Picking an Image Model on Three Conditions
gemini-nano-banana-2.1 went GA on October 6, and gemini-3.1-flash-image was deprecated with no shutdown date. Here is a table and a short script for choosing a replacement by resolution, reference images, and search grounding.
◈ API / SDK2026-09-29
The Size Column Said 'F6' and 'About A4' — I Let Gemini Read It, and Let a Table Do the Math
An artist's spreadsheet mixed 'F6', '41×31.8cm', and 'about A4' in one size column. Here is the small tool I built: Gemini reads each cell, a lookup table converts to centimeters, and the artwork image decides the orientation. Working code, step by step.