ChatGPTを使ってUnityでスクリプトを自動生成

# はじめに
今回は現在、はやりのChatGPTを使って自分のしたい内容を打ち込んだら自動的にコードを生成してくれるものを作りたいと思って作ったので、それの記録的な感じでこの記事を書いてます。

## 1.参考資料
今回、ChatGPTを使うにあたって参考にしたものは以下ものです。
APIを利用するためのコードの参考
→https://github.com/unitycoder/UnityOpenAIGPT3
ChatGPT
→https://chat.openai.com/chat
APIキーについて
→https://auto-worker.com/blog/?p=6988

## 2.実際にできたもの
実際にできたものは以下のようなものです。
Want to do と書いたところにやりたい内容
Class name　と書いたところにスクリプトのクラス名を入力してしたの聞くをクリックすると自動的にAseetsフォルダーにスクリプトが生成される形になっています。
![スクリーンショット 0005-03-12 0.27.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866543/c90ee123-4244-bc91-cc1d-7556c743872a.png)

実際に生成されたコード（手直しなし）
![スクリーンショット 0005-03-12 0.42.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866543/910f9803-e98c-fac9-999a-497e24873812.png)

このように多分、ちゃんと動くであろうコードが生成されます。
実際に引き算や足し算、カウントダウンなどのコードはオブジェクトに適用して動作させたらちゃんと動作することを確認しています。
## 3.コードとか詳しい内容
コードに関しては基本的にはGitHubから拾ってきたもので、そこからすこし改良しています。
ものすごく汚い可能性があるのでコードの批評などは勘弁してください。、。。

```C#:OpenAI
using UnityEditor;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;


namespace UnityLibrary
{
    public class OpenAI : MonoBehaviour
    {
        string CODE;
        public  void Generate()
        {
            var filePath = "Assets/" + Classname + ".cs";
            File.WriteAllText(filePath, CODE);
            AssetDatabase.Refresh();
        }
        const string url = "https://api.openai.com/v1/completions";
         string modelName = "text-davinci-003"; 
        [SerializeField][TextArea(3,4)]
         string wanttodo;
        [SerializeField][TextArea(3, 4)]
        string Classname;
        [SerializeField] [TextArea(20,21)]
         string inputResults;
         GameObject loadingIcon;

        string apiKey = "APIKEY";
        bool isRunning = false;

        void Start()
        {
            LoadAPIKey();
        }

        public void Execute()
        {
            if (isRunning)
            {
                Debug.LogError("Already running");
                return;
            }
            isRunning = true;
            loadingIcon.SetActive(true);
            
            RequestData requestData = new RequestData()
            {
                model = modelName,
                prompt = "Unityで" + wanttodo + "するスクリプトを書いてください。" + "クラス名は" + Classname + "で。usingから記述してください。MonoBehaviourを継承してください。",
                temperature = 0.7f,
                max_tokens = 1000,
                top_p = 1,
                frequency_penalty = 0,
                presence_penalty = 0
            };

            string jsonData = JsonUtility.ToJson(requestData);

            byte[] postData = System.Text.Encoding.UTF8.GetBytes(jsonData);

            UnityWebRequest request = UnityWebRequest.Post(url, jsonData);
            request.uploadHandler = new UploadHandlerRaw(postData);
            request.downloadHandler = new DownloadHandlerBuffer();
            request.SetRequestHeader("Content-Type", "application/json");
            request.SetRequestHeader("Authorization", "Bearer " + apiKey);

            UnityWebRequestAsyncOperation async = request.SendWebRequest();

            async.completed += (op) =>
            {
                if (request.result == UnityWebRequest.Result.ConnectionError)
                {
                    Debug.LogError(request.error);
                }
                else
                {
                    Debug.Log(request.downloadHandler.text);
                    OpenAIAPI responseData = JsonUtility.FromJson<OpenAIAPI>(request.downloadHandler.text);
                    string generatedText = responseData.choices[0].text.TrimStart('\n').TrimStart('\n');
                   inputResults = generatedText;
                   CODE = inputResults;
                    Generate();

                }
                loadingIcon.SetActive(false);
                isRunning = false;
            };

        } 
        public void LoadAPIKey()
        {
            var keypath = Path.Combine(Application.streamingAssetsPath, "secretkey.txt");
            if (File.Exists(keypath) == false)
            {
                Debug.LogError("Apikey missing: " + keypath);
            }
            apiKey = File.ReadAllText(keypath).Trim();
            Debug.Log("API key loaded, len= " + apiKey.Length);
        }
    }
}

```
APIKEYの部分には自分のAPIKEYをはっつけてください。
エラーとか出たらChatGPTに相談しよう！
そしてインスペクター拡張？を行うためのスクリプトです。
```C#:Editor
 [CustomEditor(typeof(UnityLibrary.OpenAI))]
    public class CharacterEditor : Editor
    {
        public override void OnInspectorGUI()
        {
            DrawDefaultInspector();

            UnityLibrary.OpenAI myScript = (UnityLibrary.OpenAI)target;
            if (GUILayout.Button("LoadAPI"))
            {
                myScript.LoadAPIKey();
            }
            if (GUILayout.Button("聞く"))
            {
                myScript.Execute();

            }
        }
    }
```

## 5.追記
更に改良を加えたので追記したいと思います。
今まではGameObject等にコードを貼り付ける形で生成していましたが、Editor拡張を用いてそれをせずともコードを生成できるようになるように改良したのでそのコードと参考動画を貼っ付けておこうと思います。
```C#:OpenAI
using UnityEditor;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;


namespace UnityLibrary
{
    public class OpenAI : MonoBehaviour
    {
        const string url = "https://api.openai.com/v1/completions";
        [SerializeField]
        [TextArea(3, 4)]
        string wanttodo;
        [SerializeField]
        [TextArea(3, 4)]
        string Classname;
        [SerializeField]
        [TextArea(20, 21)]
        string inputResults;
        string apiKey = "YOUR API KEY";
        static bool isRunning = false;

        void Start()
        {
            LoadAPIKey();
        }

        public static void Execute(string wanttex, string nametex)
        {
            if (isRunning)
            {
                Debug.LogError("Already running");
                return;
            }
            isRunning = true;
            RequestData requestData = new RequestData()
            {
                model = "text-davinci-003",
                prompt = "Unityで" + wanttex + "をするスクリプトを書いてください。" + "クラス名は" + nametex + "で。usingから記述してください。MonoBehaviourを継承してください。",
                temperature = 0.7f,
                max_tokens = 1000,
                top_p = 1,
                frequency_penalty = 0,
                presence_penalty = 0
            };
            string jsonData = JsonUtility.ToJson(requestData);
            byte[] postData = System.Text.Encoding.UTF8.GetBytes(jsonData);
            UnityWebRequest request = UnityWebRequest.Post(url, jsonData);
            request.uploadHandler = new UploadHandlerRaw(postData);
            request.downloadHandler = new DownloadHandlerBuffer();
            request.SetRequestHeader("Content-Type", "application/json");
            request.SetRequestHeader("Authorization", "Bearer " + "YOUR API KEY");
            UnityWebRequestAsyncOperation async = request.SendWebRequest();
            async.completed += (op) =>
            {
                if (request.result == UnityWebRequest.Result.ConnectionError)
                {
                    Debug.LogError(request.error);
                }
                else
                {
                    Debug.Log(request.downloadHandler.text);
                    OpenAIAPI responseData = JsonUtility.FromJson<OpenAIAPI>(request.downloadHandler.text);
                    string generatedText = responseData.choices[0].text.TrimStart('\n').TrimStart('\n');
                    string CODE = generatedText;
                    var filePath = "Assets/" + nametex + ".cs";
                    File.WriteAllText(filePath, CODE);
                    AssetDatabase.Refresh();
                }
                isRunning = false;
            };
        }
        public void LoadAPIKey()
        {
            var keypath = Path.Combine(Application.streamingAssetsPath, "secretkey.txt");
            if (File.Exists(keypath) == false)
            {
                Debug.LogError("Apikey missing: " + keypath);
            }
            apiKey = File.ReadAllText(keypath).Trim();
            Debug.Log("API key loaded, len= " + apiKey.Length);
        }

        public class AIEDITORE : EditorWindow
        {
            private string todotext = "";
            private string nametext = "";
            [MenuItem("GPT/ScriptGeneration/Generator", false, 1)]

            private static void ShowWindow()
            {
                AIEDITORE window = GetWindow<AIEDITORE>();
                window.titleContent = new GUIContent("ScriptGenerator");
            }

            private void OnGUI()
            {
                GUILayout.Label("want to do");
                todotext = EditorGUILayout.TextArea(todotext, GUILayout.Height(100));
                GUILayout.Label("class name");
                nametext = EditorGUILayout.TextArea(nametext, GUILayout.Height(100));
                if (GUILayout.Button("Generate"))
                {
                    if (todotext != "" && nametext != "")
                    {
                        Execute(todotext, nametext);
                    }
                    else
                    {
                        Debug.LogError("Please enter text");
                    }
                }
            }
        }
    }
}

```
https://twitter.com/Upon_Main/status/1635578398432178176?s=20
## 4.最後に
今回はChatGPTを使ってスクリプト自動生成する試みをしてみました。
ChatGPTのAPI自体は有料で登録時に$18分もらえてその間は遊べると思うので、ぜひ試してみてください。