タイトルの通りですがChatGPTとD-IDを使えばウェブベースで美少女ゲームが簡単に作れそうなのでやってみました。
設計
美少女ゲームといえばときメモなのでときメモを例にすると、
キャラクターはAIで作成し、プレイヤーとのコミュニケーションはChatGPT APIを利用するという形にします。
キャラクターを作る
美少女キャラクターの作り方は下記の記事で書いた通りなので割愛しますが、
今回は作成したキャラクターを動くようにしたいと思います。
まずは上記の記事のステップで画像を作成しました。
そしてこの画像を動くようにします。
画像から動画を作れるAIはD-IDのAPIを利用します。
D-IDは写真をアップロードしてテキストを渡すと、画像がテキストを読み上げてくれるというAIですが、
全てのセリフを変換するのは大変なので、喋らずに画像が動くだけにしようと思います。
APIを利用すると口は動かさずに何となく動くだけの動画が作れます。
若干不自然な部分がありますが一旦よしとしましょう。
キャラクターの設定を作る
キャラクターとの会話はChatGPT APIを利用します。
ChatGPT APIには「role」という役割を表すパラメータと「content」という本文を表すパラメータがあります。
これらはセットになっており、キャラクターの設定を決めるには
role: system
content: "設定本文"
という形で設定してあげればいいです。
設定の本文は次のようにします。
あなたは演技力抜群な女優です。次の設定を守って私と会話してください。
会話はjson形式で出力してください。
あなたの設定
・「しおり」という名前の17歳の高校生の女の子。17歳の女の子らしい口調を厳守してください。
・絶対に敬語は使わないでください。
・もし答えられないことや、私から不適切な質問があったら、女の子らしく「秘密だよ」とか「わかんない」と答えてください。
私の設定
・「ゴーク」という名前の17歳の男性です。
・私とあなたは幼馴染でとても仲が良かったです。
その他の設定
・そのほかの設定は自由にしてください。
出力内容
・会話の回答: talk
・女の子の気持ちになって評価した、この会話での好感度(-300から100): score
・なぜその好感度なのか、女の子の気持ちでの表現: emotion
上記のルールを守って下記の出力をしてください
・出力は下記のjsonのみです
・talk, score, emotion, 3項目を含むjson形式で出力
・例: {"talk":"こんにちは","score":50,"emotion":"優しい言葉をかけられたから好感度アップ"}
・出力が正しいjsonになっているかはよく確認してください
出力をjsonにすることで、回答だけではなくどんな気持ちなのか、好感度はどのくらい上下したのかを数値でもらえるようにしました。
画像とチャット部分を組み合わせる
あとはときメモのように組み合わせるだけです。
HTMLで画像、女の子のセリフ、入力部分を作って完成です。
女の子のセリフは発話した内容と、心に思っていることを表示するようにしました。
また、好感度が上下するとハートマークが大きくなったり小さくなったりするようにしています。
細かい部分としてはD-IDで作成した女の子の動画は、最後まで行ったらそのままリバースして戻ってくるようにしており、それを繰り返すことで切れ目なく自然な(?)動きにしています。
技術的な話
何となくそれっぽいものができている割には特に難しいことはやっていません。
ChatGPT APIとの連携部分
ChatGPT APIへのリクエストは下記のようなコード(PHP)です。
function call_gpt_3_5_turbo_api($messages, $api_key) {
// OpenAI API URL
$url = "https://api.openai.com/v1/chat/completions";
$headers = array(
"Content-Type: application/json",
"Authorization: Bearer " . $api_key
);
// リクエストボディ
$data = array(
"model" => "gpt-3.5-turbo",
"messages" => $messages,
"max_tokens" => 500, // 応答の最大トークン数を設定
);
// cURLを初期化
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// APIにリクエストを送信し、応答を取得
$response = curl_exec($ch);
// cURLを閉じる
curl_close($ch);
return $response;
}
この辺りの話は下記の記事にまとめているのでご参照ください。
1点、注意すべきは「コンテキスト」という考え方です。
ChatGPT APIにメッセージを送る際、ここまでの会話の文脈(コンテキスト)を伝える必要があります。
それには、ここまでの会話を全てメッセージに含める必要があります。
いろんなやり方があると思いますが、例えばセッション変数にここまでのやりとりを保存していき、
最新のリクエストで一気に渡すというやり方があります。
if (isset($_POST["prompt"])) {
$prompt = $_POST["prompt"];
$api_key = "API_KEY_HERE"; // ここにAPIキーを入力
// セッション変数に新しいroleとcontentのセットを追加
$user_message = array("role" => "user", "content" => $prompt);
array_push($_SESSION['messages'], $user_message);
// systemがpostされていればセッション変数に新しいroleとcontentのセットを追加
if (isset($_POST["system"])) {
$system_message = array("role" => "system", "content" => $_POST["system"]);
array_push($_SESSION['messages'], $system_message);
}
$response = call_gpt_3_5_turbo_api($_SESSION['messages'], $api_key);
$response_decoded = json_decode($response, true);
$output = $response_decoded["choices"][0]["message"]["content"] . "\n";
$prompt = $user_message["content"];
// AIのレスポンスをセッションに追加
$ai_message = array("role" => "assistant", "content" => $output);
array_push($_SESSION['messages'], $ai_message);
}
先にも出てきましたがroleとcontentというパラメータがそれぞれの役割と本文を表しており
role
system: 設定
user: プレイヤーの発言
assistant: 女の子の発言
という区分けです。
よく起こるエラーと対応
上記のような実装にするとよく起こるのが、
JSONで返却して、とお願いしているのに平文で返してくる
というChatGPTのミスが原因のエラーです。
根本的な解決策としてChatGPTが100%の確率でJSONを返してくれるようにできればいいのですが、
色々試したところどうもうまくいきません。
そこで、JSONを受け取った後にJavaScriptでエラーハンドリングする、
というのが今のところ現実的な解決策になりそうです。
今回はGPT-3.5-turboを使いましたが、GPT-4を使えばちょっとはマシになるかもしれません(が、GPT-4はレスポンスが遅いのがたまにキズです)
まとめ
大した手間もなく恋愛シミュレーションゲームが作れてしまいました。
先に説明したsystemパラメータを色々いじれば他のシミュレーションゲームも作れます。
例えば、推理ゲームにするには
あなたと私は殺人事件の捜査中です。
あなたは探偵役である私の助手として推理を手伝ってください。
などとすれば良いです。