using System.Collections.Generic; using Cysharp.Threading.Tasks; using Framework; using Gameplay.Effect; using PhxhSDK; using UnityEngine; using Constants = Framework.Constants; namespace Gameplay.Emoji { public class UnitEmojiManager { // public readonly Character.Character character; public GameObject rootObj; private Material _matBg; private Material _matUp; private Material _matMid; private Material _matDown; private string _oldImgPathBg; private string _oldImgPathUp; private string _oldImgPathMid; private string _oldImgPathDown; private List _emojiEffectIds; private List _effectDatas; private Color _eyeColor; private Color _eyeBowColor; private string _bowType; private static readonly int MainColor = Shader.PropertyToID("_MainColor"); private static readonly int MaskTex = Shader.PropertyToID("_MaskTex"); private Skeleton _skeleton; private Texture _cacheMaskTex; private static readonly int MainTex = Shader.PropertyToID("_MainTex"); private static readonly int ShadowScale = Shader.PropertyToID("_shadowScale"); private static readonly int FaceTex = Shader.PropertyToID("_FaceTex"); private static readonly int SkinColor = Shader.PropertyToID("_SkinColor"); private static readonly int EyeColor = Shader.PropertyToID("_EyeColor"); private static readonly int EyeBrowColor = Shader.PropertyToID("_EyeBrowColor"); private Dictionary _cacheLoadCount = new Dictionary(); public bool forceTop; private static readonly int ZTest = Shader.PropertyToID("_ZTest"); internal UnitEmojiManager(GameObject obj, string eyeColor, string bowType, string eyebowColor, bool forceTop = false) { // Character_1 rootObj = obj; _eyeColor = ColorUtility.TryParseHtmlString(eyeColor, out var color) ? color : Color.white; _eyeBowColor = ColorUtility.TryParseHtmlString(eyebowColor, out var color2) ? color2 : Color.white; _bowType = bowType; if (obj.transform.childCount <= 0) { DebugUtil.LogError("节点不匹配:" + obj.name); return; } var collectorTrans = obj.transform.GetChild(0); var collector = new ComponentCollector(collectorTrans.gameObject); _skeleton = new Skeleton(collector.Transforms, obj.transform); this.forceTop = forceTop; _BindObj(); } public void Dispose() { if (_cacheMaskTex != null) { Object.Destroy(_cacheMaskTex); _cacheMaskTex = null; } _UnloadLoadTextures(); } private void _UnloadLoadTextures() { foreach (var kv in _cacheLoadCount) { var path = kv.Key; var count = kv.Value; for (var i = 0; i < count; ++i) { AssetManager.Instance.Unload(path); } } } private void _BindObj() { var emojiObj = _skeleton.GetBone("F_emoji"); if (emojiObj != null) { // 删除 Object.Destroy(emojiObj.gameObject); } var newEmojiObj = _skeleton.GetBone("G_emoji"); if (newEmojiObj == null) { DebugUtil.LogError($"模型 {rootObj.name} 找不到表情的骨骼:G_emoji"); return; } rootObj = newEmojiObj.gameObject; var meshRender = rootObj.GetComponentInChildren(); // 这里直接去Inst的 var mats = meshRender.sharedMaterials; if (mats.Length != 4) { DebugUtil.LogError("表情的材质球数量不对"); return; } _matUp = Object.Instantiate(mats[0]); // 设置_ZTest if (forceTop) { _matUp.SetInt(ZTest, (int)UnityEngine.Rendering.CompareFunction.Always); } else { _matUp.SetInt(ZTest, (int)UnityEngine.Rendering.CompareFunction.LessEqual); } _matMid = Object.Instantiate(mats[1]); // 设置_ZTest if (forceTop) { _matMid.SetInt(ZTest, (int)UnityEngine.Rendering.CompareFunction.Always); } else { _matMid.SetInt(ZTest, (int)UnityEngine.Rendering.CompareFunction.LessEqual); } _matDown = Object.Instantiate(mats[2]); _matBg = Object.Instantiate(mats[3]); meshRender.materials = new[] { _matUp, _matMid, _matDown, _matBg }; _emojiEffectIds = new List(); _effectDatas = new List(); } public void Show() { if (rootObj == null) { DebugUtil.LogError("表情的根节点为空"); return; } if (rootObj.activeSelf) return; rootObj.SetActive(true); } public void Hide() { if (!rootObj.activeSelf) return; rootObj.SetActive(false); } private bool _CheckIsEmpty(string imgName) { if (string.IsNullOrEmpty(imgName)) return true; if (imgName == "0") return true; if (!int.TryParse(imgName, out var result)) { return true; } if (result == 0) { return true; } return false; } private string _GetFixNum(string imgName) { if (imgName.Length < 2) { return "0" + imgName; } return imgName; } private async UniTask _LoadTexture(string imgPath) where T : Texture { if (_cacheLoadCount.ContainsKey(imgPath)) { _cacheLoadCount[imgPath]++; } else { _cacheLoadCount[imgPath] = 1; } return await AssetManager.Instance.LoadAssetAsync(imgPath); } public async void SwitchEmoji(int emojiId) { if (_matUp == null) return; if (emojiId == 0) return; #if UNITY_EDITOR var emojiCfg = EditorTableManager.instance.tables.EmojiConfig.Get(emojiId); #else var emojiCfg = TableManager.Instance.Tables.EmojiConfig.Get(emojiId); #endif if (emojiCfg == null) { DebugUtil.LogError("无效的emojiId:{0}", emojiId); return; } // 眉毛 T_emoji_eyebows_1_01 // 眼睛 T_emoji_eyes_01 // 嘴巴 T_emoji_mouth_01 // 背景 T_emoji_acc_01 const string emptyPath = "T_emoji_empty"; const string prefix = "Assets/Art/Character/Models/Emoji/Tex/"; var imgPathBg = _CheckIsEmpty(emojiCfg.BgImg) ? emptyPath : $"T_emoji_acc_{_GetFixNum(emojiCfg.BgImg)}"; var imgPathUp = _CheckIsEmpty(emojiCfg.UpImg) ? emptyPath : $"T_emoji_eyebows_{_bowType}_{_GetFixNum(emojiCfg.UpImg)}"; var imgPathUpMask = _CheckIsEmpty(emojiCfg.UpImg) ? emptyPath : $"T_emoji_eyebows_{_bowType}_{_GetFixNum(emojiCfg.UpImg)}_mask"; var imgPathMid = _CheckIsEmpty(emojiCfg.MidImg) ? emptyPath : $"T_emoji_eyes_{_GetFixNum(emojiCfg.MidImg)}"; var imgPathMidMask = _CheckIsEmpty(emojiCfg.MidImg) ? emptyPath : $"T_emoji_eyes_{_GetFixNum(emojiCfg.MidImg)}_mask"; var imgPathDown = _CheckIsEmpty(emojiCfg.DownImg) ? emptyPath : $"T_emoji_mouth_{_GetFixNum(emojiCfg.DownImg)}"; imgPathBg = prefix + imgPathBg + ".png"; imgPathUp = prefix + imgPathUp + ".png"; imgPathMid = prefix + imgPathMid + ".png"; imgPathDown = prefix + imgPathDown + ".png"; imgPathMidMask = prefix + imgPathMidMask + ".png"; imgPathUpMask = prefix + imgPathUpMask + ".png"; if (_oldImgPathBg != imgPathBg) { _oldImgPathBg = imgPathBg; _matBg.mainTexture = await _LoadTexture(imgPathBg); } if (_oldImgPathUp != imgPathUp) { _oldImgPathUp = imgPathUp; var isWithMask = AssetManager.Instance.CanLocateAsset(imgPathUpMask); if (isWithMask) { var maskTex = await _LoadTexture(imgPathUpMask); _matUp.SetTexture(MaskTex, maskTex); } else { _matUp.SetTexture(MaskTex, null); } _matUp.mainTexture = await _LoadTexture(imgPathUp); _matUp.SetColor(MainColor, _eyeBowColor); } if (_oldImgPathMid != imgPathMid) { _oldImgPathMid = imgPathMid; var isWithMask = AssetManager.Instance.CanLocateAsset(imgPathMidMask); if (isWithMask) { var maskTex = await _LoadTexture(imgPathMidMask); _matMid.SetTexture(MaskTex, maskTex); } else { _matMid.SetTexture(MaskTex, null); } _matMid.mainTexture = await _LoadTexture(imgPathMid); _matMid.SetColor(MainColor, _eyeColor); } if (_oldImgPathDown != imgPathDown) { _oldImgPathDown = imgPathDown; _matDown.mainTexture = await _LoadTexture(imgPathDown); } // 特效的判定 var effectIds = emojiCfg.EffectIds; var isSame = _CheckSame(effectIds, _emojiEffectIds); if (!isSame) { // 销毁旧的 for (var i = 0; i < _effectDatas.Count; ++i) { var effectData = _effectDatas[i]; effectData.Destroy(); } _effectDatas.Clear(); _emojiEffectIds = effectIds; if (Application.isPlaying) { // 获取特效 for (var i = 0; i < _emojiEffectIds.Count; ++i) { var effectId = _emojiEffectIds[i]; var effectData = EffectManager.instance.PlayEffectById(effectId, rootObj.transform.position, rootObj.transform.rotation, rootObj.transform); _effectDatas.Add(effectData); } } } Show(); } private static bool _CheckSame(IReadOnlyList a, IReadOnlyList b) { if (a.Count != b.Count) return false; for (var i = 0; i < a.Count; ++i) { if (a[i] != b[i]) return false; } return true; } } }