230 lines
9.5 KiB
C#
230 lines
9.5 KiB
C#
using System.Collections.Generic;
|
||
using BrewMonster.Scripts.Chat.EmotionData;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
|
||
namespace BrewMonster.Scripts.Editor
|
||
{
|
||
/// <summary>
|
||
/// Tool: atlas + txt → Sprite Mode Multiple (một PNG/bộ) + SO; batch linh hoạt số slot.
|
||
/// </summary>
|
||
public class EmotionAtlasConverterWindow : EditorWindow
|
||
{
|
||
private Texture2D _sourceAtlas;
|
||
private TextAsset _txtAsset;
|
||
private int _emotionSetIndex;
|
||
private int _cellW = 32;
|
||
private int _cellH = 32;
|
||
private string _outputFolder = "Assets/PerfectWorld/UI/Chat/GeneratedEmotions";
|
||
private bool _sliceInPlace;
|
||
|
||
private List<EmotionBatchSlot> _batchSlots = new List<EmotionBatchSlot>();
|
||
private Vector2 _batchScroll;
|
||
|
||
private string _libraryAssetPath = "Assets/PerfectWorld/UI/Chat/GeneratedEmotions/EmotionLibrary.asset";
|
||
|
||
[MenuItem("Tools/Perfect World/ChatSystem/Emotion Atlas Converter…")]
|
||
public static void Open()
|
||
{
|
||
GetWindow<EmotionAtlasConverterWindow>(true, "Emotion Atlas Converter", true);
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
if (_batchSlots == null)
|
||
_batchSlots = new List<EmotionBatchSlot>();
|
||
if (_batchSlots.Count == 0)
|
||
{
|
||
for (int i = 0; i < 8; i++)
|
||
_batchSlots.Add(new EmotionBatchSlot { EmotionSetIndex = i });
|
||
}
|
||
}
|
||
|
||
private void OnGUI()
|
||
{
|
||
EditorGUILayout.LabelField("Chung / Shared", EditorStyles.boldLabel);
|
||
_cellW = EditorGUILayout.IntField("Cell width (W)", _cellW);
|
||
_cellH = EditorGUILayout.IntField("Cell height (H)", _cellH);
|
||
_outputFolder = EditorGUILayout.TextField("Output folder", _outputFolder);
|
||
_sliceInPlace = EditorGUILayout.ToggleLeft(
|
||
"Slice tại asset nguồn (ghi đè import Multiple lên atlas đang chọn) — Slice in-place on source asset",
|
||
_sliceInPlace);
|
||
if (_sliceInPlace)
|
||
{
|
||
EditorGUILayout.HelpBox(
|
||
"Cảnh báo: Unity sẽ đổi import của file atlas gốc thành Sprite Multiple full lưới.\n" +
|
||
"Warning: overwrites source texture import settings.",
|
||
MessageType.Warning);
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.HelpBox(
|
||
"Mặc định: copy atlas vào Output/Emotions{N}/Emotions{N}_atlas.png rồi Multiple slice (giữ nguyên file nguồn).\n" +
|
||
"Default: copy atlas to output folder then slice (source unchanged).",
|
||
MessageType.None);
|
||
}
|
||
|
||
EditorGUILayout.Space(8f);
|
||
EditorGUILayout.LabelField("Một bộ / Single set", EditorStyles.boldLabel);
|
||
_sourceAtlas = (Texture2D)EditorGUILayout.ObjectField("Atlas", _sourceAtlas, typeof(Texture2D), false);
|
||
_txtAsset = (TextAsset)EditorGUILayout.ObjectField("EmotionsN.txt", _txtAsset, typeof(TextAsset), false);
|
||
_emotionSetIndex = EditorGUILayout.IntField("Emotion set index (N)", _emotionSetIndex);
|
||
|
||
EditorGUILayout.Space();
|
||
if (GUILayout.Button("Convert → EmotionSetData + Atlas (Multiple)"))
|
||
{
|
||
RunConvertSingle();
|
||
}
|
||
|
||
EditorGUILayout.Space(12f);
|
||
EditorGUILayout.LabelField("Batch → EmotionLibrary", EditorStyles.boldLabel);
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
if (GUILayout.Button("+ Thêm slot", GUILayout.Width(100)))
|
||
_batchSlots.Add(new EmotionBatchSlot { EmotionSetIndex = NextSuggestedSetIndex() });
|
||
if (GUILayout.Button("− Xóa slot cuối", GUILayout.Width(120)) && _batchSlots.Count > 0)
|
||
_batchSlots.RemoveAt(_batchSlots.Count - 1);
|
||
EditorGUILayout.LabelField($"Số slot: {_batchSlots.Count}");
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
_batchScroll = EditorGUILayout.BeginScrollView(_batchScroll, GUILayout.MinHeight(160f));
|
||
for (int i = 0; i < _batchSlots.Count; i++)
|
||
{
|
||
var slot = _batchSlots[i];
|
||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||
EditorGUILayout.BeginHorizontal();
|
||
EditorGUILayout.LabelField($"#{i}", GUILayout.Width(24));
|
||
slot.EmotionSetIndex = EditorGUILayout.IntField("Set N", slot.EmotionSetIndex, GUILayout.Width(120));
|
||
slot.Atlas = (Texture2D)EditorGUILayout.ObjectField(slot.Atlas, typeof(Texture2D), false);
|
||
slot.Txt = (TextAsset)EditorGUILayout.ObjectField(slot.Txt, typeof(TextAsset), false);
|
||
if (GUILayout.Button("×", GUILayout.Width(22)))
|
||
{
|
||
_batchSlots.RemoveAt(i);
|
||
i--;
|
||
EditorGUILayout.EndHorizontal();
|
||
EditorGUILayout.EndVertical();
|
||
continue;
|
||
}
|
||
|
||
EditorGUILayout.EndHorizontal();
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
|
||
EditorGUILayout.EndScrollView();
|
||
|
||
_libraryAssetPath = EditorGUILayout.TextField("Library .asset path", _libraryAssetPath);
|
||
|
||
EditorGUILayout.Space();
|
||
if (GUILayout.Button("Convert batch → EmotionLibrary.asset"))
|
||
{
|
||
RunConvertLibrary();
|
||
}
|
||
|
||
EditorGUILayout.Space(8f);
|
||
EditorGUILayout.HelpBox(
|
||
"Mỗi bộ: một texture **Sprite Multiple**, tên sub-sprite `cell_0000` … theo chỉ số ô (giống C++).\n" +
|
||
"Each set: one **Sprite Multiple** texture; sub-sprite names `cell_0000` … by cell index.",
|
||
MessageType.Info);
|
||
}
|
||
|
||
private int NextSuggestedSetIndex()
|
||
{
|
||
int max = -1;
|
||
foreach (var s in _batchSlots)
|
||
{
|
||
if (s.EmotionSetIndex > max)
|
||
max = s.EmotionSetIndex;
|
||
}
|
||
|
||
return max + 1;
|
||
}
|
||
|
||
private void RunConvertSingle()
|
||
{
|
||
if (!EmotionAtlasConverterCore.ConvertOneSet(
|
||
_sourceAtlas, _txtAsset, _emotionSetIndex, _cellW, _cellH, _outputFolder, _sliceInPlace,
|
||
out var snapshot, out string err))
|
||
{
|
||
EditorUtility.DisplayDialog("Error", err, "OK");
|
||
return;
|
||
}
|
||
|
||
var so = ScriptableObject.CreateInstance<EmotionSetDataSO>();
|
||
EmotionAtlasConverterCore.ApplySnapshotToSetSO(so, snapshot);
|
||
string setFolder = $"{_outputFolder}/Emotions{_emotionSetIndex}";
|
||
string soPath = $"{setFolder}/EmotionSetData_{_emotionSetIndex}.asset";
|
||
AssetDatabase.CreateAsset(so, soPath);
|
||
AssetDatabase.SaveAssets();
|
||
|
||
EditorUtility.DisplayDialog("Done", $"Đã tạo:\n{soPath}\nAtlas (Multiple) trong thư mục set (hoặc đã slice tại nguồn).", "OK");
|
||
EditorGUIUtility.PingObject(so);
|
||
}
|
||
|
||
private void RunConvertLibrary()
|
||
{
|
||
if (!EmotionAtlasConverterCore.TryValidateBatchIndices(_batchSlots, out string dupErr))
|
||
{
|
||
EditorUtility.DisplayDialog("Error", dupErr, "OK");
|
||
return;
|
||
}
|
||
|
||
var library = ScriptableObject.CreateInstance<EmotionLibrarySO>();
|
||
library.Sets.Clear();
|
||
|
||
int ok = 0;
|
||
int total = _batchSlots.Count;
|
||
for (int i = 0; i < total; i++)
|
||
{
|
||
var slot = _batchSlots[i];
|
||
if (slot.Atlas == null && slot.Txt == null)
|
||
continue;
|
||
|
||
if (slot.Atlas == null || slot.Txt == null)
|
||
{
|
||
EditorUtility.DisplayDialog("Error",
|
||
$"Slot #{i} (Set N={slot.EmotionSetIndex}): cần cả Atlas và TXT.",
|
||
"OK");
|
||
Object.DestroyImmediate(library);
|
||
return;
|
||
}
|
||
|
||
EditorUtility.DisplayProgressBar("Emotion Library", $"Set {slot.EmotionSetIndex}…", (float)i / Mathf.Max(1, total));
|
||
|
||
if (!EmotionAtlasConverterCore.ConvertOneSet(
|
||
slot.Atlas, slot.Txt, slot.EmotionSetIndex, _cellW, _cellH, _outputFolder, _sliceInPlace,
|
||
out var snapshot, out string err))
|
||
{
|
||
EditorUtility.ClearProgressBar();
|
||
EditorUtility.DisplayDialog("Error", $"Set {slot.EmotionSetIndex}: {err}", "OK");
|
||
Object.DestroyImmediate(library);
|
||
return;
|
||
}
|
||
|
||
library.Sets.Add(snapshot);
|
||
ok++;
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
|
||
if (ok == 0)
|
||
{
|
||
EditorUtility.DisplayDialog("Error", "Không có cặp Atlas+TXT hợp lệ.", "OK");
|
||
Object.DestroyImmediate(library);
|
||
return;
|
||
}
|
||
|
||
string dir = System.IO.Path.GetDirectoryName(_libraryAssetPath)?.Replace('\\', '/') ?? _outputFolder;
|
||
if (!string.IsNullOrEmpty(dir))
|
||
EmotionAtlasConverterCore.EnsureFolder(dir);
|
||
|
||
AssetDatabase.CreateAsset(library, _libraryAssetPath);
|
||
AssetDatabase.SaveAssets();
|
||
|
||
EditorUtility.DisplayDialog("Done",
|
||
$"EmotionLibrary: {ok} bộ.\n{_libraryAssetPath}",
|
||
"OK");
|
||
EditorGUIUtility.PingObject(library);
|
||
}
|
||
}
|
||
}
|