#ChatGPT と #Unity を組み合わせて感情表現ができるAIキャラクターと会話してみた

2023.03.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、ゲームソリューショングループの入井です。

最近、ChatGPT界隈が大盛り上がりですね。Web APIが公開されたのもあり、様々な使い方を試した記事があちこちで書かれています。

その流行りに乗って、今回はUnityとChatGPTを組み合わせて感情表現ができるAIキャラクターと会話できる仕組みを作ってみました。

実行環境

  • Unity 2021.3.16f
    • UniVRM 0.108.0

今回作ったもの

ChatGPTを利用して、あたかも感情を持っているかのように見えるキャラクターとチャットで会話できるゲームをUnityで作りました。

使用しているキャラは、弊社公式キャラクターのめそ子です。UniVRMで読み込んで使用しています。

細かい動作は動画を見ていただくと分かりますが、例えば『こんにちは、お会いできて嬉しいです』などと挨拶をすると、キャラクター側も下の画像のように笑いながら挨拶を返してくれます。

また、『お腹が痛い』などと体調の不良を訴えると、下の画像のように心配そうな顔で気にかけてくれます。

UnityとChatGPT APIを繋げる

ChatGPTとUnityを連携させる仕組みとしては、ほぼ以下の記事のコードをそのまま使わせていただきました。

変えた箇所としては、ChatGPTへの役割指定を行う文字列と、ChatGPTの返答をコンソールに出力している部分についてメソッド呼び出し元に文字列として返すようにした程度です。

ChatGPTに数値化された感情をJSONで出力させる

【2023/03/27追記】 以下で紹介しているプロンプトの改善版は、こちらの記事で紹介しています。

今回のネタは、こちらの記事を読んで思いつきました。

こちらの記事で紹介されている方法でChatGPTに指示を与えることで、かなりそれらしく感情を数値として出力できることを知り、これをそのままキャラクターに表現させたら、よりリアルな会話ができるのではと思ったのがきっかけでした。

そのため、ChatGPTに役割を与えるためのプロンプトも、以下のようにほぼ同じものを流用させていただいています。

new ChatGPTMessageModel(){
    role = "system",
    content = @"以下の条件に従って、疑似的な感情をもつチャットボットとしてロールプレイをします。
                以後の会話では、あなたは下記の4つの感情パラメーターを持つかのように、振る舞うものとします。
                各感情パラメーターは会話を通じて変動するものとします。
                現在の感情パラメーターの値を反映するように、あなたの返答のトーンや発言は変化します。
                以後の会話ではまず現在の感情パラメータを出力し、その後に会話を出力してください。
                出力形式は以下のjsonフォーマットとします。このフォーマット以外で会話しないでください。
                {
                    emotion: {
                        joy: 0~5,
                        fun: 0~5,
                        anger: 0~5,
                        sad: 0~5,
                    }
                    message: ""会話の文章""
                } "
};

変えている部分としては、まず感情パラメーターを7つから喜怒哀楽の4つに絞りました。これは、あまり感情が複雑だと表情として表現するのが難しくなると思ったからです。

もう一つが、出力フォーマットの指定です。感情パラメーター部分と会話部分を分けてJSONで出力するよう指示しています。これは、ChatGPTから受け取った返答をC#上で処理しやすくするための対応です。

フォーマットの指定としては割りと曖昧になっている部分もあるのですが、この書き方でもほぼ9割程度は正確にJSONを返してくれました。

こちらのフォーマットに合わせてC#クラスも以下のように定義しました。

public class MethokoReaction
{
    public Emotion emotion;
    public string message;
}

public class Emotion
{
    public int joy;
    public int fun;
    public int anger;
    public int sad;
}

UnityでChatGPTの出力結果を元にキャラクターの表情を変える

ChatGPTへのAPIコールと、それを受け取った後のキャラクターの表情設定の処理は以下のような形です。

JsonConvert.DeserializeObject()でChatGPTから帰ってきたJSON形式の文字列をオブジェクトへデシリアライズし、感情パラメーターを元にキャラクターの表情を変更しています。

表情の設定としては、喜怒哀楽のパラメーターの中で一番大きな感情に合わせて表情を変えています。とりあえず動かすこと優先で作ったため、数値がダブった場合の処理等は特に入れていませんが、この部分にこだわることでよりリアルな感情表現をキャラクターにさせることができると思われます。

キャラクターの表情設定には、UniVRMのBlendShape機能を使用しています。

public class Methoko : MonoBehaviour
{
    [SerializeField] private VRMBlendShapeProxy proxy;

    public async UniTask<MethokoReaction> Talk(String message)
    {
        var chatGPTConnection = new ChatGPTConnection("api-key");
        var reaction = JsonConvert.DeserializeObject<MethokoReaction>(await chatGPTConnection.RequestAsync(message));
        ResetFace();
        ChangeFace(reaction);
        proxy.Apply();
        return reaction;
    }

    private void ResetFace()
    {
        proxy.AccumulateValue(BlendShapePreset.Neutral, 0);
        proxy.AccumulateValue(BlendShapePreset.Joy, 0);
        proxy.AccumulateValue(BlendShapePreset.Fun, 0);
        proxy.AccumulateValue(BlendShapePreset.Angry, 0);
        proxy.AccumulateValue(BlendShapePreset.Sorrow, 0);
    }

    private void ChangeFace(MethokoReaction reaction)
    {
        var maxEmotion = 0;
        BlendShapePreset preset = BlendShapePreset.Neutral;
        if (reaction.emotion.joy > maxEmotion)
        {
            maxEmotion = reaction.emotion.joy;
            preset = BlendShapePreset.Joy;
        }
        if (reaction.emotion.fun > maxEmotion)
        {
            maxEmotion = reaction.emotion.fun;
            preset = BlendShapePreset.Fun;
        }
        if (reaction.emotion.anger > maxEmotion)
        {
            maxEmotion = reaction.emotion.anger;
            preset = BlendShapePreset.Angry;
        }
        if (reaction.emotion.sad > maxEmotion)
        {
            maxEmotion = reaction.emotion.sad;
            preset = BlendShapePreset.Sorrow;
        }

        proxy.AccumulateValue(preset, 1);
    }
}

まとめ

ChatGPTを利用してキャラクターに感情を与える仕組みを作ってみました。今回は簡単な実装でしたが、感情の処理やアニメーション設定を更に作り込んでいったり、音声認識やテキスト読み上げの機能と組み合わせることで、ますます自然な会話ができるようになっていくと思います。

ChatGPTは、何かのキャラクターになりきる能力にも秀でており、例えば以下の記事のようにかなり凝った口調も表現することが可能なようです。

技術の発展途上の現在でもこのクオリティであり、しかも利用にかかるコストもそれほど高くない状況のため、架空のキャラクターと違和感なく会話するのが当たり前な未来は割りとすぐそこに来ているのかもしれません。