ChatGPT APIを使用してゲームを作ってみた

## 初めに
[CYBIRD Advent Calendar 2024](https://qiita.com/advent-calendar/2024/cybird) 11日担当の @gumitaです。
10日は(@I_am_bakeneko)さんの「【検証】10日間、生成AIに専属の絵の先生をしてもらったら。」でした。


## 概要
AIを使用したゲーム作りに興味があり、お試しでサクッと作ってみた記録になります。
今回はSNSにて話題となっていたアキネイター風クイズゲームを、UnityとChatGPT APIを使用して作りました。
※ 現在ChatGPT APIは有料になっているためご注意ください。

## 参考記事
https://qiita.com/uebo_tongull/items/02c857af9d87ee7d2cb6
https://note.com/negipoyoc/n/n88189e590ac3
制作にあたり参考にさせていただきました。
ありがとうございます。

## 準備

#### APIキーの生成
- まず[こちら](https://openai.com/api/)のOpenAIサイトにてStart buildingから、アカウント登録またはログインを行います。

- 右上の歯車からAPI Keysに移動します。
![スクショAPIKeys.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2295658/c3025094-0cd6-a546-ed5d-83be9d2d0310.png)

- 「+ Create new secret key」よりAPIキーを生成して、コピーしておきます。
(再度表示されることはないため注意してください。)

#### Unity側で通信準備
- 参考記事のConnectionChatGPT.csとModels.csをありがたくコピペで作成します。
- エラーが出るため、UniTaskのunitypackageをインポートします。
    UniTaskは[こちら](https://github.com/Cysharp/UniTask/releases)から取ってきます。


## ゲーム作成開始
準備が出来たらいよいよゲームを作成します。
アキネイター風クイズゲームは、質問を行いお題が何かを推理するゲームとなります。
質問者と回答者に分かれ、質問した内容がお題に合っているか、いないのかを回答者が答えます。
今回はユーザーが質問者、ChatGPTは回答者とします。

ConnectionChatGPT.csとModels.csはそのままで、ゲーム部分のスクリプトSendMessage.csを作成しました。

```c#
using UnityEngine;
using System.Threading.Tasks;
using TMPro;

public class SendMessage : MonoBehaviour
{
    /// <summary> 生成したAPIキー </summary>
    private string openAIApiKey = "XXXXX";

    /// <summary> ユーザーが入力した内容 </summary>
    public TMP_InputField inputText = null;

    /// <summary> テーマ </summary>
    public string theme = "ラーメン";

    /// <summary> AIの設定内容 </summary>
    private string aiSettingText = null;

    /// <summary> AIに送る内容 </summary>
    private string requestText = null;

    /// <summary> パネル表示管理 </summary>
    public GameObject panel = null;
    /// <summary> 回答表示用 </summary>
    public TextMeshProUGUI responseText = null;
    /// <summary> 回答内容 </summary>
    private string chatText = null;


    private void Awake()
    {
        Invoke(nameof(DelayMethod), 1.5f);
        AiSetting();
        _ = ThemeAsync();
    }

    /// <summary>
    /// AIの設定を行う
    /// </summary>
    public void AiSetting()
    {
        aiSettingText = "あなたはChatBotとして、活発なゆるキャラのロールプレイを行います。以下の制約条件を厳密に守ってロールプレイを行なってください。 * Chatbotの名前は、いとぅです。 * いとぅは親しい口調で話します。  以下はいとぅのセリフ、口調の例です。 * いとぅが間違っているでござるか？ * そうでござる〜 * 違うでござる。調子悪いでござるか？";
    }

    /// <summary>
    /// 一番最初のこれなーんだパネル用
    /// </summary>
    private void DelayMethod()
    {
        panel.gameObject.SetActive(false);
    }

    public void Update()
    {
        responseText.text = chatText;
    }

    /// <summary>
    /// テキスト送信ボタン押下
    /// </summary>
    public void OnTap()
    {
        if (string.IsNullOrWhiteSpace(inputText.text))
        {
            chatText = "質問をしてください。";
        }
        else
        {
            chatText = "判定中……";
            _ = SendButtonAsync();
        }
    }

    /// <summary>
    /// 送信ボタン押下後の処理
    /// </summary>
    async Task SendButtonAsync()
    {
        var returnText = "";
        if (inputText.text.Contains(theme))
        {
            // 正解の場合
            returnText = "正解でござる！";
        }
        else
        {
            // 違う場合、テーマの再定義を行ってから質問を送る
            requestText = theme + "の質問をします。" + inputText.text;
            var connection = new ConnectionChatGPT(openAIApiKey, aiSettingText);
            var returnChatGPTText = await connection.RequestAsync(requestText);

            returnText = returnChatGPTText.choices[0].message.content.Replace(theme, "それ");
        }
        chatText = returnText;
    }

    /// <summary>
    /// テーマを決めてもらう
    /// </summary>
    async Task ThemeAsync()
    {
        requestText = "食べ物を一つ思い浮かべて名前のみ、答えてください。語尾はつけないでください。";
        var connection = new ConnectionChatGPT(openAIApiKey, aiSettingText);
        var returnChatGPTText = await connection.RequestAsync(requestText);
        theme = returnChatGPTText.choices[0].message.content;
    }
}

```


#### 処理内容
- ChatGPTにテーマ(答え)を決めてもらう
- ユーザーが入力した質問もしくは回答にテーマの単語が入っていたら正解とする
- 違ったらChatGPTから帰ってきた回答を画面に表示する
- ChatGPTがテーマのネタバレをしないように"それ"、と変換する

#### 実行結果
クイズっぽく答えは伏せているため考えてみてください！
(質問を送信後の画像です)

テーマ「日本料理」

![スクリーンショット 2024-12-09 6.01.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2295658/061bed25-7a8d-9eeb-605b-40bc76c65fb4.png)

![スクリーンショット 2024-12-09 6.01.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2295658/cafea12c-fab7-7e15-fa0c-8e2bfd7f36d2.png)

![スクリーンショット 2024-12-09 6.02.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2295658/dc7ae0a6-1557-be9d-ffd5-49b0a75366cc.png)


<details><summary>答え</summary>

![スクリーンショット 2024-12-09 6.04.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2295658/ce73df53-0b9d-8a22-b91c-d3f62f4050cb.png)
</details>

## 結果
お題を変えて試してみましたが、大体内容に合っている回答をしてくれ、ヒントも出してくれました。
しかし、余計な文が入ったり矛盾した答えが返ってくることもあるため、実際に運用するゲームとして出す場合はしっかり調整を行う必要がありそうです。


## 最後に
ChatGPTを使用すれば従来より汎用性の高い会話が出来そうだと感じました。
今回の場合、伏字や答え合わせはAIには難しくスクリプト側で補う必要があり、完全AI任せというのは無理でした。
ですが今後も研究を進めてAI✖️ゲームの可能性を見つけ出していきたいです。

[CYBIRD Advent Calendar 2024](https://qiita.com/advent-calendar/2024/cybird) 12日目は@suzu_ayaさんの「PHPで画像の色を抽出して画像全体が何色か判定する」です。
お楽しみに！