| | |
| | | private readonly List<GameObject> illusionShadows = new List<GameObject>(); |
| | | private SkeletonAnimation skeletonAnimation; |
| | | |
| | | private Color color = Color.white; |
| | | |
| | | private bool createSwitch = false; |
| | | |
| | | private int curFrame = 0; |
| | | private int frameInteral = 4; |
| | | private int frameInteral = 6; |
| | | |
| | | public void SetSkeletonAnimation(SkeletonAnimation _skeletonAnimation) |
| | | { |
| | | skeletonAnimation = _skeletonAnimation; |
| | | } |
| | | |
| | | public void Show(bool v) |
| | | public void Show(bool v, Color _color = default) |
| | | { |
| | | createSwitch = v; |
| | | curFrame = 0; |
| | | color = _color; |
| | | |
| | | if (!v) |
| | | { |
| | | // 清理所有残影对象 |
| | | for (int i = illusionShadows.Count - 1; i >= 0; i--) |
| | | { |
| | | var go = illusionShadows[i]; |
| | | if (go != null) |
| | | { |
| | | DOTween.Kill(go, complete: false); |
| | | if (Application.isPlaying) |
| | | Destroy(go); |
| | | else |
| | | DestroyImmediate(go); |
| | | } |
| | | } |
| | | illusionShadows.Clear(); |
| | | // for (int i = illusionShadows.Count - 1; i >= 0; i--) |
| | | // { |
| | | // var go = illusionShadows[i]; |
| | | // if (go != null) |
| | | // { |
| | | // DOTween.Kill(go, complete: false); |
| | | // if (Application.isPlaying) |
| | | // Destroy(go); |
| | | // else |
| | | // DestroyImmediate(go); |
| | | // } |
| | | // } |
| | | // illusionShadows.Clear(); |
| | | |
| | | } |
| | | } |
| | |
| | | sa.timeScale = 0; |
| | | |
| | | RendererAdjuster parent = skeletonAnimation.GetComponentInParent<RendererAdjuster>(); |
| | | objTest.AddMissingComponent<RendererAdjuster>().SetSortingOrder(parent.sortingOrder); |
| | | objTest.AddMissingComponent<RendererAdjuster>().SetSortingOrder(parent.sortingOrder - 1); |
| | | |
| | | sa.skeleton.A = skeletonAnimation.skeleton.A; |
| | | sa.skeleton.R = color.r; |
| | | sa.skeleton.G = color.g; |
| | | sa.skeleton.B = color.b; |
| | | sa.skeleton.A = 0.5F; //skeletonAnimation.skeleton.A; |
| | | // 使用DoTween做alpha淡出,Tween与objTest绑定,便于统一Kill |
| | | DOTween.To(() => sa.skeleton.A, x => { sa.skeleton.A = x; sa.LateUpdate(); }, 0f, 1f) |
| | | .SetTarget(objTest); |
| | |
| | | if (skeletonAnim != null) skeletonAnim.timeScale = ratio; |
| | | } |
| | | |
| | | public void ShowIllusionShadow(bool isVisible) |
| | | public void ShowIllusionShadow(bool isVisible, Color color = default) |
| | | { |
| | | if (illusionShadow != null) |
| | | { |
| | | illusionShadow.SetSkeletonAnimation(skeletonAnim); |
| | | illusionShadow.Show(isVisible); |
| | | illusionShadow.Show(isVisible, color); |
| | | } |
| | | } |
| | | } |
| | |
| | | { |
| | | const float moveTime = 0.5f; |
| | | |
| | | private static readonly Color colorGreen = new Color(33f / 255f, |
| | | 133f / 255f, |
| | | 6f / 255f); |
| | | private static readonly Color colorBlue = new Color(40f / 255f, |
| | | 87f / 255f, |
| | | 189f / 255f); |
| | | |
| | | protected SkillEffect skillEffect; |
| | | protected HB427_tagSCUseSkill tagUseSkillAttack; |
| | | public SkillConfig skillConfig; |
| | |
| | | protected bool moveFinished = false; |
| | | public int fromSkillId; |
| | | public bool isPlay = false; |
| | | |
| | | private float MoveSpeed = 750f; |
| | | |
| | | private Dictionary<int, BattleDrops> tempDropList = new Dictionary<int, BattleDrops>(); |
| | | private Dictionary<int, HB422_tagMCTurnFightObjDead> tempDeadPackList = new Dictionary<int, HB422_tagMCTurnFightObjDead>(); |
| | |
| | | } |
| | | } |
| | | |
| | | protected void ShadowIllutionCreate(bool create) |
| | | { |
| | | if (create) |
| | | { |
| | | Color color = Color.white; |
| | | //1-连击;2-反击;3-追击 |
| | | // 反击蓝色 |
| | | // 追击连击绿色 |
| | | bool change = false; |
| | | if (tagUseSkillAttack.BattleType == 1) |
| | | { |
| | | color = colorGreen; |
| | | change = true; |
| | | } |
| | | else if (tagUseSkillAttack.BattleType == 2) |
| | | { |
| | | color = colorBlue; |
| | | change = true; |
| | | } |
| | | else if (tagUseSkillAttack.BattleType == 3) |
| | | { |
| | | color = colorGreen; |
| | | change = true; |
| | | } |
| | | |
| | | if (change) |
| | | { |
| | | MoveSpeed = 1125f; |
| | | caster.motionBase.ShowIllusionShadow(true, color); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | MoveSpeed = 750f; |
| | | caster.motionBase.ShowIllusionShadow(false); |
| | | } |
| | | } |
| | | |
| | | // 技能释放主逻辑:广播事件、高亮目标、执行释放 |
| | | public virtual void Cast() |
| | | { |
| | |
| | | if (hintConfig != null) |
| | | { |
| | | caster.heroInfoBar.ShowTips(((char)hintConfig.prefix).ToString(), true); |
| | | // Debug.Break(); |
| | | } |
| | | } |
| | | |
| | |
| | | RectTransform target = battleField.GetTeamNode(caster.GetEnemyCamp(), skillConfig); |
| | | ExecuteMoveAndCastSequence(target, () => |
| | | { |
| | | MoveToTarget(battleField.GetTeamNode(caster.Camp, caster.teamHero.positionNum), Vector2.zero, OnAttackFinish, 750F); |
| | | // ShadowIllutionCreate(true); |
| | | MoveToTarget(battleField.GetTeamNode(caster.Camp, caster.teamHero.positionNum), Vector2.zero, () => |
| | | { |
| | | // ShadowIllutionCreate(false); |
| | | OnAttackFinish(); |
| | | }, MoveSpeed); |
| | | }); |
| | | } |
| | | |
| | |
| | | ExecuteMoveAndCastSequence(targetTrans, () => |
| | | { |
| | | RectTransform rectTransform = battleField.GetTeamNode(caster.Camp, caster.teamHero.positionNum); |
| | | MoveToTarget(rectTransform, Vector2.zero, OnAttackFinish, 750F); |
| | | // ShadowIllutionCreate(true); |
| | | MoveToTarget(rectTransform, Vector2.zero, () => |
| | | { |
| | | // ShadowIllutionCreate(false); |
| | | OnAttackFinish(); |
| | | }, MoveSpeed); |
| | | }); |
| | | } |
| | | |
| | |
| | | RectTransform target = battleField.GetTeamNode(caster.Camp, skillConfig); |
| | | ExecuteMoveAndCastSequence(target, () => |
| | | { |
| | | MoveToTarget(battleField.GetTeamNode(caster.Camp, caster.teamHero.positionNum), Vector2.zero, OnAttackFinish, 750F); |
| | | // ShadowIllutionCreate(true); |
| | | MoveToTarget(battleField.GetTeamNode(caster.Camp, caster.teamHero.positionNum), Vector2.zero, () => |
| | | { |
| | | // ShadowIllutionCreate(false); |
| | | OnAttackFinish(); |
| | | }, MoveSpeed); |
| | | }); |
| | | } |
| | | |
| | | // 执行移动-施法-返回序列:通用的移动攻击流程 |
| | | private void ExecuteMoveAndCastSequence(RectTransform target, Action onReturnComplete) |
| | | { |
| | | ShadowIllutionCreate(true); |
| | | MoveToTarget(target, new Vector2(skillConfig.CastDistance, 0), () => |
| | | { |
| | | TurnBack(() => |
| | | { |
| | | ShadowIllutionCreate(false); |
| | | CastImpl(() => |
| | | { |
| | | TurnBack(() => |
| | |
| | | } |
| | | |
| | | // 移动到目标位置:处理角色的移动动画和逻辑 |
| | | protected void MoveToTarget(RectTransform target, Vector2 offset, Action _onComplete = null, float speed = 500f) |
| | | protected void MoveToTarget(RectTransform target, Vector2 offset, Action _onComplete = null, float speed = 750f) |
| | | { |
| | | if (skillConfig.CastDistance >= 9999) |
| | | { |
| | |
| | | public void OnSkillFinished() |
| | | { |
| | | // 修复:使用循环代替递归,避免栈溢出风险 |
| | | try |
| | | { |
| | | // try |
| | | // { |
| | | while (true) |
| | | { |
| | | // 验证技能效果是否完成 |
| | |
| | | } |
| | | |
| | | if (pack is CustomB421ActionPack actionPack) |
| | | { |
| | | actionPack.Distribute(); |
| | | PackageRegedit.Distribute(pack); |
| | | } |
| | | else |
| | | { |
| | | PackageRegedit.Distribute(pack); |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError($"OnSkillFinished异常: {ex.Message},技能ID={skillConfig.SkillID}"); |
| | | // 确保状态一致性,即使出现异常也要标记完成 |
| | | isFinished = true; |
| | | throw; // 重新抛出异常供上层处理 |
| | | } |
| | | // } |
| | | // catch (Exception ex) |
| | | // { |
| | | // Debug.LogError($"OnSkillFinished异常: {ex.Message},技能ID={skillConfig.SkillID}"); |
| | | // // 确保状态一致性,即使出现异常也要标记完成 |
| | | // isFinished = true; |
| | | // throw; // 重新抛出异常供上层处理 |
| | | // } |
| | | |
| | | isFinished = true; |
| | | } |
| | |
| | | |
| | | public class BattleHeroInfoBar : MonoBehaviour |
| | | { |
| | | public class TipsInfo |
| | | { |
| | | public string message; |
| | | public bool useArtText; |
| | | public bool followCharacter; |
| | | } |
| | | |
| | | protected BattleObject battleObject; |
| | | |
| | | public Slider sliderHp; |
| | |
| | | |
| | | [SerializeField] public List<BattleBuffCell> buffCells = new List<BattleBuffCell>(); |
| | | |
| | | protected List<KeyValuePair<string, bool>> messages = new List<KeyValuePair<string, bool>>(); |
| | | protected List<TipsInfo> messages = new List<TipsInfo>(); |
| | | |
| | | public BasicHeroInfoContainer heroInfoContainer; |
| | | |
| | |
| | | } |
| | | tipsList.Clear(); |
| | | } |
| | | public void ShowTips(string message, bool useArtText = false) |
| | | public void ShowTips(string message, bool useArtText = false, bool followCharacter = false) |
| | | { |
| | | messages.Add(new KeyValuePair<string, bool>(message, useArtText)); |
| | | messages.Add(new TipsInfo |
| | | { |
| | | message = message, |
| | | useArtText = useArtText, |
| | | followCharacter = followCharacter |
| | | }); |
| | | } |
| | | |
| | | public void SetActive(bool active) |
| | |
| | | gameObject.SetActive(active); |
| | | } |
| | | |
| | | public void PopUpTipsDirectly(string message, bool useArtText = false) |
| | | public void PopUpTipsDirectly(string message, bool useArtText = false, bool followCharacter = false) |
| | | { |
| | | GameObject prefab = textTips.gameObject; |
| | | |
| | | GameObject go = GameObject.Instantiate(prefab, transform); |
| | | GameObject go = GameObject.Instantiate(prefab, followCharacter ? transform : battleObject.battleField.battleRootNode.transform); |
| | | |
| | | BattleTips tips = go.GetComponent<BattleTips>(); |
| | | |
| | | if (!followCharacter) |
| | | { |
| | | go.transform.position = prefab.transform.position; |
| | | tips.beginPos = go.transform.localPosition; |
| | | tips.endPos = tips.endPos + new Vector2(go.transform.localPosition.x, go.transform.localPosition.y); |
| | | } |
| | | |
| | | tips.SetSpeedRatio(battleObject.battleField.speedRatio); |
| | | |
| | | tips.SetText(message, useArtText); |
| | | |
| | |
| | | tipsList[i].Run(); |
| | | } |
| | | |
| | | timer += Time.deltaTime; |
| | | timer += 1f / (float)BattleConst.skillMotionFps * battleObject.battleField.speedRatio; |
| | | |
| | | if (messages.Count > 0 && timer >= PopUpInterval) |
| | | { |
| | | // 播放飘字 |
| | | KeyValuePair<string, bool> message = messages[0]; |
| | | TipsInfo message = messages[0]; |
| | | messages.RemoveAt(0); |
| | | |
| | | PopUpTipsDirectly(message.Key, message.Value); |
| | | PopUpTipsDirectly(message.message, message.useArtText, message.followCharacter); |
| | | |
| | | timer = 0f; |
| | | } |
| | |
| | | |
| | | using UnityEngine; |
| | | using System; |
| | | using UnityEngine.UI; |
| | |
| | | public class BattleTips : MonoBehaviour |
| | | { |
| | | public Vector2 beginPos = Vector2.zero; |
| | | public Vector2 endPos = new Vector2(0, 100); |
| | | public float showTime = 0.4f; |
| | | public Vector2 endPos = new Vector2(0, 150); |
| | | |
| | | public float scaleChangeTime = 0.2667f; // 7~8帧 (8/30=0.2667秒) |
| | | public float totalShowTime = 0.8f; // 总时间约24帧 (8+16=24帧) |
| | | public float timer = 0f; |
| | | |
| | | public Vector3 beginScale = new Vector3(4f, 4f, 4f); |
| | | public Vector3 endScale = new Vector3(2f, 2f, 2f); |
| | | public Color beginColor = new Color(1f, 1f, 1f, 0.5f); |
| | | public Color endColor = new Color(1f, 1f, 1f, 1f); |
| | | |
| | | public RectTransform rectTransform; |
| | | |
| | |
| | | |
| | | public Action OnFinish; |
| | | |
| | | private float speedRatio = 1f; |
| | | |
| | | public void SetSpeedRatio(float ratio) |
| | | { |
| | | speedRatio = ratio; |
| | | } |
| | | |
| | | public void SetText(string text, bool useArtText = false) |
| | | { |
| | | rectTransform.anchoredPosition = Vector2.zero; |
| | | // 初始放大200% 透明度50% 7~8帧内缩回100% 透明度到100% 再往上飘14~16帧 然后消失 (30帧/秒) |
| | | |
| | | // 8+16/30=0.8秒 |
| | | |
| | | |
| | | rectTransform.anchoredPosition = beginPos; |
| | | rectTransform.localScale = beginScale; |
| | | |
| | | timer = 0f; |
| | | gameObject.SetActive(true); |
| | | |
| | | if (useArtText) |
| | | { |
| | | artText.text = text; |
| | | artText.color = beginColor; |
| | | tipText.gameObject.SetActive(false); |
| | | artText.gameObject.SetActive(true); |
| | | } |
| | | else |
| | | { |
| | | tipText.text = text; |
| | | tipText.color = beginColor; |
| | | artText.gameObject.SetActive(false); |
| | | tipText.gameObject.SetActive(true); |
| | | } |
| | |
| | | if (!gameObject.activeSelf) |
| | | return; |
| | | |
| | | if (timer >= showTime) |
| | | if (timer >= totalShowTime) |
| | | { |
| | | gameObject.SetActive(false); |
| | | OnFinish?.Invoke(); |
| | | OnFinish = null; |
| | | return; |
| | | } |
| | | |
| | | rectTransform.anchoredPosition = Vector2.Lerp(beginPos, endPos, timer / showTime); |
| | | timer += Time.deltaTime; |
| | | // 整个过程都往上飘 |
| | | float moveProgress = timer / totalShowTime; |
| | | rectTransform.anchoredPosition = Vector2.Lerp(beginPos, endPos, moveProgress); |
| | | |
| | | // 阶段1: 7~8帧内缩放从beginScale到endScale,透明度从50%到100% |
| | | if (timer < scaleChangeTime) |
| | | { |
| | | float scaleProgress = timer / scaleChangeTime; |
| | | rectTransform.localScale = Vector3.Lerp(beginScale, endScale, scaleProgress); |
| | | |
| | | Color currentColor = Color.Lerp(beginColor, endColor, scaleProgress); |
| | | if (tipText.gameObject.activeSelf) |
| | | tipText.color = currentColor; |
| | | if (artText.gameObject.activeSelf) |
| | | artText.color = currentColor; |
| | | } |
| | | // 阶段2: 缩放完成后,保持endScale和100%透明度,继续往上飘 |
| | | else |
| | | { |
| | | rectTransform.localScale = endScale; |
| | | |
| | | if (tipText.gameObject.activeSelf) |
| | | tipText.color = endColor; |
| | | if (artText.gameObject.activeSelf) |
| | | artText.color = endColor; |
| | | } |
| | | |
| | | timer += 1f / (float)BattleConst.skillMotionFps * speedRatio; |
| | | } |
| | | } |