CapersProject/Assets/02.Scripts/Editor/JsonHelperEditor.cs
2024-09-12 13:17:34 +09:00

215 lines
8.8 KiB
C#

using System.Collections.Generic;
using System.IO;
using BlueWater.Interfaces;
using BlueWater.Items;
using BlueWater.Npcs.Customers;
using BlueWater.Utility;
using UnityEditor;
using UnityEngine;
namespace BlueWater.Editors
{
public class JsonHelperEditor : EditorWindow
{
// Json파일이 추가될 때 마다, DataType을 추가하고,
// LoadData의 switch문에 LoadData<>를 추가합니다.
private enum DataType
{
None = 0,
ItemDataTable,
CustomerDataTable,
FoodDataTable,
CocktailDataTable,
DrinkDataTable,
LevelDataTable,
CardDataTable,
}
private string _jsonFilePath = "Assets/Resources/Json/FileName";
private string _assetFilePath = "Assets/02.Scripts/ScriptableObject/Item";
private DataType _dataType = DataType.None;
[MenuItem("Tools/Json파일 ScriptableObject로 자동 변환")]
public static void ShowWindow()
{
GetWindow<JsonHelperEditor>("Json파일 ScriptableObject로 자동 변환");
}
private void OnGUI()
{
GUILayout.Label("Json파일 ScriptableObject로 자동 변환", EditorStyles.boldLabel);
GUILayout.Space(10);
EditorGUILayout.HelpBox("Assets 폴더를 시작으로 전체 Path가 필요하며, Json 파일이 있는 Path를 입력해야 합니다." +
"\n파일 확장자는 자동으로 .json이 들어갑니다." +
"\n예시: Assets/Resources/Json/최종 Json 파일 이름", MessageType.Info);
_jsonFilePath = EditorGUILayout.TextField("Json 파일 위치", _jsonFilePath);
EditorGUILayout.HelpBox("Assets 폴더를 시작으로 전체 Path가 필요하며, File을 저장할 폴더의 전체 Path가 필요합니다." +
"\n파일명은 Json파일의 이름과 동일하게 자동으로 적용됩니다." +
"\n예시1: Assets/02.Scripts/ScriptableObject" +
"\n예시2: Assets/02.Scripts/ScriptableObject/최종 폴더 이름", MessageType.Info);
_assetFilePath = EditorGUILayout.TextField("저장할 폴더 위치", _assetFilePath);
_dataType = (DataType)EditorGUILayout.EnumPopup("데이터 종류", _dataType);
if (GUILayout.Button("변환하기"))
{
LoadDataFromJson();
}
}
private void LoadDataFromJson()
{
if (string.IsNullOrEmpty(_jsonFilePath) || string.IsNullOrEmpty(_assetFilePath))
{
EditorUtility.DisplayDialog("경고 메세지", "빈 목록을 채워주세요.", "확인");
Debug.LogError("빈 목록을 채워주세요.");
return;
}
switch (_dataType)
{
case DataType.None:
EditorUtility.DisplayDialog("경고 메세지", "데이터 타입이 None인지 확인해주세요.", "확인");
Debug.LogError("데이터 타입이 None인지 확인해주세요.");
return;
case DataType.ItemDataTable:
LoadData<ItemData, ItemDataSo>();
break;
case DataType.CustomerDataTable:
LoadData<CustomerData, CustomerDataSo>();
break;
case DataType.FoodDataTable:
LoadData<FoodData, FoodDataSo>();
break;
case DataType.CocktailDataTable:
LoadData<CocktailData, CocktailDataSo>();
break;
case DataType.DrinkDataTable:
LoadData<DrinkData, DrinkDataSo>();
break;
case DataType.LevelDataTable:
LoadData<LevelData, LevelDataSo>();
break;
case DataType.CardDataTable:
LoadData<CardData, CardDataSo>();
break;
default:
EditorUtility.DisplayDialog("경고 메세지", "데이터 타입이 제대로 설정되어있는지 확인해주세요.", "OK");
Debug.LogError("데이터 타입이 제대로 설정되어있는지 확인해주세요.");
return;
}
}
private void LoadData<T, U>()
where T : class, IIdx
where U : ScriptableObject, IDataContainer<T>, new()
{
var fullJsonFilePath = _jsonFilePath + ".json";
if (!File.Exists(fullJsonFilePath))
{
EditorUtility.DisplayDialog("경고 메세지", "Json 파일을 찾을 수 없습니다.\n" + fullJsonFilePath, "확인");
Debug.LogError("Json 파일을 찾을 수 없습니다.\n" + fullJsonFilePath);
return;
}
var newData = JsonHelper.LoadJsonData<T>(fullJsonFilePath);
if (newData == null || newData.Count == 0)
{
EditorUtility.DisplayDialog("경고 메세지", "Json 파일 데이터가 비어있거나, 불러올 수 없습니다.\n" + fullJsonFilePath, "확인");
Debug.LogError("Json 파일 데이터가 비어있거나, 불러올 수 없습니다.\n" + fullJsonFilePath);
return;
}
newData.Sort((x, y) => x.Idx.CompareTo(y.Idx));
var assetFileName = Path.GetFileNameWithoutExtension(_jsonFilePath) + ".asset";
var assetPath = Path.Combine(_assetFilePath, assetFileName);
var so = AssetDatabase.LoadAssetAtPath<U>(assetPath);
if (so == null)
{
so = CreateInstance<U>();
if (so is IDataContainer<T> container)
{
container.SetData(newData);
}
AssetDatabase.CreateAsset(so, assetPath);
EditorUtility.DisplayDialog("알림 메세지", "새로운 ScriptableObject가 생성되었습니다.", "확인");
}
else
{
if (so is IDataContainer<T> container)
{
var existingData = container.GetData();
MergeData(existingData, newData);
existingData.Sort((x, y) => x.Idx.CompareTo(y.Idx));
container.SetData(existingData);
}
EditorUtility.DisplayDialog("알림 메세지", "기존 ScriptableObject가 업데이트되었습니다.", "확인");
}
EditorUtility.SetDirty(so);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
/// <summary>
/// Json에 있는 데이터만 덮어씌우도록 하는 함수
/// Json안에 없는 데이터는 변경하지 않습니다.
/// </summary>
/// <param name="existingData"></param>
/// <param name="newData"></param>
/// <typeparam name="T"></typeparam>
private void MergeData<T>(List<T> existingData, List<T> newData) where T : class, IIdx
{
existingData ??= new List<T>();
var newDataIdxSet = new HashSet<string>();
foreach (var newDataItem in newData)
{
if (newDataItem != null)
{
newDataIdxSet.Add(newDataItem.Idx);
}
}
existingData.RemoveAll(item => item == null || !newDataIdxSet.Contains(item.Idx));
var existingDataDict = new Dictionary<string, T>();
foreach (var data in existingData)
{
if (data != null)
{
existingDataDict[data.Idx] = data;
}
}
foreach (var newDataItem in newData)
{
if (newDataItem == null) continue;
if (existingDataDict.TryGetValue(newDataItem.Idx, out var existingItem))
{
var properties = typeof(T).GetProperties();
foreach (var property in properties)
{
var newValue = property.GetValue(newDataItem);
if (newValue != null && property.CanWrite)
{
property.SetValue(existingItem, newValue);
}
}
}
else
{
existingDataDict[newDataItem.Idx] = newDataItem;
}
}
existingData.Clear();
existingData.AddRange(existingDataDict.Values);
}
}
}