124 lines
4.8 KiB
C#
124 lines
4.8 KiB
C#
using System;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
|
|
namespace BrewMonster.Scripts.Chat.EmotionData
|
|
{
|
|
/// <summary>
|
|
/// Ánh xạ (emotionSet, emotionIndex) từ <see cref="EmotionLibrarySO"/> sang tag TMP.
|
|
/// Gán asset này vào <see cref="CECUIManager"/> (field emotion) — <see cref="BrewMonster.UI.CECGameUIMan"/> không phải MonoBehaviour nên không kéo SO trên Inspector được.
|
|
///
|
|
/// Cách dùng:
|
|
/// 1) Tạo <see cref="EmotionLibrarySO"/> bằng Emotion Atlas Converter.
|
|
/// 2) Tạo <see cref="TMP_SpriteAsset"/> từ cùng atlas (Window → TextMeshPro → Sprite Importer),
|
|
/// đảm bảo tên sub-sprite khớp (vd. cell_0000) với atlas Multiple từ tool.
|
|
/// 3) Gán TMP_SpriteAsset vào ô chat <see cref="TMPro.TextMeshProUGUI"/> (Sprite Asset / Additional).
|
|
/// 4) Kéo SO này vào field trên GameObject có <see cref="CECUIManager"/> (Awake gọi SetEmotionSpriteMap).
|
|
/// </summary>
|
|
[CreateAssetMenu(fileName = "EmotionLibrarySpriteMap", menuName = "Perfect World/Chat/Emotion Library Sprite Map", order = 2)]
|
|
public class EmotionLibrarySpriteMap : ScriptableObject, IEmotionSpriteMap
|
|
{
|
|
[Tooltip("Dữ liệu emotion đã convert (Entries + FrameSprites).")]
|
|
public EmotionLibrarySO Library;
|
|
|
|
[Tooltip("Sprite Asset dùng cho chat TMP — cùng tên sub-sprite với atlas (cell_XXXX). Bắt buộc cho emoji động (anim) hoặc khi không dùng name tag.")]
|
|
public TMP_SpriteAsset TmpSpriteAsset;
|
|
|
|
[Tooltip("Nếu true và chỉ 1 frame: thử <sprite name=\"...\"> (không cần tra index). Động (nhiều frame) vẫn cần TmpSpriteAsset để tra index.")]
|
|
public bool PreferSpriteNameTag = true;
|
|
|
|
[Tooltip("FPS mặc định cho <sprite anim> khi không suy ra được từ FrameTicks.")]
|
|
public int DefaultAnimFps = 10;
|
|
|
|
public bool TryGetSprite(int emotionSet, int emotionIndex, out EmotionSpriteInfo info)
|
|
{
|
|
info = default;
|
|
|
|
if (Library == null)
|
|
return false;
|
|
|
|
EmotionSetSnapshot set = Library.GetSetOrNull(emotionSet);
|
|
if (set == null || emotionIndex < 0 || emotionIndex >= set.Entries.Count)
|
|
return false;
|
|
|
|
EmotionEntryData entry = set.Entries[emotionIndex];
|
|
Sprite[] frames = entry.FrameSprites;
|
|
if (frames == null || frames.Length == 0)
|
|
return false;
|
|
|
|
if (frames.Length == 1)
|
|
{
|
|
Sprite s0 = frames[0];
|
|
if (s0 == null)
|
|
return false;
|
|
|
|
if (PreferSpriteNameTag && !string.IsNullOrEmpty(s0.name))
|
|
{
|
|
info = new EmotionSpriteInfo
|
|
{
|
|
UseSpriteName = true,
|
|
SpriteName = s0.name,
|
|
IsAnimated = false
|
|
};
|
|
return true;
|
|
}
|
|
|
|
int idx = FindSpriteIndexInTmpAsset(s0.name);
|
|
if (idx < 0)
|
|
return false;
|
|
|
|
info = new EmotionSpriteInfo { SpriteIndex = idx, IsAnimated = false };
|
|
return true;
|
|
}
|
|
|
|
if (TmpSpriteAsset == null)
|
|
return false;
|
|
|
|
int start = FindSpriteIndexInTmpAsset(frames[0]?.name);
|
|
int end = FindSpriteIndexInTmpAsset(frames[frames.Length - 1]?.name);
|
|
if (start < 0 || end < 0)
|
|
return false;
|
|
|
|
int fps = EstimateFps(entry.FrameTicks, frames.Length);
|
|
info = new EmotionSpriteInfo
|
|
{
|
|
SpriteIndex = start,
|
|
IsAnimated = true,
|
|
AnimEndFrame = end,
|
|
AnimFPS = fps > 0 ? fps : DefaultAnimFps
|
|
};
|
|
return true;
|
|
}
|
|
|
|
private int FindSpriteIndexInTmpAsset(string spriteName)
|
|
{
|
|
if (TmpSpriteAsset == null || string.IsNullOrEmpty(spriteName))
|
|
return -1;
|
|
|
|
if (TmpSpriteAsset.spriteInfoList != null)
|
|
{
|
|
for (int i = 0; i < TmpSpriteAsset.spriteInfoList.Count; i++)
|
|
{
|
|
var s = TmpSpriteAsset.spriteInfoList[i];
|
|
if (s != null && s.name == spriteName)
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
private static int EstimateFps(int[] frameTicks, int numFrames)
|
|
{
|
|
if (frameTicks == null || numFrames < 2 || frameTicks.Length < numFrames)
|
|
return 0;
|
|
int last = frameTicks[numFrames - 1];
|
|
int first = frameTicks[0];
|
|
int span = last - first;
|
|
if (span <= 0)
|
|
return 0;
|
|
return Mathf.Max(1, Mathf.RoundToInt((numFrames - 1) * 60f / span));
|
|
}
|
|
}
|
|
}
|