using System; using System.Collections.Generic; using System.Text; using Entitas; using Entitas.CodeGeneration.Attributes; using UnityEngine; namespace CoreGame.Render { [Combat, Unique] public class JumpTextComponent : IComponent, IReset { public Queue hurtDatas = new Queue(); private readonly Dictionary m_JumpTextListDict = new(); private const string SpriteTextPath = "Model/JumpText/SpriteJumpText"; private const string CriticalTextPath = "Model/JumpText/CritJumpText"; private const string NormalTextPath = "Model/JumpText/NormalJumpText"; private const string CureTextPath = "Model/JumpText/CureJumpText"; private const string HurtMissImagePath = "Assets/RawResources/UI/CoreGame/Hurt/hurt_miss.png"; public static bool s_ShowJumpText = true; private static float s_HalfRandomAngle = BattleConst.JumpTextAngle / 2; private static readonly StringBuilder s_StringBuilder = new StringBuilder(); private static readonly int[] s_Nums = new int[4]; private const int s_CharDictWidth_CN = 13; private static char[] s_CharDict_CN = { //1,2,3,4,5,6,7,8,9,0,.,万,亿 '\"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', //普通 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '<', ':', ';', //暴击 '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'J', 'H', 'I', //火元素 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'X', 'V', 'W', //雷元素 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'e', 'c', 'd', //毒元素 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r' //治疗 }; private static int[] s_NumIndex = { 9, 0, 1, 2, 3, 4, 5, 6, 7, 8 }; private static string ConvertValueToStr_CN(long value, int typeIndex) { s_StringBuilder.Clear(); Array.Fill(s_Nums, 0); if (value < 10000) { for (int i = 0; i < 4; i++) { var num = (int)(value / Math.Pow(10, 3 - i)) % 10; s_Nums[i] = num; } int startIndex = 0; while (s_Nums[startIndex] == 0) { startIndex++; } for (int i = startIndex; i < 4; i++) { s_StringBuilder.Append(s_CharDict_CN[typeIndex * s_CharDictWidth_CN + s_NumIndex[s_Nums[i]]]); } } else { // 10000亿 if (value >= 1000000000000) { value = 999999999999; } //1亿 var biggerThanOneHundredMillion = value >= 100000000; double temp = biggerThanOneHundredMillion ? value * 0.00000001 : value * 0.0001; // temp 肯定大于1, 若小于1,算法不成立 double tempScale = 1; int count = 0; while (temp * tempScale < 1000) { tempScale *= 10; count++; } temp *= tempScale; for (int i = 0; i < 4; i++) { s_Nums[i] = (int)(temp / Math.Pow(10, 3 - i)) % 10; } var pointIndex = 3 - count; for (int i = 0; i < 4; i++) { s_StringBuilder.Append(s_CharDict_CN[typeIndex * s_CharDictWidth_CN + s_NumIndex[s_Nums[i]]]); if (pointIndex == i && pointIndex != 3) { s_StringBuilder.Append(s_CharDict_CN[typeIndex * s_CharDictWidth_CN + 10]); } } s_StringBuilder.Append(biggerThanOneHundredMillion ? s_CharDict_CN[typeIndex * s_CharDictWidth_CN + 12] : s_CharDict_CN[typeIndex * s_CharDictWidth_CN + 11]); } return s_StringBuilder.ToString(); } private string ConvertValueToStr_Temp(long value, int typeIndex) { s_StringBuilder.Clear(); long temp = value; int length = 0; // 计算数字的位数 while (temp != 0) { temp /= 10; length++; } // 存储每一位数字 int[] digits = new int[length]; // 填充数组 temp = value; for (int i = length - 1; i >= 0; i--) { digits[i] = (int)(temp % 10); temp /= 10; } // 输出每一位数字 foreach (var digit in digits) { s_StringBuilder.Append(s_CharDict_CN[typeIndex * s_CharDictWidth_CN + s_NumIndex[digit]]); } return s_StringBuilder.ToString(); } private string GetJumpTextContent(long value, int typeIndex) { return ConvertValueToStr_CN(value, typeIndex); // return ConvertValueToStr_Temp(value, typeIndex); } public void Reset() { hurtDatas.Clear(); foreach (var param in m_JumpTextListDict) { var paramValue = param.Value; GoPoolDic.GetPool(paramValue.reuseGoData.path).ReleaseGo(paramValue.reuseGoData); SptPool.Free(ref paramValue); } m_JumpTextListDict.Clear(); } public void PushHurtData(HurtData hurtData) { if (!s_ShowJumpText) return; if (hurtDatas.Count == 0) { Contexts.Combat.jumpTextEntity.ReplaceComponent(CombatComponentsLookup.JumpText, this); } var malloc = SptPool.Malloc(); malloc.CopyFrom(hurtData); hurtDatas.Enqueue(malloc); } private class JumpTextParam : ISptPool { public Vector2 pos; public string content; public bool isCritical; public bool isHeadShot; public ReuseGoData reuseGoData; public void Reset() { pos = Vector2.zero; content = null; isCritical = false; isHeadShot = false; reuseGoData = null; } } public void ShowChangeHpJumpText(HurtData hurtData) { var dst = hurtData.dst; var pos = dst.transformProxy.position; if (dst.bindPointProxy?.TryGetBindPointOrDefault(BindPointType.JumpText, out var trans) == true) { pos = trans.position; } if ((hurtData.hurtRet & HurtRet.Miss) == HurtRet.Miss) { CreateJumpText(SpriteTextPath, pos, HurtMissImagePath); return; } if ((hurtData.hurtRet & HurtRet.Cure) == HurtRet.Cure) { var value = hurtData.calculateDeltaHp; if (value == 0) return; var content = GetJumpTextContent(value, 5); CreateJumpText(CureTextPath, pos, content); return; } bool isHeadShot = (hurtData.hurtRet & HurtRet.HeadShot) == HurtRet.HeadShot; if ((hurtData.hurtRet & HurtRet.Critical) == HurtRet.Critical) { var value = -hurtData.calculateDeltaHp - hurtData.hpShieldDelta; if (value == 0) return; var content = GetJumpTextContent(value, 1); CreateJumpText(CriticalTextPath, pos, content, isHeadShot, true); return; } if ((hurtData.hurtRet & HurtRet.Normal) == HurtRet.Normal) { int typeIndex = 0; switch (hurtData.elementType) { case ElementType.None: typeIndex = 0; break; case ElementType.Fire: typeIndex = 2; break; case ElementType.Thunder: typeIndex = 3; break; case ElementType.Toxic: typeIndex = 4; break; } var value = -hurtData.calculateDeltaHp - hurtData.hpShieldDelta; if (value == 0) return; var content = GetJumpTextContent(value, typeIndex); CreateJumpText(NormalTextPath, pos, content, isHeadShot); return; } } private void CreateJumpText(string prefabPath, Vector2 pos, string content, bool isHeadShot = false, bool isCritical = false) { var param = SptPool.Malloc(); param.pos = pos; param.content = content; param.isHeadShot = isHeadShot; param.isCritical = isCritical; var reuseGoData = GoPoolDic.GetPool(prefabPath).GetNewGo(CreateJumpTextAction, param); param.reuseGoData = reuseGoData; m_JumpTextListDict.Add(reuseGoData.GetSeq(), param); } private void DestroyJumpText(object seqObj) { var seq = (int)seqObj; if (!m_JumpTextListDict.TryGetValue(seq, out var param)) return; GoPoolDic.GetPool(param.reuseGoData.path).ReleaseGo(param.reuseGoData); SptPool.Free(ref param); m_JumpTextListDict.Remove(seq); } private void CreateJumpTextAction(ReuseGoData reuseGoData) { var go = reuseGoData.go; var param = (JumpTextParam)reuseGoData.param; var distance = RandomSrv.Range(0, BattleConst.JumpTextRadius); var angle = RandomSrv.Range(-s_HalfRandomAngle, s_HalfRandomAngle); var radians = angle * Mathf.Deg2Rad; var dir = new Vector2(Mathf.Sin(radians), Mathf.Cos(radians)); go.transform.position = param.pos + dir * distance; go.TryGetComponent(out var jumpText); switch (jumpText.rendererType) { case JumpText.RendererType.TextMeshPro: jumpText.textMeshPro.text = param.content; jumpText.textMeshPro.sortingOrder = param.isCritical ? 1003 : 1001; break; case JumpText.RendererType.Sprite: GameObjectUtil.LoadAssetAsync(param.content, go, (_, obj) => { jumpText.spriteRenderer.sprite = obj as Sprite; jumpText.spriteRenderer.sortingOrder = 1000; }); break; default: throw new ArgumentOutOfRangeException(); } if (jumpText.iconRenderers.Length > 0) { jumpText.iconRenderers[0].gameObject.SetActiveWrap(param.isHeadShot); } foreach (var icon in jumpText.iconRenderers) { icon.sortingOrder = param.isCritical ? 1002 : 1000; } go.SetActiveWrap(true); Contexts.Combat.renderTimerEntity?.renderTimer.AddTimer(1f, reuseGoData.GetSeq(), DestroyJumpText); } } }