using UnityEngine; using UnityEditor; using System.IO; using System.Collections.Generic; using SK.Framework; using Newtonsoft.Json; [System.Serializable] public class QuestionJson { public string type; public string question; public List options; public string answer; public List answers; public string explanation; } public static class QuestionProfileBatchImporter { [MenuItem("Tools/Import Questions/One Profile Per JSON (All Types)")] public static void ImportEachJsonAsProfile() { string folder = EditorUtility.OpenFolderPanel("选择 JSON 文件夹", Application.dataPath, ""); if (string.IsNullOrEmpty(folder)) return; string[] jsonFiles = Directory.GetFiles(folder, "*.json", SearchOption.TopDirectoryOnly); if (jsonFiles.Length == 0) { EditorUtility.DisplayDialog("提示", "该文件夹中没有 JSON 文件", "OK"); return; } string saveFolder = EditorUtility.SaveFolderPanel("选择保存目录", Application.dataPath, ""); if (string.IsNullOrEmpty(saveFolder)) return; string relativeSaveFolder = FileUtil.GetProjectRelativePath(saveFolder); if (string.IsNullOrEmpty(relativeSaveFolder)) { EditorUtility.DisplayDialog("错误", "选择的保存目录不在当前 Unity 项目内", "OK"); return; } int totalFiles = 0; int singleCount = 0; int multiCount = 0; int judgeCount = 0; foreach (string file in jsonFiles) { try { string json = File.ReadAllText(file); // 直接解析为 QuestionJson 列表(数组格式) List questions = JsonConvert.DeserializeObject>(json); if (questions == null || questions.Count == 0) { Debug.LogWarning($"文件没有有效题目: {Path.GetFileName(file)}"); continue; } var profile = ScriptableObject.CreateInstance(); profile.SingleChoices = new List(); profile.MultipleChoices = new List(); profile.Judges = new List(); int seqSingle = 1; int seqMulti = 1; int seqJudge = 1; foreach (var q in questions) { string type = string.IsNullOrEmpty(q.type) ? "single" : q.type.ToLower(); if (type == "single") { var single = new SingleChoiceQuestion { Sequence = seqSingle++, Question = q.question, Analysis = q.explanation ?? "", choiceType = ChoiceType.Text, Choices = new List() }; if (q.options != null) { for (int i = 0; i < q.options.Count; i++) single.Choices.Add(new QuestionChoice(q.options[i], null)); } single.Answer = ParseAnswerIndex(q.answer, q.options?.Count ?? 0); profile.SingleChoices.Add(single); singleCount++; } else if (type == "multiple") { var multi = new MultipleChoiceQuestion { Sequence = seqMulti++, Question = q.question, Analysis = q.explanation ?? "", choiceType = ChoiceType.Text, Choices = new List(), Answers = new List() }; if (q.options != null) { for (int i = 0; i < q.options.Count; i++) multi.Choices.Add(new QuestionChoice(q.options[i], null)); } if (q.answers != null) { foreach (var ans in q.answers) { int idx = ParseAnswerIndex(ans, q.options?.Count ?? 0); if (idx >= 0) multi.Answers.Add(idx); } } profile.MultipleChoices.Add(multi); multiCount++; } else if (type == "judgment") { var judge = new JudgeQuestion { Sequence = seqJudge++, Question = q.question, Analysis = q.explanation ?? "", Positive = "正确", Negative = "错误", Answer = ParseJudgeAnswer(q.answer) }; profile.Judges.Add(judge); judgeCount++; } else { Debug.LogWarning($"未知的题目类型: {q.type},题目: {q.question}"); } } string fileName = Path.GetFileNameWithoutExtension(file); string savePath = Path.Combine(relativeSaveFolder, fileName + ".asset"); // 确保目录存在 string directory = Path.GetDirectoryName(savePath); if (!Directory.Exists(directory)) Directory.CreateDirectory(directory); AssetDatabase.CreateAsset(profile, savePath); totalFiles++; Debug.Log($"✅ 成功创建: {savePath} \n(单选题: {profile.SingleChoices.Count}, 多选题: {profile.MultipleChoices.Count}, 判断题: {profile.Judges.Count})"); } catch (System.Exception e) { Debug.LogError($"❌ 处理文件失败: {Path.GetFileName(file)}\n错误: {e.Message}"); } } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); EditorUtility.DisplayDialog("导入完成", $"处理了 {totalFiles}/{jsonFiles.Length} 个 JSON 文件\n" + $"单选题:{singleCount} 道\n" + $"多选题:{multiCount} 道\n" + $"判断题:{judgeCount} 道\n" + $"保存目录:{relativeSaveFolder}", "OK"); } [MenuItem("Tools/Import Questions/Single JSON File")] public static void ImportSingleJsonFile() { string filePath = EditorUtility.OpenFilePanel("选择 JSON 文件", Application.dataPath, "json"); if (string.IsNullOrEmpty(filePath)) return; string saveFolder = EditorUtility.SaveFolderPanel("选择保存目录", Application.dataPath, ""); if (string.IsNullOrEmpty(saveFolder)) return; string relativeSaveFolder = FileUtil.GetProjectRelativePath(saveFolder); if (string.IsNullOrEmpty(relativeSaveFolder)) { EditorUtility.DisplayDialog("错误", "选择的保存目录不在当前 Unity 项目内", "OK"); return; } try { string json = File.ReadAllText(filePath); List questions = JsonConvert.DeserializeObject>(json); if (questions == null || questions.Count == 0) { EditorUtility.DisplayDialog("错误", "JSON 文件中没有有效的题目数据", "OK"); return; } var profile = ScriptableObject.CreateInstance(); profile.SingleChoices = new List(); profile.MultipleChoices = new List(); profile.Judges = new List(); int seqSingle = 1; int seqMulti = 1; int seqJudge = 1; int singleCount = 0; int multiCount = 0; int judgeCount = 0; foreach (var q in questions) { string type = string.IsNullOrEmpty(q.type) ? "single" : q.type.ToLower(); if (type == "single") { var single = new SingleChoiceQuestion { Sequence = seqSingle++, Question = q.question, Analysis = q.explanation ?? "", choiceType = ChoiceType.Text, Choices = new List() }; if (q.options != null) { for (int i = 0; i < q.options.Count; i++) single.Choices.Add(new QuestionChoice(q.options[i], null)); } single.Answer = ParseAnswerIndex(q.answer, q.options?.Count ?? 0); profile.SingleChoices.Add(single); singleCount++; } else if (type == "multiple") { var multi = new MultipleChoiceQuestion { Sequence = seqMulti++, Question = q.question, Analysis = q.explanation ?? "", choiceType = ChoiceType.Text, Choices = new List(), Answers = new List() }; if (q.options != null) { for (int i = 0; i < q.options.Count; i++) multi.Choices.Add(new QuestionChoice(q.options[i], null)); } if (q.answers != null) { foreach (var ans in q.answers) { int idx = ParseAnswerIndex(ans, q.options?.Count ?? 0); if (idx >= 0) multi.Answers.Add(idx); } } profile.MultipleChoices.Add(multi); multiCount++; } else if (type == "judgment") { var judge = new JudgeQuestion { Sequence = seqJudge++, Question = q.question, Analysis = q.explanation ?? "", Positive = "正确", Negative = "错误", Answer = ParseJudgeAnswer(q.answer) }; profile.Judges.Add(judge); judgeCount++; } } string fileName = Path.GetFileNameWithoutExtension(filePath); string savePath = Path.Combine(relativeSaveFolder, fileName + ".asset"); AssetDatabase.CreateAsset(profile, savePath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); EditorUtility.DisplayDialog("导入完成", $"成功导入: {fileName}\n" + $"单选题: {singleCount} 道\n" + $"多选题: {multiCount} 道\n" + $"判断题: {judgeCount} 道", "OK"); Debug.Log($"✅ 资源已创建: {savePath}"); } catch (System.Exception e) { EditorUtility.DisplayDialog("错误", $"导入失败: {e.Message}", "OK"); Debug.LogError($"❌ 导入失败: {e}"); } } private static int ParseAnswerIndex(string answer, int optionCount) { if (string.IsNullOrWhiteSpace(answer)) return -1; answer = answer.Trim(); char c = char.ToUpperInvariant(answer[0]); if (c >= 'A' && c < 'A' + optionCount) return c - 'A'; if (int.TryParse(answer, out int num)) { int idx = num - 1; if (idx >= 0 && idx < optionCount) return idx; if (num >= 0 && num < optionCount) return num; } return -1; } private static bool ParseJudgeAnswer(string answer) { if (string.IsNullOrWhiteSpace(answer)) return false; answer = answer.Trim().ToLower(); if (answer == "true" || answer == "t" || answer == "正确" || answer == "对" || answer == "√" || answer == "1") return true; else if (answer == "false" || answer == "f" || answer == "错误" || answer == "错" || answer == "×" || answer == "0") return false; return false; } }