NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Gameplay/Emoji/UnitEmojiManager.cs

423 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 string[] EMOJI_MASK_IDS = new string[]
{
"12", "13",
"14", "15"
};
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;
}
private bool _WithMask(string imgName)
{
for (int i = 0; i < EMOJI_MASK_IDS.Length; i++)
{
var maskId = EMOJI_MASK_IDS[i];
if (imgName == maskId) return true;
}
return false;
}
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 imgPathMid = _CheckIsEmpty(emojiCfg.MidImg) ? emptyPath : $"T_emoji_eyes_{_GetFixNum(emojiCfg.MidImg)}";
var imgPathDown = _CheckIsEmpty(emojiCfg.DownImg) ? emptyPath : $"T_emoji_mouth_{_GetFixNum(emojiCfg.DownImg)}";
var maskPath = emptyPath;
var isWithMask = _WithMask(emojiCfg.MidImg);
if (isWithMask)
{
maskPath = imgPathMid + "_mask";
}
imgPathBg = prefix + imgPathBg + ".png";
imgPathUp = prefix + imgPathUp + ".png";
imgPathMid = prefix + imgPathMid + ".png";
imgPathDown = prefix + imgPathDown + ".png";
maskPath = prefix + maskPath + ".png";
if (_oldImgPathBg != imgPathBg)
{
_oldImgPathBg = imgPathBg;
_matBg.mainTexture = await AssetManager.Instance.LoadAssetAsync<Texture>(imgPathBg);
}
if (_oldImgPathUp != imgPathUp)
{
_oldImgPathUp = imgPathUp;
_matUp.mainTexture = await AssetManager.Instance.LoadAssetAsync<Texture>(imgPathUp);
}
if (_oldImgPathMid != imgPathMid)
{
_oldImgPathMid = imgPathMid;
if (isWithMask)
{
var maskTex = await AssetManager.Instance.LoadAssetAsync<Texture>(maskPath);
_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;
}
}
}