Files
test/Assets/PerfectWorld/Scripts/Editor/EmotionAtlasConverterWindow.cs
T
2026-04-06 11:21:51 +07:00

230 lines
9.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}
}