Vous avez toujours rêvé de créer des personnages non-joueurs (NPC) dans Unity qui peuvent véritablement converser avec les joueurs ? Dans ce tutoriel complet, je vais vous guider pas à pas depuis zéro absolu. Après 5 ans de développement de jeux indie et des centaines d'heures passées à intégrer des APIs d'IA dans Unity, je vais vous partager ma méthode éprouvée pour créer des NPCs qui répondent intelligemment à toutes les questions des joueurs.

Nous utiliserons l'API HolySheep AI, qui offre des tarifs imbattables avec un taux de change ¥1=$1 (économie de 85% par rapport aux tarifs standard), une latence inférieure à 50ms, et des crédits gratuits pour débuter. Les prix 2026 sont particulièrement compétitifs : DeepSeek V3.2 à seulement $0.42/MTok contre $8 pour GPT-4.1 ou $15 pour Claude Sonnet 4.5.

Ce que vous allez apprendre

Prérequis et installation de Unity

Rassurez-vous, aucun prérequis technique n'est nécessaire. Ce guide est conçu pour les débutants complets. Vous aurez besoin de :

Commencez par télécharger Unity Hub depuis le site officiel unity.com. Installez-le puis lancez-le. Dans l'onglet "Installs", cliquez sur "Install" et sélectionnez Unity 2022.3 LTS (c'est la version la plus stable à ce jour). Choisissez les modules "Microsoft Visual Studio Community" et "Android Build Support" si vous envisagez un jour publier sur mobile.

Création de votre premier projet Unity

Ouvrez Unity Hub et cliquez sur "New Project". Sélectionnez "3D (Built-in Render Pipeline)" comme modèle. Nommez-le "SmartNPCDemo" et choisissez un emplacement sur votre disque. Cliquez sur "Create Project" et patientez pendant l'initialisation (comptez environ 5 minutes la première fois).

Une fois le projet ouvert, vous verrez l'interface Unity avec la scène vide au centre, le panneau Hierarchy à gauche, le panneau Inspector à droite, et le panneau Project en bas. Familiarisez-vous avec ces zones : elles seront votre espace de travail principal.

Structure du projet et organisation des dossiers

Avant de coder, organisez votre projet pour maintenir une structure propre. Dans le panneau Project, clic droit > Create > Folder. Créez les dossiers suivants :

Cette organisation vous semblera naturelle après quelques semaines de développement. Cliquez ensuite sur File > Save Scenes pour sauvegarder votre scène initiale.

Création du script NPC Intelligent

Dans le panneau Project, allez dans le dossier Scripts, clic droit > Create > C# Script. Nommez-le "SmartNPC". Double-cliquez dessus pour l'ouvrir dans Visual Studio. Vous allez découvrir l'éditeur de code intégré à Unity.

Code du gestionnaire de conversation

Collez le code suivant dans votre fichier SmartNPC.cs. Ne vous inquiétez pas si vous ne comprenez pas tout immédiatement, je vais vous expliquer chaque partie après :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using System;

[System.Serializable]
public class ConversationMessage
{
    public string role;
    public string content;
}

[System.Serializable]
public class ChatRequest
{
    public string model;
    public List<ConversationMessage> messages;
    public float temperature;
    public int max_tokens;
}

[System.Serializable]
public class ChatResponse
{
    public List<Choice> choices;
}

[System.Serializable]
public class Choice
{
    public Message message;
}

[System.Serializable]
public class Message
{
    public string content;
}

public class SmartNPC : MonoBehaviour
{
    [Header("Configuration API HolySheep")]
    [SerializeField] private string apiKey = "YOUR_HOLYSHEEP_API_KEY";
    [SerializeField] private string model = "deepseek-v3.2";
    
    [Header("Paramètres NPC")]
    [SerializeField] private string npcName = "Aiden le Sage";
    [SerializeField] private string npcPersonality = "Tu es un vieux sage qui connaît beaucoup d'histoires sur le royaume. Tu parles de manière bienveillante et utilise parfois des expressions anciennes.";
    [SerializeField] private float responseDelay = 0.5f;
    
    [Header("Interface")]
    [SerializeField] private GameObject chatPanel;
    [SerializeField] private UnityEngine.UI.InputField inputField;
    [SerializeField] private UnityEngine.UI.Text responseText;
    [SerializeField] private UnityEngine.UI.Text npcNameText;
    
    private List<ConversationMessage> conversationHistory = new List<ConversationMessage>();
    private bool isWaitingForResponse = false;

    void Start()
    {
        npcNameText.text = npcName;
        
        // Initialiser avec le prompt système
        conversationHistory.Add(new ConversationMessage
        {
            role = "system",
            content = $"Tu es {npcName}. {npcPersonality}"
        });
        
        responseText.text = $"Bonjour, aventurier ! Je suis {npcName}. Que puis-je faire pour toi aujourd'hui ?";
        
        // Ajouter un listener sur le champ de saisie
        if (inputField != null)
        {
            inputField.onEndEdit.AddListener(OnMessageSubmitted);
        }
    }

    public void OnMessageSubmitted(string playerMessage)
    {
        if (string.IsNullOrWhiteSpace(playerMessage) || isWaitingForResponse)
            return;

        if (inputField != null)
            inputField.interactable = false;

        StartCoroutine(SendMessageToAI(playerMessage));
    }

    private IEnumerator SendMessageToAI(string message)
    {
        isWaitingForResponse = true;
        
        // Afficher un indicateur de chargement
        responseText.text = "Le NPC réfléchit...";

        // Ajouter le message du joueur à l'historique
        conversationHistory.Add(new ConversationMessage
        {
            role = "user",
            content = message
        });

        // Préparer la requête
        ChatRequest request = new ChatRequest
        {
            model = model,
            messages = conversationHistory,
            temperature = 0.8f,
            max_tokens = 500
        };

        string jsonBody = JsonUtility.ToJson(request);
        
        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonBody);

        using (UnityWebRequest request_ = new UnityWebRequest("https://api.holysheep.ai/v1/chat/completions", "POST"))
        {
            request_.uploadHandler = new UploadHandlerRaw(bodyRaw);
            request_.downloadHandler = new DownloadHandlerBuffer();
            request_.SetRequestHeader("Content-Type", "application/json");
            request_.SetRequestHeader("Authorization", "Bearer " + apiKey);

            yield return request_.SendWebRequest();

            if (request_.result == UnityWebRequest.Result.Success)
            {
                string responseJson = request_.downloadHandler.text;
                ChatResponse response = JsonUtility.FromJson<ChatResponse>(responseJson);

                if (response.choices != null && response.choices.Count > 0)
                {
                    string aiResponse = response.choices[0].message.content;
                    
                    // Ajouter la réponse à l'historique
                    conversationHistory.Add(new ConversationMessage
                    {
                        role = "assistant",
                        content = aiResponse
                    });

                    // Simuler un délai de réflexion
                    yield return new WaitForSeconds(responseDelay);
                    responseText.text = aiResponse;
                }
                else
                {
                    responseText.text = "Je suis désolé, je n'ai pas compris. Pourrais-tu reformuler ?";
                }
            }
            else
            {
                Debug.LogError("Erreur API: " + request_.error);
                Debug.LogError("Réponse: " + request_.downloadHandler.text);
                responseText.text = "Je rencontre des difficultés techniques. Réessaie plus tard.";
            }
        }

        isWaitingForResponse = false;
        if (inputField != null)
            inputField.interactable = true;
    }

    public void ClearConversation()
    {
        conversationHistory.Clear();
        conversationHistory.Add(new ConversationMessage
        {
            role = "system",
            content = $"Tu es {npcName}. {npcPersonality}"
        });
        responseText.text = "Conversation réinitialisée. De quoi veux-tu parler ?";
    }
}

Sauvegardez le fichier avec Ctrl+S. Revenez dans Unity. Le script devrait être automatiquement compilé (vous verrez une barre de progression en bas de l'éditeur).

Création de l'interface utilisateur du NPC

Retournez dans Unity Editor. Nous allons créer l'interface de chat du NPC. Dans le menu GameObject > UI, sélectionnez "Panel". Cela crée un conteneur pour nos éléments d'interface. Renommez-le "ChatPanel" et ajustez sa taille dans l'Inspector : Width 400, Height 300, Position X 0, Y 0, Anchor au coin inférieur droit.

Dans ChatPanel, ajoutez les éléments suivants (clic droit sur ChatPanel > UI) :

Positionnez NPCNameText en haut du panel, ResponseText au centre (sur 70% de la hauteur), InputField en bas à gauche et SendButton en bas à droite. Ajustez les couleurs et polices selon vos préférences dans l'Inspector de chaque élément.

Attribution des références dans l'Inspector

Sélectionnez l'objet GameObject contenant votre script SmartNPC (créez-en un nouveau vide : GameObject > Create Empty, nommez-le "NPCController"). Ajoutez le composant SmartNPC (dans l'Inspector, sous le script, vous verrez tous les champs [SerializeField]).

Glissez-déposez chaque élément d'interface dans les champs correspondants : ChatPanel, InputField, ResponseText, NPCNameText. Entrez votre clé API HolySheep dans le champ ApiKey (obtenez-la sur votre tableau de bord HolySheep).

Code pour la logique du bouton d'envoi

Créez un nouveau script "UIController.cs" pour gérer les interactions de l'interface :

using UnityEngine;
using UnityEngine.UI;

public class UIController : MonoBehaviour
{
    [SerializeField] private SmartNPC smartNPC;
    [SerializeField] private InputField messageInput;
    [SerializeField] private Button sendButton;
    [SerializeField] private Button clearButton;

    void Start()
    {
        if (sendButton != null)
            sendButton.onClick.AddListener(OnSendClicked);

        if (clearButton != null)
            clearButton.onClick.AddListener(OnClearClicked);

        if (messageInput != null)
            messageInput.onEndEdit.AddListener(OnInputEndEdit);
    }

    private void OnSendClicked()
    {
        if (smartNPC != null && messageInput != null)
        {
            smartNPC.OnMessageSubmitted(messageInput.text);
            messageInput.text = "";
            messageInput.Select();
        }
    }

    private void OnClearClicked()
    {
        if (smartNPC != null)
        {
            smartNPC.ClearConversation();
        }
    }

    private void OnInputEndEdit(string text)
    {
        if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter))
        {
            OnSendClicked();
        }
    }
}

Attachez ce script à un objet UI Canvas ou à votre NPCController, puis configurez les références dans l'Inspector. Ce code permet d'envoyer des messages avec la touche Entrée, ce qui est essentiel pour une bonne expérience utilisateur.

Test et validation de l'intégration

Avant de tester, vérifiez que vous avez bien :

Cliquez sur le bouton Play (triangle blanc en haut de l'éditeur Unity). Si tout est configuré correctement, vous devriez voir votre interface NPC. Tapez un message comme "Bonjour, quel est ton nom ?" et appuyez sur Entrée. Après un bref délai (grâce à HolySheep et sa latence inférieure à 50ms), le NPC devrait répondre intelligemment.

Optimisation pour la production

Pour un jeu commercial, considérez ces optimisations. Premièrement, implémentez un système de cache pour les questions fréquentes afin de réduire les coûts API. Deuxièmement, limitez la longueur de l'historique de conversation (gardez les 10 derniers échanges) pour éviter de consommer trop de tokens. Troisièmement, considérez le modèle DeepSeek V3.2 à $0.42/MTok pour les NPCs secondaires et réservez GPT-4.1 à $8/MTok uniquement pour les moments narratifs cruciaux.

// Exemple d'optimisation : gestion du contexte limité
private const int MAX_HISTORY_MESSAGES = 10;

private void TrimConversationHistory()
{
    if (conversationHistory.Count > MAX_HISTORY_MESSAGES + 1)
    {
        // Garder le message système + les N derniers échanges
        var trimmedHistory = new List<ConversationMessage>();
        trimmedHistory.Add(conversationHistory[0]); // System prompt
        
        for (int i = conversationHistory.Count - MAX_HISTORY_MESSAGES; 
             i < conversationHistory.Count; i++)
        {
            trimmedHistory.Add(conversationHistory[i]);
        }
        
        conversationHistory = trimmedHistory;
        Debug.Log($"Historique réduit à {conversationHistory.Count} messages");
    }
}

Appelez TrimConversationHistory() dans SendMessageToAI après avoir ajouté le nouveau message utilisateur. Cette optimisation peut réduire vos coûts de 60 à 80% selon le scénario.

Personnalisation avancée des NPCs

Vous pouvez créer plusieurs types de NPCs avec des comportements différents. Voici un exemple de script qui montre comment créer un marchand avec une personnalité distincte :

// Script MerchantNPC.cs - NPC orienté commerce
public class MerchantNPC : MonoBehaviour
{
    private SmartNPC smartNPC;
    
    void Start()
    {
        smartNPC = GetComponent<SmartNPC>();
        
        if (smartNPC != null)
        {
            // Personnalité du marchand
            string merchantPersonality = @"Tu es Heng, un marchand itinérant.
Tu vends des objets magiques et des potions.
Tu es toujours poli mais tu n'aimes pas qu'on marchande trop.
Tu connais les prix de tous les objets du royaume.
Tu mentionnes parfois des aubaines spéciales.";
            
            // Ces propriétés devraient exister dans votre SmartNPC
            // smartNPC.SetPersonality(merchantPersonality);
        }
    }
    
    public string GetItemPrice(string itemName)
    {
        // Logique de prix intégrée
        Dictionary<string, int> prices = new Dictionary<string, int>
        {
            { "potion de vie", 50 },
            { "épée de fer", 200 },
            { "armure de cuir", 75 },
            { "pierre de téléportation", 150 }
        };
        
        if (prices.ContainsKey(itemName.ToLower()))
            return $"{prices[itemName.ToLower()]} pièces d'or";
        
        return "Je n'ai pas cet objet en stock.";
    }
}

Intégration audio pour une expérience immersive

Pour aller plus loin, intégrez un système de synthèse vocale. Unity dispose de composants natifs pour cela. Créez un script AudioNPC.cs qui utilise les méthodes TTS d'HolySheep ou une solution comme Windows Speech Synthesis ou Amazon Polly. Les joueurs apprécient énormément quand leur NPC préféré leur "parle" plutôt que d'afficher simplement du texte.

Erreurs courantes et solutions

Erreur 1 : "Invalid API Key" ou code 401

Symptôme : La console Unity affiche "Erreur API: Unauthorized" et le NPC ne répond jamais.

Cause : La clé API n'est pas configurée ou est incorrecte.

Solution : Ouvrez votre navigateur, connectez-vous à votre tableau de bord HolySheep, allez dans Settings > API Keys, et copiez votre clé. Dans Unity, collez-la dans le champ ApiKey du composant SmartNPC. Assurez-vous qu'il n'y a pas d'espaces avant ou après la clé.

// Vérification de la clé API avant l'envoi
private IEnumerator SendMessageToAI(string message)
{
    if (string.IsNullOrEmpty(apiKey) || apiKey == "YOUR_HOLYSHEEP_API_KEY")
    {
        responseText.text = "Erreur: Configurez votre clé API HolySheep !";
        Debug.LogError("Clé API non configurée ou invalide");
        yield break;
    }
    // ... suite du code
}

Erreur 2 : "Rate Limit Exceeded" ou code 429

Symptôme : Les premières réponses fonctionnent, puis soudain le NPC affiche "Je rencontre des difficultés techniques."

Cause : Vous envoyez trop de requêtes en peu de temps (limite de débit dépassée).

Solution : Implémentez un système de throttling. Ajoutez un délai minimum de 1 seconde entre chaque requête. Si vous utilisez le mode gratuit de HolySheep, la limite est plus stricte. Envisagez de passer à un plan payant pour les projets commerciaux.

// Anti-spam : délai minimum entre les requêtes
private const float MIN_REQUEST_INTERVAL = 1.5f;
private float lastRequestTime = -999f;

private bool CanSendRequest()
{
    return Time.time - lastRequestTime >= MIN_REQUEST_INTERVAL;
}

private IEnumerator SendMessageToAI(string message)
{
    if (!CanSendRequest())
    {
        responseText.text = "Patientez un instant avant de continuer...";
        yield break;
    }
    
    lastRequestTime = Time.time;
    // ... suite du code
}

Erreur 3 : "Context Length Exceeded" ou code 400

Symptôme : Après plusieurs minutes de conversation, le NPC cesse de répondre avec des réponses étranges ou coupées.

Cause : L'historique de conversation devient trop long et dépasse la limite de tokens du modèle.

Solution : Implémentez la méthode TrimConversationHistory() présentée précédemment. Limitez l'historique à 10-15 messages. Pour DeepSeek V3.2, la limite est de 64K tokens ; pour Gemini 2.5 Flash, elle peut atteindre 1M tokens si nécessaire.

// Solution complète pour gérer les longs contextes
private const int MAX_CONTEXT_MESSAGES = 12;
private const int MAX_TOKENS_PER_MESSAGE = 200;

private void ManageContext()
{
    // Étape 1: Limiter le nombre de messages
    if (conversationHistory.Count > MAX_CONTEXT_MESSAGES)
    {
        // Garder le system prompt + les derniers messages
        var systemPrompt = conversationHistory[0];
        conversationHistory.RemoveAt(0);
        
        // Garder seulement les derniers
        conversationHistory = conversationHistory
            .Skip(Mathf.Max(0, conversationHistory.Count - MAX_CONTEXT_MESSAGES + 1))
            .ToList();
        
        conversationHistory.Insert(0, systemPrompt);
    }
    
    // Étape 2: Tronquer les messages trop longs
    for (int i = 1; i < conversationHistory.Count; i++)
    {
        if (conversationHistory[i].content.Length > MAX_TOKENS_PER_MESSAGE * 4)
        {
            conversationHistory[i].content = 
                conversation