Skip to content

Instantly share code, notes, and snippets.

@negipoyoc
Created March 23, 2023 12:18
Show Gist options
  • Save negipoyoc/4fbb8d5136a834661cee15abf7102167 to your computer and use it in GitHub Desktop.
Save negipoyoc/4fbb8d5136a834661cee15abf7102167 to your computer and use it in GitHub Desktop.
ChatGPTに自分のコードのパフォーマンス改善やテストコードの生成などをお願いするUnity Editor拡張です。
using System;
using System.Collections.Generic;
using System.Text;
using Cysharp.Threading.Tasks;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
using Object = UnityEngine.Object;
namespace LuppetEditor
{
public class ChatGptCodeReviewerWindow : EditorWindow
{
private bool _isWaitingForResponse;
private float _copyFeedbackTime;
private int _promptType;
private string _promptText;
private const string ChatGptapiUrl = "https://api.openai.com/v1/chat/completions";
private const string APIKey = "{{自分のOpenAIのAPIKeyを入力してね}}";
// ウィンドウを開くためのメニュー項目
[MenuItem("Window/ChatGPT Code Reviewer")]
public static void ShowWindow()
{
GetWindow<ChatGptCodeReviewerWindow>("ChatGPT Code Reviewer");
}
// ドラッグアンドドロップ用の変数
private Object _codeFile;
// チャット欄用の変数
private string _inputText = "";
private Vector2 _chatScrollViewPosition;
// メッセージリスト
private List<ChatGPTMessageModel> _messageList = new();
private void OnGUI()
{
CreateInitialPrompt();
// ドラッグアンドドロップ領域の作成
CreateDragAndDropArea();
// チャット欄の作成
CreateChatArea();
}
private string CodeFileContent
{
get
{
if (!_codeFile) return "";
string assetPath = AssetDatabase.GetAssetPath(_codeFile);
return System.IO.File.ReadAllText(assetPath);
}
}
private async void CreateDragAndDropArea()
{
GUILayout.Label("C#ファイルをドロップしてください。:");
Rect dropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUILayout.ExpandWidth(true));
GUI.Box(dropArea, "[ここにドロップ]");
Event e = Event.current;
if (!dropArea.Contains(e.mousePosition))
return;
switch (e.type)
{
case EventType.DragUpdated:
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
e.Use();
break;
case EventType.DragPerform:
_messageList = new();
DragAndDrop.AcceptDrag();
_codeFile = DragAndDrop.objectReferences[0];
e.Use();
// スクリプトがドロップされた後の処理
var codeMessage = new ChatGPTMessageModel
{
role = "system",
content =
$"{_promptText} \n--\n{CodeFileContent}\n--"
};
_messageList.Add(codeMessage);
await CommunicateWithChatGPT(_messageList);
break;
}
}
private void CreateInitialPrompt()
{
_promptType = GUILayout.SelectionGrid(_promptType,
new[] {"Optimize Code", "Generate Test Code", "Add Comments", "Free Input"}, 4);
switch (_promptType)
{
case 0:
_promptText = $"あなたはUnityの高度な専門家としてふるまってください。以下のコードについてパフォーマンスに問題がある部分があればそれを最適化しつつ、修正理由を教えてください。";
break;
case 1:
_promptText = $"あなたはUnityの高度な専門家としてふるまってください。以下のコードに対してテストコードを生成してください。";
break;
case 2:
_promptText = $"あなたはUnityの高度な専門家としてふるまってください。以下のコードに適切なコメントを自動的に追加してください。";
break;
case 3:
_promptText = $"あなたはUnityの高度な専門家としてふるまってください。このコードを一旦保持して私の次の質問に備えてください。";
break;
}
GUILayout.Label("Initial Prompt:" + _promptText);
GUILayout.Space(10);
}
private void CreateChatArea()
{
GUILayout.Label("ChatGPT APIとの対話:");
_chatScrollViewPosition = GUILayout.BeginScrollView(_chatScrollViewPosition, GUIStyle.none, GUIStyle.none,
GUILayout.ExpandHeight(true));
GUIStyle messageStyle = new GUIStyle(GUI.skin.label)
{
wordWrap = true
};
foreach (ChatGPTMessageModel message in _messageList)
{
Color originalColor = GUI.color;
GUI.color = message.role == "system"
? Color.green
: (message.role == "user" ? Color.white : Color.yellow);
GUIContent content = new GUIContent($"{message.role}: {message.content}");
Rect buttonRect = GUILayoutUtility.GetRect(content, messageStyle);
if (Event.current.type == EventType.Repaint && buttonRect.Contains(Event.current.mousePosition))
{
EditorGUIUtility.AddCursorRect(buttonRect, MouseCursor.Link);
content.tooltip = "クリックしてコピー";
}
if (GUI.Button(buttonRect, content, messageStyle))
{
EditorGUIUtility.systemCopyBuffer = message.content;
_copyFeedbackTime = Time.realtimeSinceStartup;
}
GUI.color = originalColor;
}
GUILayout.EndScrollView();
if (_isWaitingForResponse)
{
EditorGUILayout.HelpBox("ChatGPTのレスポンスを待っています...", MessageType.Info);
}
GUILayout.Space(10);
GUILayout.BeginHorizontal();
_inputText = GUILayout.TextField(_inputText, GUILayout.ExpandWidth(true));
if (GUILayout.Button("送る", GUILayout.Width(100)))
{
SendButtonClicked();
}
GUILayout.EndHorizontal();
if (_copyFeedbackTime > 0 && Time.realtimeSinceStartup - _copyFeedbackTime < 2)
{
EditorGUI.HelpBox(GUILayoutUtility.GetLastRect(), "コピーしました!", MessageType.Info);
}
}
private async void SendButtonClicked()
{
if (string.IsNullOrEmpty(_inputText))
return;
ChatGPTMessageModel userMessage = new ChatGPTMessageModel {role = "user", content = _inputText};
_messageList.Add(userMessage);
_inputText = "";
// ここでChatGPT APIと通信
await CommunicateWithChatGPT(_messageList);
}
private async UniTask CommunicateWithChatGPT(List<ChatGPTMessageModel> messages)
{
ChatGPTCompletionRequestModel requestData = new ChatGPTCompletionRequestModel
{
model = "gpt-3.5-turbo",
messages = messages
};
string json = JsonUtility.ToJson(requestData);
byte[] bodyRaw = Encoding.UTF8.GetBytes(json);
using (UnityWebRequest www = new UnityWebRequest(ChatGptapiUrl, "POST"))
{
www.uploadHandler = new UploadHandlerRaw(bodyRaw);
www.downloadHandler = new DownloadHandlerBuffer();
www.SetRequestHeader("Content-Type", "application/json");
www.SetRequestHeader("Authorization", $"Bearer {APIKey}");
_isWaitingForResponse = true;
await www.SendWebRequest();
_isWaitingForResponse = false;
if (www.result != UnityWebRequest.Result.Success)
{
Debug.LogError("Error: " + www.error);
}
else
{
ChatGPTResponseModel response =
JsonUtility.FromJson<ChatGPTResponseModel>(www.downloadHandler.text);
if (response.choices.Length > 0)
{
ChatGPTMessageModel chatGPTMessage = response.choices[0].message;
_messageList.Add(chatGPTMessage);
Repaint();
}
else
{
Debug.LogError("Error: No choices in ChatGPT response");
}
}
}
}
}
[Serializable]
public class ChatGPTMessageModel
{
public string role;
public string content;
}
//ChatGPT APIにRequestを送るためのJSON用クラス
[Serializable]
public class ChatGPTCompletionRequestModel
{
public string model;
public List<ChatGPTMessageModel> messages;
}
//ChatGPT APIからのResponseを受け取るためのクラス
[System.Serializable]
public class ChatGPTResponseModel
{
public string id;
public string @object;
public int created;
public Choice[] choices;
public Usage usage;
[System.Serializable]
public class Choice
{
public int index;
public ChatGPTMessageModel message;
public string finish_reason;
}
[System.Serializable]
public class Usage
{
public int prompt_tokens;
public int completion_tokens;
public int total_tokens;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment