猫のChatGPT
名付けて、CatGPT を作りました。
ChatGPTのAPIを使ってCatGPT を作ったにゃん pic.twitter.com/SGVLss7ZZK
— たつや (@tatsuya1970) April 8, 2023
OpenAI
OpenAIのAPIキーを取得する。
こちらを参照
https://qiita.com/tatsuya1970/items/c04cb7b6d07c4cf51007#3-openai
Live2D
まずは、UnityHubで "2D" のプロジェクトを新規作成します。
Unityが立ち上がったら、
2次元のイラストを自由に動かすことができるLive2DのSDKである「Cubism SDK for Unity」をプロジェクトにインポートします。
ダウンロードはこちらから
https://www.live2d.com/download/cubism-sdk/download-unity/
こんなエラーが出ますが、多分無視して大丈夫と思います。
ArgumentNullException: Value cannot be null.
Parameter name: shader
Live2Dのウェブサイトからキャラクターをダウンロードします、
https://www.live2d.com/download/sample-data/
初音ミクなどありますが、
今回はCatGPTなので「黒猫のひじき」をダウンロード。
そして、ダウンロードしたキャラクターのフォルダごとUnity の Project にそのままドラッグします。
Prefab が生成されるので、Sceneに適当に置きます。
Unityの設定
UI
アニメーション
「黒猫のひじき」の8個のモーション全部を 黄色のノード(Idle) に繋げる。
モーションの名前や左側のParameterの名前は1からの8数字にした。
(今回はランダムでモーションを選ぶためモーションの名前が数字の方がコーディングしやすい)
Idle と各モーションの Inspector は
Idle -> 各モーション
モーション -> Idle はデフォルトのまま
スクリプト
汚いコードですが晒します。
今どきは、UniTaskやCoroutineを使わずasync/await とかを使うのでしょうけど・・・
まず、JSON Object アセットをインストールします。
https://assetstore.unity.com/packages/tools/input-management/json-object-710?locale=ja-JP
で、以下のスクリプトを入力
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System;
using Defective.JSON;
using TMPro;
public class answer : MonoBehaviour
{
private Animator animator;
public Button button; // OKボタン
public GameObject inputField; //ユーザー入力するフィールド
[SerializeField]
TextMeshProUGUI responseText;
//OpenAIのAPIにリクエストするとき必要
[Serializable]
public class body
{
public string model;
public Transfers[] messages;
}
[Serializable]
public class Transfers
{
public string role;
public string content;
}
public void OnClick()
{
animator = GameObject.Find("hijiki").GetComponent<Animator>();
//ユーザーが投稿した内容を取得
string mes = inputField.GetComponent<Text>().text;
var url = "https://api.openai.com/v1/chat/completions";
// ボットの性格を設定
string chara = "あなたは猫ですが人間の言葉も理解できます。語尾に「にゃ」をつけるなど猫っぽい話し方をしてください。";
string itemJson
= "{ \"model\": \"gpt-3.5-turbo\"," +
"\"messages\": [" +
"{ \"role\": \"system\", \"content\": \"" + chara +"\"}," +
"{ \"role\": \"user\", \"content\": \"" + mes + "\"}]}";
body item = JsonUtility.FromJson<body>(itemJson);
string serialisedItemJson = JsonUtility.ToJson(item);
// Debug.Log("serialisedItemJson " + serialisedItemJson);
StartCoroutine(post(url, serialisedItemJson));
}
private IEnumerator post(string url, string body)
{
var request = new UnityWebRequest(url, "POST");
byte[] postData = System.Text.Encoding.UTF8.GetBytes(body);
request.uploadHandler = new UploadHandlerRaw(postData);
request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
// OpenAi のAPIキー
request.SetRequestHeader("Authorization", "Bearer OpenAIのAPIキー");
yield return request.SendWebRequest();
// 通信エラーチェック
if (request.isNetworkError)
{
Debug.Log(request.error);
animator.SetBool("2", true); //機嫌が悪いアニメーション
yield return new WaitForSeconds(0.8f);
animator.SetBool("2", false);
}
else
{
Debug.Log(request.responseCode);
if (request.responseCode == 200 || request.responseCode == 201)
{
string text = request.downloadHandler.text;
Debug.Log(text);
JSONObject json = new JSONObject(text);
JSONObject choices = json.GetField("choices");
JSONObject message = choices[0].GetField("message");
string content = message.GetField("content").ToString();
Debug.Log(content);
string motion = UnityEngine.Random.Range(1,9).ToString(); //モーションをランダムで選ぶ
animator.SetBool(motion, true);
responseText.text = content;
yield return new WaitForSeconds(3.0f);
animator.SetBool(motion, false);
}
else
{
responseText.text = request.responseCode + "エラーだよ!!\nどこかが間違ってるよ。";
Debug.Log("エラー");
animator.SetBool("2", true); //機嫌が悪いモーション
yield return new WaitForSeconds(0.8f);
animator.SetBool("2", false);
yield return new WaitForSeconds(3.0f);
}
}
}
}
(コード説明) 猫のキャラクター設定
上のコードの中間部分の変数charaで指定してます。
該当部分を抽出しますと、
string chara = "あなたは猫ですが人間の言葉も理解できます。語尾に「にゃ」をつけるなど猫っぽい話し方をしてください。";
string itemJson
= "{ \"model\": \"gpt-3.5-turbo\"," +
"\"messages\": [" +
"{ \"role\": \"system\", \"content\": \"" + chara +"\"}," +
"{ \"role\": \"user\", \"content\": \"" + mes + "\"}]}";
なお、今回は記憶を持たせてないので、しりとりとか連続性のある話はできません。 記憶といっても、今までの会話の内容(JSON)を Add していくのだと思うのですが、もっと他にいいやり方もありそうだし、次回の課題にします。
Inspector の設定
最後に Button のInspector を以下のように設定。
以上です。
※本作品のキャラクターには株式会社Live2Dの著作物であるサンプルデータが株式会社Live2Dの定める規約に従って用いられています。本作品は制作者の完全な自己の裁量で制作されています。