332 lines
11 KiB
C#
332 lines
11 KiB
C#
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<int> _emojiEffectIds;
|
|
private List<EffectPlayingData> _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<string, int> _cacheLoadCount = new Dictionary<string, int>();
|
|
|
|
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<MeshRenderer>();
|
|
// 这里直接去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<int>();
|
|
_effectDatas = new List<EffectPlayingData>();
|
|
}
|
|
|
|
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<T> _LoadTexture<T>(string imgPath) where T : Texture
|
|
{
|
|
if (_cacheLoadCount.ContainsKey(imgPath))
|
|
{
|
|
_cacheLoadCount[imgPath]++;
|
|
}
|
|
else
|
|
{
|
|
_cacheLoadCount[imgPath] = 1;
|
|
}
|
|
return await AssetManager.Instance.LoadAssetAsync<T>(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<Texture>(imgPathBg);
|
|
}
|
|
if (_oldImgPathUp != imgPathUp)
|
|
{
|
|
_oldImgPathUp = imgPathUp;
|
|
var isWithMask = AssetManager.Instance.CanLocateAsset<Texture>(imgPathUpMask);
|
|
if (isWithMask)
|
|
{
|
|
var maskTex = await _LoadTexture<Texture>(imgPathUpMask);
|
|
_matUp.SetTexture(MaskTex, maskTex);
|
|
}
|
|
else
|
|
{
|
|
_matUp.SetTexture(MaskTex, null);
|
|
}
|
|
_matUp.mainTexture = await _LoadTexture<Texture>(imgPathUp);
|
|
_matUp.SetColor(MainColor, _eyeBowColor);
|
|
}
|
|
if (_oldImgPathMid != imgPathMid)
|
|
{
|
|
_oldImgPathMid = imgPathMid;
|
|
var isWithMask = AssetManager.Instance.CanLocateAsset<Texture>(imgPathMidMask);
|
|
if (isWithMask)
|
|
{
|
|
var maskTex = await _LoadTexture<Texture>(imgPathMidMask);
|
|
_matMid.SetTexture(MaskTex, maskTex);
|
|
}
|
|
else
|
|
{
|
|
_matMid.SetTexture(MaskTex, null);
|
|
}
|
|
_matMid.mainTexture = await _LoadTexture<Texture>(imgPathMid);
|
|
_matMid.SetColor(MainColor, _eyeColor);
|
|
}
|
|
if (_oldImgPathDown != imgPathDown)
|
|
{
|
|
_oldImgPathDown = imgPathDown;
|
|
_matDown.mainTexture = await _LoadTexture<Texture>(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<int> a,
|
|
IReadOnlyList<int> 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;
|
|
}
|
|
}
|
|
}
|