feat: add config instance.
This commit is contained in:
@@ -0,0 +1,370 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster.Utils
|
||||
{
|
||||
public class JsonScriptableObjectImporterWindow : EditorWindow
|
||||
{
|
||||
private const string DefaultFieldTemplate =
|
||||
@"public string m_strName;
|
||||
public int m_id;
|
||||
public string m_strPath;
|
||||
public int m_iRowNum;
|
||||
public int m_iColNum;
|
||||
public int m_bLimitJump;
|
||||
public string[] m_routeFiles;
|
||||
public string m_content;";
|
||||
|
||||
private TextAsset jsonFile;
|
||||
private DefaultAsset outputFolder;
|
||||
private string dataTypeName = "JsonData";
|
||||
private string assetTypeName = "JsonDataAsset";
|
||||
private string assetFileName = "JsonDataAsset";
|
||||
private string fieldDefinitions = DefaultFieldTemplate;
|
||||
private bool generateStruct;
|
||||
private Vector2 scrollPosition;
|
||||
|
||||
[MenuItem("Tools/JSON ScriptableObject Importer")]
|
||||
public static void OpenWindow()
|
||||
{
|
||||
GetWindow<JsonScriptableObjectImporterWindow>("JSON -> ScriptableObject");
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.HelpBox(
|
||||
"1. Keo file JSON vao o ben duoi.\n" +
|
||||
"2. Dien ten type va field cho struct/class.\n" +
|
||||
"3. Bam Generate Runtime Types de tao file C# trong project Unity.\n" +
|
||||
"4. Cho Unity compile xong, bam Create Asset From JSON.",
|
||||
MessageType.Info);
|
||||
|
||||
DrawJsonDropZone();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
jsonFile = (TextAsset)EditorGUILayout.ObjectField("JSON Input", jsonFile, typeof(TextAsset), false);
|
||||
outputFolder = (DefaultAsset)EditorGUILayout.ObjectField("Output Folder", outputFolder, typeof(DefaultAsset), false);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Type Settings", EditorStyles.boldLabel);
|
||||
dataTypeName = EditorGUILayout.TextField("Data Type Name", dataTypeName);
|
||||
assetTypeName = EditorGUILayout.TextField("Asset Type Name", assetTypeName);
|
||||
assetFileName = EditorGUILayout.TextField("Asset File Name", assetFileName);
|
||||
generateStruct = EditorGUILayout.Toggle("Generate As Struct", generateStruct);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Struct/Class Fields", EditorStyles.boldLabel);
|
||||
EditorGUILayout.HelpBox(
|
||||
"Dien cac field theo cu phap C#. Vi du: public string name; public int id; public string[] tags;",
|
||||
MessageType.None);
|
||||
fieldDefinitions = EditorGUILayout.TextArea(fieldDefinitions, GUILayout.MinHeight(220));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
if (GUILayout.Button("Generate Runtime Types", GUILayout.Height(32)))
|
||||
{
|
||||
GenerateRuntimeTypes();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Create Asset From JSON", GUILayout.Height(32)))
|
||||
{
|
||||
CreateAssetFromJson();
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private void DrawJsonDropZone()
|
||||
{
|
||||
Rect dropArea = GUILayoutUtility.GetRect(0f, 70f, GUILayout.ExpandWidth(true));
|
||||
GUI.Box(dropArea, "Drop file JSON vao day", EditorStyles.helpBox);
|
||||
|
||||
Event currentEvent = Event.current;
|
||||
if (!dropArea.Contains(currentEvent.mousePosition))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentEvent.type == EventType.DragUpdated || currentEvent.type == EventType.DragPerform)
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
||||
|
||||
if (currentEvent.type == EventType.DragPerform)
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
foreach (UnityEngine.Object draggedObject in DragAndDrop.objectReferences)
|
||||
{
|
||||
if (draggedObject is TextAsset textAsset)
|
||||
{
|
||||
jsonFile = textAsset;
|
||||
GUI.FocusControl(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentEvent.Use();
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateRuntimeTypes()
|
||||
{
|
||||
if (!ValidateGenerateInputs())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string folderPath = AssetDatabase.GetAssetPath(outputFolder);
|
||||
string sanitizedDataTypeName = SanitizeIdentifier(dataTypeName, "JsonData");
|
||||
string sanitizedAssetTypeName = SanitizeIdentifier(assetTypeName, sanitizedDataTypeName + "Asset");
|
||||
string outputPath = Path.Combine(folderPath, sanitizedAssetTypeName + ".cs").Replace("\\", "/");
|
||||
|
||||
string source = BuildRuntimeSource(
|
||||
sanitizedDataTypeName,
|
||||
sanitizedAssetTypeName,
|
||||
generateStruct,
|
||||
fieldDefinitions);
|
||||
|
||||
File.WriteAllText(outputPath, source, new UTF8Encoding(false));
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
Debug.Log("Generated runtime types at: " + outputPath);
|
||||
EditorUtility.DisplayDialog(
|
||||
"Generate Complete",
|
||||
"Da tao file C# runtime. Hay doi Unity compile xong roi bam 'Create Asset From JSON'.",
|
||||
"OK");
|
||||
}
|
||||
|
||||
private void CreateAssetFromJson()
|
||||
{
|
||||
if (!ValidateImportInputs())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string sanitizedDataTypeName = SanitizeIdentifier(dataTypeName, "JsonData");
|
||||
string sanitizedAssetTypeName = SanitizeIdentifier(assetTypeName, sanitizedDataTypeName + "Asset");
|
||||
string jsonText = jsonFile.text;
|
||||
|
||||
Type dataType = FindType(sanitizedDataTypeName);
|
||||
Type assetType = FindType(sanitizedAssetTypeName);
|
||||
Type wrapperType = FindType(sanitizedDataTypeName + "ArrayWrapper");
|
||||
|
||||
if (dataType == null || assetType == null || wrapperType == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
"Types Not Ready",
|
||||
"Khong tim thay cac type vua generate. Hay chac chan Unity da compile xong sau khi bam Generate.",
|
||||
"OK");
|
||||
return;
|
||||
}
|
||||
|
||||
ScriptableObject asset = ScriptableObject.CreateInstance(assetType);
|
||||
SetFieldIfExists(assetType, asset, "rawJson", jsonText);
|
||||
|
||||
string trimmedJson = jsonText.TrimStart();
|
||||
if (trimmedJson.StartsWith("[", StringComparison.Ordinal))
|
||||
{
|
||||
object wrapper = JsonUtility.FromJson("{\"items\":" + jsonText + "}", wrapperType);
|
||||
object items = wrapperType.GetField("items", BindingFlags.Public | BindingFlags.Instance)?.GetValue(wrapper);
|
||||
SetFieldIfExists(assetType, asset, "items", items);
|
||||
}
|
||||
else
|
||||
{
|
||||
object data = JsonUtility.FromJson(jsonText, dataType);
|
||||
SetFieldIfExists(assetType, asset, "data", data);
|
||||
}
|
||||
|
||||
string folderPath = AssetDatabase.GetAssetPath(outputFolder);
|
||||
string requestedName = string.IsNullOrWhiteSpace(assetFileName) ? sanitizedAssetTypeName : assetFileName.Trim();
|
||||
string assetPath = AssetDatabase.GenerateUniqueAssetPath(
|
||||
Path.Combine(folderPath, requestedName + ".asset").Replace("\\", "/"));
|
||||
|
||||
AssetDatabase.CreateAsset(asset, assetPath);
|
||||
EditorUtility.SetDirty(asset);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
Selection.activeObject = asset;
|
||||
|
||||
Debug.Log("Created ScriptableObject asset at: " + assetPath);
|
||||
EditorUtility.DisplayDialog("Import Complete", "Da tao asset tu JSON thanh cong.", "OK");
|
||||
}
|
||||
|
||||
private bool ValidateGenerateInputs()
|
||||
{
|
||||
if (outputFolder == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Missing Output Folder", "Hay chon folder de tao file C# runtime.", "OK");
|
||||
return false;
|
||||
}
|
||||
|
||||
string folderPath = AssetDatabase.GetAssetPath(outputFolder);
|
||||
if (!AssetDatabase.IsValidFolder(folderPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("Invalid Folder", "Output Folder phai la mot folder trong Assets.", "OK");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(dataTypeName) || string.IsNullOrWhiteSpace(assetTypeName))
|
||||
{
|
||||
EditorUtility.DisplayDialog("Missing Type Names", "Hay nhap Data Type Name va Asset Type Name.", "OK");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(fieldDefinitions))
|
||||
{
|
||||
EditorUtility.DisplayDialog("Missing Fields", "Hay nhap field cho struct/class.", "OK");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ValidateImportInputs()
|
||||
{
|
||||
if (!ValidateGenerateInputs())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (jsonFile == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Missing JSON", "Hay keo hoac chon file JSON dau vao.", "OK");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string BuildRuntimeSource(
|
||||
string dataType,
|
||||
string assetType,
|
||||
bool asStruct,
|
||||
string fields)
|
||||
{
|
||||
string keyword = asStruct ? "struct" : "class";
|
||||
string createMenuName = "Tools/Generated/" + assetType;
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.AppendLine("using System;");
|
||||
builder.AppendLine("using UnityEngine;");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("[Serializable]");
|
||||
builder.AppendLine("public " + keyword + " " + dataType);
|
||||
builder.AppendLine("{");
|
||||
foreach (string line in NormalizeFieldLines(fields))
|
||||
{
|
||||
builder.AppendLine(" " + line);
|
||||
}
|
||||
|
||||
builder.AppendLine("}");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("[CreateAssetMenu(fileName = \"" + assetType + "\", menuName = \"" + createMenuName + "\")]");
|
||||
builder.AppendLine("public class " + assetType + " : ScriptableObject");
|
||||
builder.AppendLine("{");
|
||||
builder.AppendLine(" public " + dataType + " data;");
|
||||
builder.AppendLine(" public " + dataType + "[] items;");
|
||||
builder.AppendLine(" [TextArea(5, 20)]");
|
||||
builder.AppendLine(" public string rawJson;");
|
||||
builder.AppendLine("}");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("[Serializable]");
|
||||
builder.AppendLine("public class " + dataType + "ArrayWrapper");
|
||||
builder.AppendLine("{");
|
||||
builder.AppendLine(" public " + dataType + "[] items;");
|
||||
builder.AppendLine("}");
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static IEnumerable<string> NormalizeFieldLines(string fields)
|
||||
{
|
||||
return fields
|
||||
.Replace("\r\n", "\n")
|
||||
.Split('\n')
|
||||
.Select(line => line.Trim())
|
||||
.Where(line => !string.IsNullOrWhiteSpace(line));
|
||||
}
|
||||
|
||||
private static void SetFieldIfExists(Type targetType, object target, string fieldName, object value)
|
||||
{
|
||||
FieldInfo field = targetType.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance);
|
||||
if (field != null)
|
||||
{
|
||||
field.SetValue(target, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static string SanitizeIdentifier(string value, string fallback)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
List<char> chars = new List<char>();
|
||||
foreach (char character in value.Trim())
|
||||
{
|
||||
if (char.IsLetterOrDigit(character) || character == '_')
|
||||
{
|
||||
chars.Add(character);
|
||||
}
|
||||
}
|
||||
|
||||
if (chars.Count == 0)
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
if (!char.IsLetter(chars[0]) && chars[0] != '_')
|
||||
{
|
||||
chars.Insert(0, '_');
|
||||
}
|
||||
|
||||
return new string(chars.ToArray());
|
||||
}
|
||||
|
||||
private static Type FindType(string typeName)
|
||||
{
|
||||
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
Type directMatch = assembly.GetType(typeName);
|
||||
if (directMatch != null)
|
||||
{
|
||||
return directMatch;
|
||||
}
|
||||
|
||||
Type fallbackMatch = GetTypesSafely(assembly).FirstOrDefault(type => type.Name == typeName);
|
||||
if (fallbackMatch != null)
|
||||
{
|
||||
return fallbackMatch;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IEnumerable<Type> GetTypesSafely(Assembly assembly)
|
||||
{
|
||||
try
|
||||
{
|
||||
return assembly.GetTypes();
|
||||
}
|
||||
catch (ReflectionTypeLoadException exception)
|
||||
{
|
||||
return exception.Types.Where(type => type != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user