416 lines
15 KiB
C#
416 lines
15 KiB
C#
using System.Collections.Generic;
|
|
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");
|
|
|
|
public readonly bool isInFight;
|
|
private int _inFightEmojiId;
|
|
|
|
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");
|
|
|
|
internal UnitEmojiManager(GameObject obj,
|
|
string eyeColor,
|
|
string bowType,
|
|
string eyebowColor,
|
|
int inFightEmojiId)
|
|
{
|
|
isInFight = inFightEmojiId >= 0;
|
|
_inFightEmojiId = inFightEmojiId;
|
|
// 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;
|
|
var collector = new ComponentCollector(obj);
|
|
_skeleton = new Skeleton(collector.Transforms);
|
|
|
|
if (isInFight)
|
|
{
|
|
// 战斗模式
|
|
// _InitFightEmoji();
|
|
}
|
|
else
|
|
{
|
|
// 剧情模式
|
|
_BindObj();
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_cacheMaskTex != null)
|
|
{
|
|
Object.Destroy(_cacheMaskTex);
|
|
_cacheMaskTex = null;
|
|
}
|
|
}
|
|
|
|
private async void _InitFightEmoji()
|
|
{
|
|
var emojiObj = _skeleton.GetBone("F_emoji");
|
|
if (emojiObj != null)
|
|
{
|
|
// 删除
|
|
Object.Destroy(emojiObj.gameObject);
|
|
}
|
|
|
|
emojiObj = _skeleton.GetBone("G_emoji");
|
|
if (emojiObj != null)
|
|
{
|
|
// 删除
|
|
Object.Destroy(emojiObj.gameObject);
|
|
}
|
|
|
|
if (_inFightEmojiId == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 找到头部
|
|
var playerObj = rootObj.transform.GetChild(0).GetChild(0);
|
|
var headObj = playerObj.GetChild(1);
|
|
if (!headObj.name.EndsWith("_face"))
|
|
{
|
|
DebugUtil.LogError("找不到头部模型,取消创建默认表情:" + playerObj.name);
|
|
return;
|
|
}
|
|
var skinMeshRender = headObj.GetComponent<SkinnedMeshRenderer>();
|
|
if (skinMeshRender == null)
|
|
{
|
|
DebugUtil.LogError("找不到头部模型,取消创建默认表情:" + playerObj.name);
|
|
return;
|
|
}
|
|
var oldMat = skinMeshRender.sharedMaterial;
|
|
if (oldMat.shader.name != "NLD_URP/NLD_Charactor"
|
|
&& oldMat.shader.name != "NLD_URP/NLD_Charactor_NoOutline")
|
|
{
|
|
DebugUtil.LogError("头部模型使用的默认材质不对,取消创建默认表情:" + playerObj.name);
|
|
return;
|
|
}
|
|
var newMatSample = await AssetManager.Instance.LoadAssetAsync<Material>(Constants.EMOJI_HEAD_MAT);
|
|
if (newMatSample == null)
|
|
{
|
|
DebugUtil.LogError("找不到表情的材质球");
|
|
return;
|
|
}
|
|
var newMat = Object.Instantiate(newMatSample);
|
|
var oldFaceTex = oldMat.GetTexture(MainTex);
|
|
// 从贴图读取颜色,读取中心的颜色
|
|
var faceColor = _GetColorFromTex(oldFaceTex);
|
|
var shadowScale = oldMat.GetFloat(ShadowScale);
|
|
var oldMaskTex = oldMat.GetTexture(MaskTex);
|
|
var defaultFaceId = _inFightEmojiId;
|
|
// Assets/Art/Character/Models/Emoji/Tex/T_emoji_lian_01.png
|
|
var combieFaceTexPath = $"Assets/Art/Character/Models/Emoji/Tex/T_emoji_lian_{_GetFixNum(defaultFaceId.ToString())}.png";
|
|
var combieFaceTex = await AssetManager.Instance.LoadAssetAsync<Texture>(combieFaceTexPath);
|
|
// Assets/Art/Character/Models/Emoji/Tex/T_emoji_lian_01_mask.png
|
|
var combieFaceMaskTexPath = $"Assets/Art/Character/Models/Emoji/Tex/T_emoji_lian_{_GetFixNum(defaultFaceId.ToString())}_mask.png";
|
|
var combieFaceMaskTex = await AssetManager.Instance.LoadAssetAsync<Texture>(combieFaceMaskTexPath);
|
|
if (combieFaceTex == null || combieFaceMaskTex == null)
|
|
{
|
|
DebugUtil.LogError("找不到表情的贴图");
|
|
return;
|
|
}
|
|
// 将oldMaskTex和combieFaceMaskTex合并
|
|
var combieMaskTex = _CombieMaskTex(oldMaskTex, combieFaceMaskTex);
|
|
_cacheMaskTex = combieMaskTex;
|
|
|
|
// 设置材质
|
|
// _FaceTex ("FaceTexture", 2D) = "white" {}
|
|
// _MaskTex ("Mask", 2D) = "white" {}
|
|
// _SkinColor ("Skin Color", color) = (1,1,1,1)
|
|
// _EyeColor ("Eye Color", color) = (1,1,1,1)
|
|
// _EyeBrowColor ("EyeBrow Color", color) = (1,1,1,1)
|
|
// _shadowScale("ShadowScale", range(0, 1.0)) = 0.5
|
|
newMat.SetTexture(FaceTex, combieFaceTex);
|
|
newMat.SetTexture(MaskTex, combieMaskTex);
|
|
newMat.SetColor(SkinColor, faceColor);
|
|
newMat.SetColor(EyeColor, _eyeColor);
|
|
newMat.SetColor(EyeBrowColor, _eyeBowColor);
|
|
newMat.SetFloat(ShadowScale, shadowScale);
|
|
|
|
skinMeshRender.material = newMat;
|
|
}
|
|
|
|
private Texture _CombieMaskTex(Texture rMask,
|
|
Texture gbMask)
|
|
{
|
|
// 将rMask的r和gMask的gb合并
|
|
var rMask2D = rMask as Texture2D;
|
|
var gMask2D = gbMask as Texture2D;
|
|
if (rMask2D == null || gMask2D == null)
|
|
{
|
|
DebugUtil.LogError("表情的贴图不是Texture2D");
|
|
return null;
|
|
}
|
|
var width = rMask2D.width;
|
|
var height = rMask2D.height;
|
|
var pixels = rMask2D.GetPixels();
|
|
var pixels2 = gMask2D.GetPixels();
|
|
var newPixels = new Color[pixels.Length];
|
|
for (var i = 0; i < pixels.Length; ++i)
|
|
{
|
|
var r = pixels[i].r;
|
|
var g = pixels2[i].g;
|
|
var b = pixels2[i].b;
|
|
newPixels[i] = new Color(r, g, b, 0);
|
|
}
|
|
var newTex = new Texture2D(width, height);
|
|
newTex.SetPixels(newPixels);
|
|
newTex.Apply();
|
|
return newTex;
|
|
}
|
|
|
|
private Color _GetColorFromTex(Texture tex)
|
|
{
|
|
var tex2D = tex as Texture2D;
|
|
if (tex2D == null)
|
|
{
|
|
DebugUtil.LogError("表情的贴图不是Texture2D");
|
|
return Color.white;
|
|
}
|
|
var width = tex2D.width;
|
|
var height = tex2D.height;
|
|
return tex2D.GetPixel(width / 2, height / 2);
|
|
}
|
|
|
|
|
|
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("找不到表情的骨骼: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]);
|
|
_matMid = Object.Instantiate(mats[1]);
|
|
_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;
|
|
}
|
|
|
|
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 AssetManager.Instance.LoadAssetAsync<Texture>(imgPathBg);
|
|
}
|
|
if (_oldImgPathUp != imgPathUp)
|
|
{
|
|
_oldImgPathUp = imgPathUp;
|
|
var isWithMask = await AssetManager.Instance.CheckAssetInPack(imgPathUpMask);
|
|
if (isWithMask)
|
|
{
|
|
var maskTex = await AssetManager.Instance.LoadAssetAsync<Texture>(imgPathUpMask);
|
|
_matUp.SetTexture(MaskTex, maskTex);
|
|
}
|
|
else
|
|
{
|
|
_matUp.SetTexture(MaskTex, null);
|
|
}
|
|
_matUp.mainTexture = await AssetManager.Instance.LoadAssetAsync<Texture>(imgPathUp);
|
|
_matUp.SetColor(MainColor, _eyeBowColor);
|
|
}
|
|
if (_oldImgPathMid != imgPathMid)
|
|
{
|
|
_oldImgPathMid = imgPathMid;
|
|
var isWithMask = await AssetManager.Instance.CheckAssetInPack(imgPathMidMask);
|
|
if (isWithMask)
|
|
{
|
|
var maskTex = await AssetManager.Instance.LoadAssetAsync<Texture>(imgPathMidMask);
|
|
_matMid.SetTexture(MaskTex, maskTex);
|
|
}
|
|
else
|
|
{
|
|
_matMid.SetTexture(MaskTex, null);
|
|
}
|
|
_matMid.mainTexture = await AssetManager.Instance.LoadAssetAsync<Texture>(imgPathMid);
|
|
_matMid.SetColor(MainColor, _eyeColor);
|
|
}
|
|
if (_oldImgPathDown != imgPathDown)
|
|
{
|
|
_oldImgPathDown = imgPathDown;
|
|
_matDown.mainTexture = await AssetManager.Instance.LoadAssetAsync<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;
|
|
}
|
|
}
|
|
}
|