UnityでOpenAI API(ChatGPT)を使ってみる

## 初めに
> この内容では、ChatGPT と Unity の統合について説明し、Unity 環境内で ChatGPT の機能を使用してインタラクティブでインテリジェントなアプリケーションを作成する方法を示します。
> 
> デモリポジトリは[こちら](https://github.com/Ugee0810/Unity-ChatGPT-Intergration-Demo)

## 🔐 OpenAI APIキーを取得する
OpenAIアカウントを作成し、APIキーを取得します。[OpenAIの API docs](https://platform.openai.com/docs/overview)からキーを発行してもらうことができます。
1. OpenAI ログイン
2. カード登録が必須なので、[Billing](https://platform.openai.com/settings/organization/billing/overview)タブに入場してカード登録する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748634/1577ff03-7581-6de5-c11a-748e8637576b.png)
追加で過度な料金発生を予防するためにLimitsタブに移動してUsage limitsを設定する
3. [View API Keys](https://platform.openai.com/api-keys)に入場し、New Secret Keyを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748634/f7a21351-27ca-d235-cfbe-1d27f426354b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748634/8519ead2-2087-0fbd-7274-53b14ab5c1d9.png)
生成できたら、キー値をコピーする（**生成されたキーは再照会できないので注意**）
4. Key値が正しく生成されたかを確認する
- 保存されたKey確認
- サポート中のモデルを確認すること(当該文書では「gpt-3.5-turbo」Modelを使用)

### Windows
1. cURLのインストール確認
```
curl --version
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748634/291a329c-0ec1-f813-f4df-1b1cfd3b4434.png)
2. cURL命令作成
```
curl https://api.openai.com/v1/chat/completions ^
-H "Content-Type: application/json" ^
-H "Authorization: Bearer YOUR_API_KEY" ^
-d "{\"model\": \"gpt-3.5-turbo\", \"messages\": [{\"role\": \"user\", \"content\": \"Say this is a test\"}], \"max_tokens\": 5}"
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748634/0be0dc65-6462-1e5b-c5e8-45c141f4d514.png)
下線にKeyを入力

### MacOS
```
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
    "model": "gpt-3.5-turbo",
    "messages": [
        {
            "role": "user",
            "content": "Say this is a test"
        }
    ],
    "max_tokens": 5
}'
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748634/4a0f0c0a-82d1-a00b-0ce2-a43a67888b09.png)

## ⚙️ パッケージのインストールと通信の準備
1. パッケージマネージャでurlでNuGetForUnityをインストール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748634/edfd3d9c-4c08-12a0-cf1f-b12a9445a6df.png)
2. NuGet PackagesタブでNewtonsoft.Jsonを検索してインストール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748634/600e4db1-8b09-61f9-ac63-fc35d1fb0b00.png)

## 📜 スクリプト作成
```csharp
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
using Newtonsoft.Json; // Use Newtonsoft.Json

public class OpenAIChatGPT : MonoBehaviour
{
    private string apiKey = "YOUR_API_KEY"; // Replace with an actual API key
    private string apiUrl = "https://api.openai.com/v1/chat/completions";

    public IEnumerator GetChatGPTResponse(string prompt, System.Action<string> callback)
    {
        // Setting OpenAI API Request Data
        var jsonData = new
        {
            model = "gpt-3.5-turbo",
            messages = new[]
            {
                new { role = "user", content = prompt }
            },
            max_tokens = 20
        };

        string jsonString = JsonConvert.SerializeObject(jsonData);

        // HTTP request settings
        UnityWebRequest request = new UnityWebRequest(apiUrl, "POST");
        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonString);
        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.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.LogError("Error: " + request.error);
        }
        else
        {
            var responseText = request.downloadHandler.text;
            Debug.Log("Response: " + responseText);
            // Parse the JSON response to extract the required parts
            var response = JsonConvert.DeserializeObject<OpenAIResponse>(responseText);
            callback(response.choices[0].message.content.Trim());
        }
    }

    public class OpenAIResponse
    {
        public Choice[] choices { get; set; }
    }

    public class Choice
    {
        public Message message { get; set; }
    }

    public class Message
    {
        public string role { get; set; }
        public string content { get; set; }
    }
}
```

```csharp
using UnityEngine;

public class ChatGPTExample : MonoBehaviour
{
    [SerializeField, TextArea(3, 5)] private string prompt;

    void Start()
    {
        OpenAIChatGPT chatGPT = gameObject.AddComponent<OpenAIChatGPT>();
        StartCoroutine(chatGPT.GetChatGPTResponse(prompt, OnResponseReceived));
    }

    void OnResponseReceived(string response)
    {
        Debug.Log("ChatGPT Response: " + response);
    }
}
```

## 📗 使用例
プロンプト質問 : In the extended virtual world, what is the newly coined word that combines "meta," which means virtual and transcendent, and "universe," which means world and universe?

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748634/779a3572-98ba-14fb-7276-dc4471bbd38f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748634/0efe936f-e950-f697-f289-102f1be70489.png)

## 🤔 期待できる部分
- [生成補助機能](https://youtu.be/-nnBrHG4mg4?si=mAgEykxHpwSM9btB)
- 教育用クイズゲーム
- ユーザーカスタマイズ型ストーリージェネレータ
- ARベースの教育プラットフォーム
    - AR環境で3Dモデルを表示して学習テーマを視覚化
    - ChatGPTによるリアルタイム質問応答および説明提供
- ARベースの観光ガイド
    - ARを通じて観光地情報を視覚的に提供
    - ChatGPTを使用して観光地に関する追加情報を提供し、質問に答える
- ARベースのショッピングヘルパー
    - ARを通じて製品情報を視覚的に提供
    - ChatGPTを使用して製品説明および質問応答