using System.Collections; using System.Collections.Generic; using UnityEngine; using DG.Tweening; using Sirenix.OdinInspector; /// /// 普通怪物类,三关都有的那个 /// public class NormalEnemy : Enemy { // _____ _ _ _ // | __ \ | | | (_) // | |__) | _| |__ | |_ ___ // | ___/ | | | '_ \| | |/ __| // | | | |_| | |_) | | | (__ // |_| \__,_|_.__/|_|_|\___| [Header("被攻击后击飞的力度调整值")][FoldoutGroup("其他",false,0)] public Vector2 hitToflyParameter; [FoldoutGroup("其他",false,0)][Header("怪物死后旋转速度的随机区间")] public float deadRotationRangeMax; [FoldoutGroup("其他",false,0)] public float deadRotationRangeMin; /// /// 这个小怪需要移动吗? /// [FoldoutGroup("其他",false,0)][Header("这个小怪需要移动吗?")] public bool needWalk; /// /// 记录自己是不是在佛教前置关卡的演出中 /// public bool isInFoStage = false; // _____ _ _ // | __ \ (_) | | // | |__) | __ ___ ____ _| |_ ___ // | ___/ '__| \ \ / / _` | __/ _ \ // | | | | | |\ V / (_| | || __/ // |_| |_| |_| \_/ \__,_|\__\___| protected Rigidbody2D m_rigidbody; /// /// 死的时候的受击朝向 /// protected int deadDir; /// /// 记录一下游戏开始时怪物的位置,方便被击飞后怪物重新回到巡逻起点 /// protected Vector3 sourcePosition; protected DOTweenPath doTweenPath; [SerializeField][ReadOnly][FoldoutGroup("状态")] protected bool inPath = true; /// /// 现在正在执行的动画,在受击中断的时候用 /// protected Tween tweenNow; private Animator animator; //记录自己是否是特殊的,特殊的用在剧情里,不按正常的来 public bool isThisSpecial; // _____ _ _ ____ _ // / ____| | | | _ \ | | // | | __ _| | | |_) | __ _ ___| | __ // | | / _` | | | _ < / _` |/ __| |/ / // | |___| (_| | | | |_) | (_| | (__| < // \_____\__,_|_|_|____/ \__,_|\___|_|\_\ void Start(){Init();} void Update(){ if(isInFoStage && state != State.dead && !isThisSpecial){ Seek(); } } // _ _ _ // | \ | | | | // | \| | ___ _ __ _ __ ___ __ _| | // | . ` |/ _ \| '__| '_ ` _ \ / _` | | // | |\ | (_) | | | | | | | | (_| | | // |_| \_|\___/|_| |_| |_| |_|\__,_|_| private void Seek(){ Transform target = FindObjectOfType().transform; Vector3 moveDir = (target.transform.position - transform.position).normalized; // //给刚体添加位移 // m_rigidbody.position += (Vector2)moveDir * speed * Time.deltaTime * Vector2.right; //给刚体以速度 if(canBeHit) m_rigidbody.velocity = new Vector2( ((moveDir.x > 0) ? 1 : -1) * speed, m_rigidbody.velocity.y ); //将面部朝向与速度同步 transform.rotation = Quaternion. Euler (transform.rotation.x, ((target.position.x - transform.position.x > 0) ? 0:-180), transform.rotation.z); } protected virtual void Init(){ //生命值初始化为满 HPLeft = HP; //Get插件 doTweenPath = GetComponent(); m_rigidbody = GetComponent(); sourcePosition = transform.position; animator = GetComponent(); //初始话目前播放的Tween动画为巡逻 tweenNow = doTweenPath.tween; //如果是需要走路的类型,则播放状态至走路状体 if(needWalk) animator.SetBool("isWalk",true); } /// /// 协程用,删除自己这个游戏物体 /// protected void Dead(){Destroy(gameObject);} /// /// 被击飞的击飞效果处理 /// /// 被击方向 public void BeHitToFly(int dir){ try{ m_rigidbody.velocity += new Vector2( //给予自身一个 -1 * dir * hitToflyParameter.x,//X方向为力度系数乘以受击方向 hitToflyParameter.y//Y方向为力度系数 //的绝对的速度 ); }catch(System.Exception) { Debug.Log("请检查这个小怪是否是可被攻击那种,如果不是,请给他添加刚体,并检查try代码"); } } // ______ _ // | ____| | | // | |____ _____ _ __ | |_ // | __\ \ / / _ \ '_ \| __| // | |___\ V / __/ | | | |_ // |______\_/ \___|_| |_|\__| protected override void OnTouchThePlayer(MyPlayer player){ //告诉玩家,你被攻击了 player.OnBeHit(ATK, ((transform.position.x - player.transform.position.x) > 0) ? 1 : -1);//通过自身位置和玩家位置的比较来返回玩家本次的受击方向 } public override void OnBeHit(MyPlayer.AtkMethod hitMethod, int hitDir){ //结束当前动画 tweenNow.Pause(); //让自己被击飞 BeHitToFly(hitDir); //修改标志表示自己当前不在Path中 inPath = false; //结算生命值 HPLeft -= MyPlayer.atkMethodMagnification[hitMethod]; //看下死了没 //死了就记录下死亡时候的面部朝向,用来做死亡翻滚效果,然后再Call一下死亡事件 if(CheckDead()) {deadDir = hitDir;OnDead();} //被打飞了在着地前都不会挨打了 canBeHit = false; //播放受击动画 // 解决Bug用,不要删除后一个条件 if(animator != null && HPLeft < 10)animator.SetBool("isBeHit",true); } public override void OnRetouchedTheGround(){ if(!inPath){//如果此时怪物没在巡逻且着地了,说明时被击飞然后着地了 //新建一个动画,让怪物回到初始记录位置 Tween tween = transform.DOMove(sourcePosition, Mathf.Abs(sourcePosition.x - transform.position.x) / speed, false); //设置该动画插值方式为线性 tween.SetEase(Ease.Linear); //更改记录的此时正在执行的动画 tweenNow = tween; //写一个委托,插入这个新动画的结束事件 TweenCallback myCallBack = () => //结束说明回到了初始点,此时让怪物重新开始巡逻,同时更改记录中的正在执行的动画为巡逻 {doTweenPath.DORestart(); tweenNow = doTweenPath.tween;}; //插入写的委托 tween.OnComplete(myCallBack); //标记自身重新回到巡逻动画 inPath = true; //矫正一下faceDir的问题 transform.rotation = Quaternion. Euler (transform.rotation.x, ((sourcePosition.x - transform.position.x > 0) ? 0:-180), transform.rotation.z); } canBeHit = true; //停止受击动画 animator.SetBool("isBeHit",false); } public override void OnDead() { // m_rigidbody.constraints = RigidbodyConstraints2D.None; //标记当前状态为死亡 state = State.dead; //加一个扭矩,营造死亡的效果 m_rigidbody.AddTorque(Random.Range(deadRotationRangeMin,deadRotationRangeMax) * deadDir); //关掉自己的碰撞体 GetComponent().enabled = false; //两秒后自己毁灭 Invoke("Dead",2f); } }