using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using DG.Tweening; using DG.Tweening.Core; using DG.Tweening.Plugins.Options; using Spine.Unity; using UnityEngine.UI; using System.Linq; public enum BattleCamp { Red, Blue } public enum BattleState { None = 0, Stunned = 1 << 0, Poisoned = 1 << 1, Bleeding = 1 << 2, Silenced = 1 << 3, Frozen = 1 << 4, Burned = 1 << 5 } public class BattleObject { public BattleField battleField; public int BattleObjectId { get; set; } public BattleCamp Camp { get; protected set; } public TeamHero teamHero { get; protected set; } // public BuffMgr buffMgr; protected MotionBase motionBase; protected GameObject heroGo; public BattleObject(BattleField _battleField) { battleField = _battleField; } public virtual void Init(GameObject _heroGo, TeamHero _teamHero, BattleCamp _camp) { heroGo = _heroGo; teamHero = _teamHero; Camp = _camp; motionBase = new MotionBase(); motionBase.Init(heroGo.GetComponentInChildren(true)); } public virtual void Run() { motionBase.Run(); } public virtual void Pause() { motionBase.Pause(); } public virtual void Resume() { motionBase.Resume(); } public virtual void Destroy() { if (heroGo != null) { GameObject.DestroyImmediate(heroGo); heroGo = null; } motionBase.Release(); motionBase = null; teamHero = null; BattleObjectId = 0; } // 眩晕 public bool IsStunned() { return teamHero.isStunned; } // 冰冻 public bool IsFrozen() { return teamHero.isFrozen; } // 石化 public bool IsStoned() { return teamHero.isStoned; } // // 禁锢 // public bool IsConfined() // { // return false; // } // 被沉默 public bool IsSlient() { return teamHero.isSlient; } // 被缴械 public bool IsDisarmed() { return teamHero.isDisarmed; } // 是否无敌 public bool IsInvincable() { return teamHero.isInvinceble; } // 是否死亡 public bool IsDead() { return teamHero.isDead; } // 是否被控住了 public bool IsCrowdControlled() { return IsStunned() || IsStoned() || IsFrozen(); } public virtual bool IsCanCastSkill() { // 被控住 if (IsCrowdControlled()) { return false; } // 被沉默 if (IsSlient()) { return false; } // 看看怒气是否达到释放要求 return teamHero.rage >= 100; } public virtual bool IsCanNormalAttack() { // 被控住 if (IsCrowdControlled()) { return false; } // 缴械 if (IsDisarmed()) { return false; } return true; } public virtual void TakeDamage(List damageValues) { if (IsDead()) return; PopDamage(damageValues); motionBase.PlayAnimation(MotionName.hit, false); // 计算伤害 int totalDamage = 0; foreach (var damage in damageValues) { totalDamage += damage; } // 扣血 teamHero.curHp -= totalDamage; // 其实这里应该是等服务器发death的action // if (IsDead()) // { // OnDeath(); // } } // 闪避开始 public virtual void OnDodgeBegin() { float pingpongTime = 0.2f; RectTransform rectTrans = heroGo.GetComponent(); rectTrans.DOAnchorPos(new Vector3(-50, 50, 0), pingpongTime) .SetEase(Ease.OutCubic); } // 闪避结束 public virtual void OnDodgeEnd() { float pingpongTime = 0.2f; RectTransform rectTrans = heroGo.GetComponent(); rectTrans.DOAnchorPos(Vector3.zero, pingpongTime) .SetEase(Ease.OutCubic); } protected virtual void OnDeath() { motionBase.OnOtherAnimationComplete = OnOtherAnimationComplete; motionBase.PlayAnimation(MotionName.dead, false); } protected virtual void OnOtherAnimationComplete(MotionName motionName) { if (motionName == MotionName.dead) { OnDeadAnimationComplete(); } } protected virtual void OnDeadAnimationComplete() { // 或许看看溶解特效? YYL TODO heroGo.SetActive(false); } // 伤害还要看 是否闪避 暴击 and so on 需要有一个DamageType 服务器应该会给 protected virtual void PopDamage(List damageValues) { // 其实应该通知出去给UI界面解耦 让UI界面自己来显示的 YYL TODO // 播放伤害数字 // 这里可以实现一个伤害数字的弹出效果 // 比如使用一个UI组件来显示伤害数字 foreach (var damage in damageValues) { Debug.Log($"Damage: {damage}"); } } public void PlaySkill(SkillConfig skillConfig, List>> damageList, Action _onComplete) { bool moveToTarget = true; if (moveToTarget) { int targetId = damageList[0].First().Key; BattleObject _targetObj = battleField.battleObjMgr.GetBattleObject(targetId); RectTransform selfRect = heroGo.GetComponent(); RectTransform targetRect = _targetObj.heroGo.GetComponent(); Vector2 curAnchoredPos = selfRect.anchoredPosition; MoveToTargetUI(selfRect, targetRect, new Vector2(100f, 0f), () => { PlaySkillAnimation(skillConfig, damageList, () => { // 回到原位置 selfRect.DOAnchorPos(curAnchoredPos, 0.2f) .SetEase(Ease.Linear) .OnComplete(() => { _onComplete?.Invoke(); }); }); }); } else { PlaySkillAnimation(skillConfig, damageList, _onComplete); } } protected void MoveToTargetUI(RectTransform selfRect, RectTransform targetRect, Vector2 offset, Action _onComplete) { // 1. 目标的本地坐标转为世界坐标 Vector3 targetWorldPos = targetRect.TransformPoint(targetRect.anchoredPosition + offset); // 2. 世界坐标转为自己父节点下的本地坐标 RectTransform parentRect = selfRect.parent as RectTransform; Vector2 targetAnchoredPos; RectTransformUtility.ScreenPointToLocalPointInRectangle( parentRect, RectTransformUtility.WorldToScreenPoint(null, targetWorldPos), null, out targetAnchoredPos); // 3. DOTween 移动 selfRect.DOAnchorPos(targetAnchoredPos, 0.2f) .SetEase(Ease.Linear) .OnComplete(() => _onComplete?.Invoke()); } protected void PlaySkillAnimation(SkillConfig skillConfig, List>> damageList, Action _onComplete) { // 关键帧列表 List keyFrameList = new List() { 15 }; motionBase.OnAttackHitEvent = (int _frame) => { Dictionary> oneRoundDamage = damageList[keyFrameList.IndexOf(_frame)]; foreach (var kvp in oneRoundDamage) { int targetId = kvp.Key; List damageValues = kvp.Value; BattleObject targetObj = battleField.battleObjMgr.GetBattleObject(targetId); if (targetObj != null && !targetObj.IsDead()) { targetObj.TakeDamage(damageValues); } } }; motionBase.OnAttackAnimationComplete = () => { _onComplete?.Invoke(); motionBase.OnAttackHitEvent = null; motionBase.OnAttackAnimationComplete = null; // 死亡确定其实不应该在这里进行触发 应该由服务器下发 YYL TODO #if UNITY_EDITOR // 暂时的处理 HashSet hitTargets = new HashSet(); foreach (var dmgDict in damageList) { foreach (var kvp in dmgDict) { int targetId = kvp.Key; hitTargets.Add(targetId); } } foreach (int targetId in hitTargets) { BattleObject targetObj = battleField.battleObjMgr.GetBattleObject(targetId); if (targetObj != null && targetObj.IsDead()) { targetObj.OnDeath(); } } #endif }; motionBase.PlayAnimationEx(MotionName.attack, false, keyFrameList); } #if UNITY_EDITOR public void EditorRevive() { teamHero.curHp = 100; heroGo.SetActive(true); motionBase.PlayAnimation(MotionName.idle, true); } public List TryAttack(BattleObject obj, SkillConfig skillConfig) { List damageList = new List(); int totalDamage = teamHero.attack - obj.teamHero.defense; damageList.Add(totalDamage); return damageList; } #endif }