using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using DG.Tweening; using DG.Tweening.Core; using System.Linq; public static class BattleUtility { // 其他通用的战斗工具方法可以放在这里 public static void MarkStartAndEnd(RectTransform startNode, RectTransform endNode) { // 运行时才执行 if (!Application.isPlaying) { Debug.LogWarning("请在运行时使用该功能!"); return; } var battleField = BattleManager.Instance.storyBattleField; if (battleField == null) { Debug.LogError("BattleManager.storyBattleField 未初始化!"); return; } BattleWin battleWin = UIManager.Instance.GetUI(); RectTransform canvasRect = battleWin.transform as RectTransform; CreateMarker(canvasRect, startNode, "StartMarker"); CreateMarker(canvasRect, endNode, "EndMarker"); } private static void CreateMarker(RectTransform canvasRect, RectTransform targetNode, string markerName) { // 获取目标节点的世界坐标(中心点) Vector3 worldPos = targetNode.TransformPoint(targetNode.rect.center); // 转换到Canvas本地坐标 Vector2 localPoint; RectTransformUtility.ScreenPointToLocalPointInRectangle( canvasRect, RectTransformUtility.WorldToScreenPoint(null, worldPos), null, out localPoint); // 创建RawImage GameObject marker = new GameObject(markerName, typeof(RawImage)); GameObject.Destroy(marker, 5f); marker.transform.SetParent(canvasRect, false); var rawImage = marker.GetComponent(); rawImage.color = Color.white; rawImage.rectTransform.sizeDelta = new Vector2(100, 100); rawImage.rectTransform.anchoredPosition = localPoint; } public static TweenerCore MoveToTarget( RectTransform transform, RectTransform target, Vector2 offset, Action onComplete = null, float speed = 500f) { // 获取目标节点的世界坐标(中心点) Vector3 worldPos = target.TransformPoint(target.rect.center + offset); RectTransform canvasRect = transform.parent as RectTransform; // 转换到Canvas本地坐标 Vector2 localPoint; RectTransformUtility.ScreenPointToLocalPointInRectangle( canvasRect, RectTransformUtility.WorldToScreenPoint(null, worldPos), null, out localPoint); // 创建RawImage float distance = Vector2.Distance(transform.anchoredPosition, localPoint); float duration = distance / speed; // 假设速度为1000单位/秒,可以根据需要调整 var tween = transform.DOAnchorPos(localPoint, duration).SetEase(Ease.Linear); tween.onComplete += () => { onComplete?.Invoke(); }; // MarkStartAndEnd(transform as RectTransform, target); // // 1. 获取目标的世界坐标(加 offset) // Vector3 targetWorldPos = target.TransformPoint(target.anchoredPosition + offset); // // 2. 获取源节点的 parent // RectTransform sourceParent = transform.parent as RectTransform; // if (sourceParent == null) // { // BattleDebug.LogError("源节点没有父节点,无法转换坐标!"); // return null; // } // // 3. 把目标世界坐标转换到源 parent 的本地坐标 // Vector2 targetAnchoredPos; // RectTransformUtility.ScreenPointToLocalPointInRectangle( // sourceParent, // RectTransformUtility.WorldToScreenPoint(CameraManager.uiCamera, targetWorldPos), // CameraManager.uiCamera, // out targetAnchoredPos); // // 4. DOTween 移动 // var tween = transform.DOAnchorPos(targetAnchoredPos, duration).SetEase(Ease.Linear); // tween.onComplete += () => // { // onComplete?.Invoke(); // }; return tween; } public static string DisplayDamageNum(long num, int attackType) { var config = DamageNumConfig.Get(attackType); var basePowerStr = UIHelper.ReplaceLargeArtNum(num); var result = string.Empty; for (int i = 0; i < basePowerStr.Length; i++) { var numChar = (char)GetDamageNumKey(config, basePowerStr[i]); if (numChar > 0) { result += numChar; } } return result; } public static int GetMainTargetPositionNum(BattleObject caster, List targetList, SkillConfig skillConfig) { int returnIndex = 0; // 根据敌方血量阵营 存活人数来选择 BattleCamp battleCamp = skillConfig.TagFriendly != 0 ? caster.Camp : caster.GetEnemyCamp(); List targetObjList = caster.battleField.battleObjMgr.GetBattleObjList(battleCamp); // 瞄准的目标范围,如果目标个数为0则为范围内全部 // 0 全部范围 // 1 对位,默认只选1个 // 2 前排 // 3 后排 // 4 纵排,按对位规则选择纵排 // 5 自己,默认只选自己 switch (skillConfig.TagAim) { case 0: // 0 全部范围: // 若TagCount目标个数为0或6,根据TagFriendly敌我配置,代表作用于敌方全体或我方全体,此时主目标为敌我站位中的2号位置 // 若TagCount目标个数为1~5个,根据TagFriendly敌我+TagAffect细分目标配置,代表随机作用于敌方或我方x个武将,第一个为主目标 if (skillConfig.TagCount == 0 || skillConfig.TagCount == 6) { returnIndex = 1; } else { uint objId = targetList[0].ObjID; BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)objId); return target.teamHero.positionNum; } break; case 1: // 1 对位: // 默认只选1个,对位规则为A1优先打B1,A2优先打B2,A3优先打B3,对位目标死亡时,优先前排,比如B2已经死亡,那么A2将优先打B1,前排1、2、3号位置全部死亡之后才开始选择后排4、5、6号位置,对位只可选1个目标,即主目标 if (targetList.Count > 0) { BattleObject battleObject = caster.battleField.battleObjMgr.GetBattleObject((int)targetList[0].ObjID); if (battleObject != null) { returnIndex = battleObject.teamHero.positionNum; } else { Debug.LogError("GetMainTargetPositionNum 找不到目标 ObjId : " + targetList[0].ObjID); returnIndex = 0; } } else { Debug.LogError("targetList 目标列表为空"); returnIndex = 0; } break; case 2: // 1、2、3号位为前排,默认2号位置为主目标,当1、2、3号位置角色全部死亡,前排将替换成后排,5号位置变更为主目标, // 若配置TagAffect细分目标,且人数小于3,则所有被选择目标均为主目标(施法位置会用客户端配置) // (即前排默认2号位或5号位规则无效,实际作用多少人就是多少个主目标) (YL : TagAffect>0 && TagAffect != 3就是全体都是主目标 后排一样 ) if (skillConfig.TagAffect != 0 && skillConfig.TagAffect != 3 && skillConfig.TagCount < 3) { uint objId = targetList[0].ObjID; BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)objId); returnIndex = target.teamHero.positionNum; } else { // 看看对面前排是否都活着 List front = new List(from bo in targetObjList where !bo.IsDead() && bo.teamHero.positionNum < 3 select bo); if (front.Count > 0) { returnIndex = 1; } else { returnIndex = 4; } } break; case 3: if (skillConfig.TagAffect != 0 && skillConfig.TagAffect != 3 && skillConfig.TagCount < 3) { uint objId = targetList[0].ObjID; BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)objId); returnIndex = target.teamHero.positionNum; } else { // 看看对面后排是否都活着 List back = new List(from bo in targetObjList where !bo.IsDead() && bo.teamHero.positionNum >= 3 select bo); if (back.Count > 0) { returnIndex = 4; } else { returnIndex = 1; } } break; // 4 纵排,按对位规则选择纵排 case 4: returnIndex = int.MaxValue; for (int i = 0; i < targetList.Count; i++) { var hurt = targetList[i]; BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)hurt.ObjID); if (target == null) { Debug.LogError("GetMainTargetPositionNum 找不到目标 ObjId : " + hurt.ObjID); continue; } else { returnIndex = Mathf.Min(returnIndex, target.teamHero.positionNum); } } break; // 5 自己,默认只选自己 case 5: returnIndex = caster.teamHero.positionNum; break; default: Debug.LogError("暂时不支持其他的方式选择主目标 有需求请联系策划 技能id:" + skillConfig.SkillID + " TagAim " + skillConfig.TagAim); returnIndex = 0; break; } return returnIndex; } public static int GetDamageNumKey(DamageNumConfig config, int _num) { if (_num == 46) return config.nums[10]; // '.' else if (_num == 107) return config.nums[11]; // 'k' else if (_num == 109) return config.nums[12]; // 'm' else if (_num == 98) return config.nums[13]; // 'b' else if (_num == 116) return config.nums[14]; // 't' int targetNum = _num - 48; if (targetNum >= config.nums.Length || targetNum < 0) { Debug.LogError("damage config " + config.TypeID + " _num is " + _num + " out of range"); return _num; } return config.nums[_num - 48]; } /// /// 保证所有分配项加起来等于totalDamage,避免因整除导致的误差 /// public static List DivideDamageToList(int[] damageDivide, long totalDamage) { List fixedDamageList = new List(); for (int i = 0; i < damageDivide.Length; i++) { float fixedDamage = (float)totalDamage * (float)damageDivide[i] / 10000f; fixedDamageList.Add((int)fixedDamage); } return fixedDamageList; } public static List FindDeadPack(List packList) { List deadPacks = new List(); for (int i = 0; i < packList.Count; i++) { var pack = packList[i]; // 寻找死亡包 找到死亡包之后要找掉落包 不能超过技能包 if (pack is HB422_tagMCTurnFightObjDead) { var deadPack = pack as HB422_tagMCTurnFightObjDead; deadPacks.Add(deadPack); } else if (pack is CustomHB426CombinePack) { // 找死亡包不要越过技能包 var combinePack = pack as CustomHB426CombinePack; if (combinePack.startTag.Tag.StartsWith("Skill_")) { break; } } } return deadPacks; } public static List FindRebornPack(List packList) { List rebornPack = new List(); for (int i = 0; i < packList.Count; i++) { var pack = packList[i]; // 寻找死亡包 找到死亡包之后要找掉落包 不能超过技能包 if (pack is HB423_tagMCTurnFightObjReborn) { var deadPack = pack as HB423_tagMCTurnFightObjReborn; rebornPack.Add(deadPack); } else if (pack is CustomHB426CombinePack) { // 找死亡包不要越过技能包 var combinePack = pack as CustomHB426CombinePack; if (combinePack.startTag.Tag.StartsWith("Skill_")) { break; } } } return rebornPack; } }