Unity接入ChatGPT实战指南:从零搭建智能对话系统

6次阅读
没有评论

共计 4127 个字符,预计需要花费 11 分钟才能阅读完成。

image.webp

技术背景

ChatGPT 在游戏开发中能大幅提升交互体验,以下是典型应用场景:

Unity 接入 ChatGPT 实战指南:从零搭建智能对话系统

  • 智能 NPC 对话:为角色生成动态回应,告别固定台词树
  • 玩家支持系统:自动解答游戏机制问题,降低客服压力
  • 剧情生成:根据玩家选择实时衍生分支故事线
  • 语言学习类游戏:构建自然语言练习环境

前置准备

  1. API 密钥申请
  2. 登录 OpenAI 平台(https://platform.openai.com)
  3. 在 API Keys 页面点击 ”Create new secret key”
  4. 建议设置使用限额(免费试用版有 18 美元额度)

  5. 费用须知

  6. gpt-3.5-turbo 模型每 1000 tokens 约 $0.002
  7. 1 个中文汉字≈1.33 个 tokens
  8. 可在 Billing 页面设置用量警报

核心实现

1. 构建网络请求

IEnumerator SendChatRequest(string prompt) {
    string apiUrl = "https://api.openai.com/v1/chat/completions";

    using(UnityWebRequest request = new UnityWebRequest(apiUrl, "POST")) {
        // 设置请求头
        request.SetRequestHeader("Content-Type", "application/json");
        request.SetRequestHeader("Authorization", "Bearer YOUR_API_KEY");

        // 构造请求体
        var requestBody = new {
            model = "gpt-3.5-turbo",
            messages = new[] {new { role = "user", content = prompt}
            },
            temperature = 0.7
        };

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

        request.uploadHandler = new UploadHandlerRaw(bodyRaw);
        request.downloadHandler = new DownloadHandlerBuffer();

        yield return request.SendWebRequest();

        if(request.result == UnityWebRequest.Result.Success) {ProcessResponse(request.downloadHandler.text);
        } else {Debug.LogError($"请求失败: {request.error}");
        }
    }
}

2. 流式响应处理

对于长回复,建议使用 Server-Sent Events(SSE):

IEnumerator HandleStreamingResponse(UnityWebRequest request) {while(!request.isDone) {if(request.downloadedBytes > 0) {
            string newData = request.downloadHandler.text;
            // 提取增量内容
            var delta = ParseDeltaContent(newData);
            UpdateUI(delta);
        }
        yield return null;
    }
}

完整代码示例

using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
using System.Text;

public class ChatGPTManager : MonoBehaviour {[SerializeField] string apiKey;
    [SerializeField] float timeout = 10f;

    public void AskGPT(string question) {StartCoroutine(SendRequestCoroutine(question));
    }

    IEnumerator SendRequestCoroutine(string prompt) {
        string apiUrl = "https://api.openai.com/v1/chat/completions";

        var requestBody = new {
            model = "gpt-3.5-turbo",
            messages = new[] {new { role = "system", content = "你是一个游戏中的 NPC"},
                new {role = "user", content = prompt}
            },
            max_tokens = 150
        };

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

        using(UnityWebRequest request = new UnityWebRequest(apiUrl, "POST")) {request.SetRequestHeader("Content-Type", "application/json");
            request.SetRequestHeader("Authorization", $"Bearer {apiKey}");

            request.uploadHandler = new UploadHandlerRaw(bodyRaw);
            request.downloadHandler = new DownloadHandlerBuffer();
            request.timeout = (int)timeout;

            float startTime = Time.time;
            yield return request.SendWebRequest();

            if(Time.time - startTime > timeout) {Debug.LogWarning("请求超时");
                yield break;
            }

            switch(request.result) {
                case UnityWebRequest.Result.Success:
                    var response = JsonUtility.FromJson<ChatGPTResponse>(request.downloadHandler.text);
                    Debug.Log(response.choices[0].message.content);
                    break;
                case UnityWebRequest.Result.ConnectionError:
                    // 网络连接错误处理
                    break;
                case UnityWebRequest.Result.ProtocolError:
                    // HTTP 错误处理
                    HandleHttpError(request.responseCode);
                    break;
            }
        }
    }

    void HandleHttpError(long responseCode) {switch(responseCode) {
            case 401:
                Debug.LogError("API 密钥无效");
                break;
            case 429:
                Debug.LogWarning("请求过于频繁");
                break;
            case 503:
                Debug.LogWarning("服务暂时不可用");
                break;
        }
    }
}

[System.Serializable]
public class ChatGPTResponse {public Choice[] choices;

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

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

性能优化

  1. 请求频率控制
  2. 使用 Time.deltaTime 计算请求间隔
  3. 对非关键对话启用队列机制

  4. 缓存策略

    private Dictionary<string, string> responseCache = new Dictionary<string, string>();
    
    string GetCachedResponse(string prompt) {if(responseCache.TryGetValue(prompt, out string cached)) {return cached;}
        return null;
    }

  5. 线程优化

  6. 使用 UniTask 替代协程降低开销
  7. 复杂 JSON 解析放在后台线程

避坑指南

  • 429 错误:遵守每分钟 3 次请求的限制(免费账户)
  • 敏感信息
  • 不要硬编码 API 密钥
  • 使用 Unity 的 PlayerPrefs 加密存储
  • 考虑使用后端中转服务
  • 上下文管理
    List<Message> conversationHistory = new List<Message>();
    
    void AddToHistory(string role, string content) {
        conversationHistory.Add(new Message { 
            role = role,
            content = content 
        });
    
        // 保持合理的历史长度
        if(conversationHistory.Count > 10) {conversationHistory.RemoveAt(0);
        }
    }

扩展思考

打字机效果实现

IEnumerator TypewriterEffect(string fullText, TextMeshProUGUI target) {
    target.text = "";
    foreach(char c in fullText) {
        target.text += c;
        yield return new WaitForSeconds(0.05f);
    }
}

本地化处理

  • 请求时指定语言参数:
    {
      "messages": [
        {
          "role": "system",
          "content": "请用简体中文回答"
        }
      ]
    }

实践建议

尝试为对话系统添加以下功能:
1. 情绪分析(通过 prompt 指定角色性格)
2. 话题引导机制
3. 基于玩家选择的动态剧情生成

最后留个思考题:如何设计一个能记住玩家游戏进度的对话系统?可以考虑将会话 ID 与玩家存档关联,并在每次对话时传入关键剧情节点信息。

正文完
 0
评论(没有评论)