add tool change UI
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63ad32ae8f64ef1449e51fe4460570a8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,348 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BrewMonster.Scripts.Chat.EmotionData;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster.Scripts.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert một bộ: ghi atlas (hoặc slice tại chỗ) + TextureImporter Sprite Multiple full lưới,
|
||||
/// gán FrameSprites theo tên cell_XXXX khớp C++ (row-major, hàng trên = row 0).
|
||||
/// </summary>
|
||||
public static class EmotionAtlasConverterCore
|
||||
{
|
||||
public const string CellSpriteNamePrefix = "cell_";
|
||||
|
||||
public static string CellSpriteName(int cellIndex) => $"{CellSpriteNamePrefix}{cellIndex:D4}";
|
||||
|
||||
/// <param name="sliceInPlace">true = chỉnh importer trên asset atlas hiện tại (ghi đè import settings). false = copy PNG vào thư mục output rồi slice (an toàn hơn).</param>
|
||||
public static bool ConvertOneSet(
|
||||
Texture2D sourceAtlas,
|
||||
TextAsset txtAsset,
|
||||
int emotionSetIndex,
|
||||
int cellW,
|
||||
int cellH,
|
||||
string outputRootFolder,
|
||||
bool sliceInPlace,
|
||||
out EmotionSetSnapshot snapshot,
|
||||
out string error)
|
||||
{
|
||||
snapshot = null;
|
||||
error = null;
|
||||
|
||||
if (sourceAtlas == null)
|
||||
{
|
||||
error = "Atlas null.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (txtAsset == null)
|
||||
{
|
||||
error = "TextAsset null.";
|
||||
return false;
|
||||
}
|
||||
|
||||
string sourceAtlasPath = AssetDatabase.GetAssetPath(sourceAtlas);
|
||||
if (string.IsNullOrEmpty(sourceAtlasPath))
|
||||
{
|
||||
error = "Atlas không phải asset trong project.";
|
||||
return false;
|
||||
}
|
||||
|
||||
var parseResult = EmotionTxtParser.Parse(txtAsset.text);
|
||||
if (!parseResult.Success)
|
||||
{
|
||||
error = parseResult.ErrorMessage;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cellW <= 0 || cellH <= 0)
|
||||
{
|
||||
error = "Cell size phải > 0.";
|
||||
return false;
|
||||
}
|
||||
|
||||
Texture2D readable = GetReadableTexture(sourceAtlas);
|
||||
if (readable == null)
|
||||
{
|
||||
error = "Không đọc được pixel từ atlas.";
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int texW = readable.width;
|
||||
int texH = readable.height;
|
||||
if (texW % cellW != 0 || texH % cellH != 0)
|
||||
{
|
||||
error = $"Texture ({texW}x{texH}) không chia hết cho cell ({cellW}x{cellH}).";
|
||||
return false;
|
||||
}
|
||||
|
||||
int nNumX = texW / cellW;
|
||||
int nNumY = texH / cellH;
|
||||
int totalCells = nNumX * nNumY;
|
||||
|
||||
foreach (var e in parseResult.Entries)
|
||||
{
|
||||
if (e.NumFrames > 0 && e.StartPos + e.NumFrames - 1 >= totalCells)
|
||||
{
|
||||
error = $"Set {emotionSetIndex}: StartPos={e.StartPos}, NumFrames={e.NumFrames} vượt lưới ({totalCells} ô).";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
EnsureFolder(outputRootFolder);
|
||||
string setFolder = $"{outputRootFolder}/Emotions{emotionSetIndex}";
|
||||
if (!AssetDatabase.IsValidFolder(setFolder))
|
||||
AssetDatabase.CreateFolder(outputRootFolder, $"Emotions{emotionSetIndex}");
|
||||
|
||||
string atlasAssetPath;
|
||||
Texture2D atlasForSo;
|
||||
|
||||
if (sliceInPlace)
|
||||
{
|
||||
atlasAssetPath = sourceAtlasPath;
|
||||
if (!ApplyMultipleSpriteSheet(atlasAssetPath, texW, texH, cellW, cellH, nNumX, nNumY, out error))
|
||||
return false;
|
||||
atlasForSo = AssetDatabase.LoadAssetAtPath<Texture2D>(atlasAssetPath);
|
||||
if (atlasForSo == null)
|
||||
{
|
||||
error = "Không load lại Texture2D sau slice.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string fileName = $"Emotions{emotionSetIndex}_atlas.png";
|
||||
atlasAssetPath = $"{setFolder}/{fileName}";
|
||||
byte[] png = readable.EncodeToPNG();
|
||||
File.WriteAllBytes(atlasAssetPath, png);
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
if (!ApplyMultipleSpriteSheet(atlasAssetPath, texW, texH, cellW, cellH, nNumX, nNumY, out error))
|
||||
return false;
|
||||
|
||||
atlasForSo = AssetDatabase.LoadAssetAtPath<Texture2D>(atlasAssetPath);
|
||||
if (atlasForSo == null)
|
||||
{
|
||||
error = $"Không load texture tại {atlasAssetPath}";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var spriteByName = LoadSpritesByName(atlasAssetPath);
|
||||
if (spriteByName.Count < totalCells)
|
||||
{
|
||||
error = $"Chỉ tìm thấy {spriteByName.Count}/{totalCells} sprite sau import.";
|
||||
return false;
|
||||
}
|
||||
|
||||
snapshot = new EmotionSetSnapshot
|
||||
{
|
||||
EmotionSetIndex = emotionSetIndex,
|
||||
CellWidth = cellW,
|
||||
CellHeight = cellH,
|
||||
SourceAtlas = atlasForSo,
|
||||
SourceTxtAssetPath = AssetDatabase.GetAssetPath(txtAsset),
|
||||
Entries = new List<EmotionEntryData>()
|
||||
};
|
||||
|
||||
foreach (var src in parseResult.Entries)
|
||||
{
|
||||
var copy = new EmotionEntryData
|
||||
{
|
||||
StartPos = src.StartPos,
|
||||
NumFrames = src.NumFrames,
|
||||
Hint = src.Hint,
|
||||
FrameTicks = (int[])src.FrameTicks.Clone(),
|
||||
FrameSprites = new Sprite[src.NumFrames]
|
||||
};
|
||||
|
||||
for (int f = 0; f < src.NumFrames; f++)
|
||||
{
|
||||
int cellIndex = src.StartPos + f;
|
||||
string key = CellSpriteName(cellIndex);
|
||||
if (!spriteByName.TryGetValue(key, out var spr) || spr == null)
|
||||
{
|
||||
error = $"Thiếu sprite '{key}' trong atlas.";
|
||||
return false;
|
||||
}
|
||||
|
||||
copy.FrameSprites[f] = spr;
|
||||
}
|
||||
|
||||
snapshot.Entries.Add(copy);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (readable != null && readable != sourceAtlas)
|
||||
Object.DestroyImmediate(readable);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ApplyMultipleSpriteSheet(
|
||||
string assetPath,
|
||||
int texW,
|
||||
int texH,
|
||||
int cellW,
|
||||
int cellH,
|
||||
int nNumX,
|
||||
int nNumY,
|
||||
out string error)
|
||||
{
|
||||
error = null;
|
||||
var importer = AssetImporter.GetAtPath(assetPath) as TextureImporter;
|
||||
if (importer == null)
|
||||
{
|
||||
error = $"Không mở TextureImporter: {assetPath}";
|
||||
return false;
|
||||
}
|
||||
|
||||
importer.textureType = TextureImporterType.Sprite;
|
||||
importer.spriteImportMode = SpriteImportMode.Multiple;
|
||||
importer.spritePixelsPerUnit = 100;
|
||||
importer.mipmapEnabled = false;
|
||||
importer.alphaIsTransparency = true;
|
||||
importer.maxTextureSize = Mathf.Max(importer.maxTextureSize, Mathf.Max(texW, texH));
|
||||
importer.spritesheet = BuildSpriteMetaData(texW, texH, cellW, cellH, nNumX, nNumY);
|
||||
|
||||
importer.SetPlatformTextureSettings(new TextureImporterPlatformSettings
|
||||
{
|
||||
name = "Default",
|
||||
overridden = false
|
||||
});
|
||||
|
||||
importer.SaveAndReimport();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static SpriteMetaData[] BuildSpriteMetaData(int texW, int texH, int cellW, int cellH, int nNumX, int nNumY)
|
||||
{
|
||||
var list = new List<SpriteMetaData>(nNumX * nNumY);
|
||||
for (int row = 0; row < nNumY; row++)
|
||||
{
|
||||
for (int col = 0; col < nNumX; col++)
|
||||
{
|
||||
int cellIndex = row * nNumX + col;
|
||||
float x = col * cellW;
|
||||
float y = texH - (row + 1) * cellH;
|
||||
list.Add(new SpriteMetaData
|
||||
{
|
||||
name = CellSpriteName(cellIndex),
|
||||
rect = new Rect(x, y, cellW, cellH),
|
||||
pivot = new Vector2(0.5f, 0.5f),
|
||||
alignment = (int)SpriteAlignment.Center,
|
||||
border = Vector4.zero
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
private static Dictionary<string, Sprite> LoadSpritesByName(string atlasAssetPath)
|
||||
{
|
||||
var map = new Dictionary<string, Sprite>();
|
||||
foreach (var o in AssetDatabase.LoadAllAssetsAtPath(atlasAssetPath))
|
||||
{
|
||||
if (o is Sprite sp && !string.IsNullOrEmpty(sp.name))
|
||||
map[sp.name] = sp;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public static void EnsureFolder(string folder)
|
||||
{
|
||||
if (AssetDatabase.IsValidFolder(folder))
|
||||
return;
|
||||
|
||||
string parent = "Assets";
|
||||
foreach (var part in folder.Replace('\\', '/').Split('/'))
|
||||
{
|
||||
if (string.IsNullOrEmpty(part) || part == "Assets")
|
||||
continue;
|
||||
string newPath = parent + "/" + part;
|
||||
if (!AssetDatabase.IsValidFolder(newPath))
|
||||
AssetDatabase.CreateFolder(parent, part);
|
||||
parent = newPath;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ApplySnapshotToSetSO(EmotionSetDataSO so, EmotionSetSnapshot snap)
|
||||
{
|
||||
if (so == null || snap == null) return;
|
||||
so.EmotionSetIndex = snap.EmotionSetIndex;
|
||||
so.CellWidth = snap.CellWidth;
|
||||
so.CellHeight = snap.CellHeight;
|
||||
so.SourceAtlas = snap.SourceAtlas;
|
||||
so.SourceTxtAssetPath = snap.SourceTxtAssetPath;
|
||||
so.Entries.Clear();
|
||||
foreach (var e in snap.Entries)
|
||||
so.Entries.Add(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kiểm tra trùng EmotionSetIndex giữa các slot đang có đủ Atlas+Txt.
|
||||
/// </summary>
|
||||
public static bool TryValidateBatchIndices(IReadOnlyList<EmotionBatchSlot> slots, out string error)
|
||||
{
|
||||
error = null;
|
||||
var used = new HashSet<int>();
|
||||
foreach (var s in slots)
|
||||
{
|
||||
if (s?.Atlas == null && s?.Txt == null)
|
||||
continue;
|
||||
if (s.Atlas == null || s.Txt == null)
|
||||
continue;
|
||||
if (!used.Add(s.EmotionSetIndex))
|
||||
{
|
||||
error = $"Trùng Emotion set index: {s.EmotionSetIndex}";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Texture2D GetReadableTexture(Texture2D atlas)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dup = new Texture2D(atlas.width, atlas.height, TextureFormat.RGBA32, false);
|
||||
var prev = RenderTexture.active;
|
||||
var rt = RenderTexture.GetTemporary(atlas.width, atlas.height, 0, RenderTextureFormat.ARGB32);
|
||||
Graphics.Blit(atlas, rt);
|
||||
RenderTexture.active = rt;
|
||||
dup.ReadPixels(new Rect(0, 0, atlas.width, atlas.height), 0, 0);
|
||||
dup.Apply(false, false);
|
||||
RenderTexture.active = prev;
|
||||
RenderTexture.ReleaseTemporary(rt);
|
||||
return dup;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Một dòng batch: index bộ (N) + atlas + txt — có thể thêm/bớt trong cửa sổ tool.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public class EmotionBatchSlot
|
||||
{
|
||||
[Tooltip("N trong Emotions{N} (thư mục output & tên file atlas copy).")]
|
||||
public int EmotionSetIndex;
|
||||
|
||||
public Texture2D Atlas;
|
||||
public TextAsset Txt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38dfc1555b7238a4c99647d8d8dcb7e4
|
||||
@@ -0,0 +1,229 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd89b10678fe79046af39970b5c83b4c
|
||||
Reference in New Issue
Block a user