using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public class Yeji : TargetAnimal { //动画播放器 Animator animator; //寻路代理 NavMeshAgent agent; //血量 [System.NonSerialized] public int hp = 3; [System.NonSerialized] public TreeAreaRecorder treeAreaRecorder; [System.NonSerialized] public float flyPlaneHeight = 5; float currentHeight = 0; public void SetFlyHeight(float value) { currentHeight = value; float agentBaseOffset = value - flyPlaneHeight; this.agent.baseOffset = agentBaseOffset / 1.2f;//因为agent的baseoffset会受节点的scale影响 state.flying = currentHeight > 0; } void Awake() { state.animal = this; animator = GetComponent(); agent = GetComponent(); this.agent.avoidancePriority = avoidancePriority; RandomWillFlyTime(); } static int _avoidancePriority = 0; static int avoidancePriority { get { if (_avoidancePriority < 50) { _avoidancePriority++; } else { _avoidancePriority = 1; } return _avoidancePriority; } } void OnDestroy() { ReleaseLandLock(); } void Update() { //寻路过程监测 if (HasCloseToDestination()) { OnReachDestination(); } else { OnMovingToDestination(); } UpdateAutoStrategy(); } #region 被击触发 public override void OnHit(Arrow arrow, Vector3 hitPoint, string partName) { arrow.Head().position = hitPoint + arrow.transform.forward * 0.1f; arrow.Hit(); if (partName == "Wing") { hp -= 1; this.agent.speed /= 2f; } else if (partName == "Body") hp -= 2; else if (partName == "Head") hp -= 3; if (hp <= 0) { Die(arrow); } else { Hurt(); } } void Die(Arrow arrow) { if (state.dead) return; arrow.onDoNextShoot += delegate() { Destroy(this.gameObject); }; this.animator.speed = 1; state.dead = true; this.agent.enabled = false; onDie?.Invoke(this); AudioMgr.ins.PlayAnimalEffect("bird_die", AudioMgr.GetAudioSource(this.gameObject)); AudioMgr.ins.PlayCheer(true); if (currentHeight < 1.5f) { arrow.arrowCameraComp.arrowCameraTemplate.SendMsg(0, null); } } void Hurt() { CancelStand(); AudioMgr.ins.PlayAnimalEffect("bird_injured", AudioMgr.GetAudioSource(this.gameObject)); } #endregion #region 寻路导航 bool _moving; bool moving { get { return _moving; } set { movingTime = 0; _moving = value; } } float movingTime; //启动寻路 void SetDestination(Vector3 pos) { moving = true; this.agent.destination = pos; } //寻路过程 void OnMovingToDestination() { if (!moving) return; movingTime += Time.deltaTime; } //寻路结束 void OnReachDestination() { if (!moving) return; moving = false; } //是否已经接近目的地(寻路完成判断) bool HasCloseToDestination() { if (!moving) return true; return movingTime > 0.1 && this.agent.velocity.magnitude < 0.05f; } //停止寻路 void StopNavigation() { if (!moving) return; this.agent.destination = this.agent.nextPosition; this.agent.velocity = Vector3.zero; this.transform.position = this.agent.destination; OnReachDestination(); } #endregion #region 自动逻辑 //自动策略的更新逻辑 void UpdateAutoStrategy() { if (state.dead) return; UpdateFlyUpDown(); UpdateBehavior(); } float toFlyHeight = 2f; float canStandTime = 3f; //能够在前几棵树徘徊 [System.NonSerialized] public int canFlyTreeCount = 4; //将会绕树转 bool willMoveRoundTree = false; Queue roundTreePostions = new Queue(); //随机移动 void UpdateFlyUpDown() { if (state.up && state.landing) { //起飞阶段 AnimatorStateInfo animatorStateInfo = animator.GetCurrentAnimatorStateInfo(0); bool isTakeOff = animatorStateInfo.IsName("FlyFromGround"); if (isTakeOff) { if (animatorStateInfo.normalizedTime >= 1.0) { state.landing = false; state.flying = true; Invoke("ReleaseLandLock", Random.value * 3f + 1.0f); } } return; } if (state.up && state.flying) { //上升阶段 float surplusHeight = toFlyHeight - currentHeight; float smoothValue = surplusHeight / toFlyHeight; float nextFlyHeight = currentHeight + Time.deltaTime * (1f + 2.5f * smoothValue); this.animator.speed = 1f + 2f * smoothValue; if (nextFlyHeight >= toFlyHeight) { //上升完成 nextFlyHeight = toFlyHeight; this.animator.speed = 1; state.up = false; } SetFlyHeight(nextFlyHeight); return; } if (state.down && state.flying) { //下降阶段 float nextH = currentHeight - Time.deltaTime * 2f; if (nextH <= 0) { //下降完成 nextH = 0; state.landing = true; state.flying = false; StopNavigation(); } SetFlyHeight(nextH); return; } if (state.down && state.landing) { //着落阶段 AnimatorStateInfo animatorStateInfo = animator.GetCurrentAnimatorStateInfo(0); bool isLand = animatorStateInfo.IsName("LandOnGround"); if (isLand) { if (animatorStateInfo.normalizedTime >= 1.0) { state.down = false; //进入站立状态 state.standing = true; canStandTime = 2f + Random.value * 3f; animator.CrossFade("IdleOnGround2", 0.2f); } } return; } } float willFlyTime; float flyingTime; void UpdateBehavior() { if (state.standing) { canStandTime -= Time.deltaTime; if (canStandTime <= 0) { CancelStand(); } return; } if (state.landing) return; if (state.flying && !state.down && !state.up) { flyingTime += Time.deltaTime; if (flyingTime >= willFlyTime && DoLandLock()) { flyingTime = 0; state.down = true; } } if (moving) return; if (willMoveRoundTree && roundTreePostions.Count > 0) { Vector3 pos = roundTreePostions.Dequeue(); SetDestination(pos); return; } else { willMoveRoundTree = false; } //规划下一次行动路线 Vector3 myPos = this.transform.position; TreeAreaRecorder.AreaInfo[] areaInfos = treeAreaRecorder.copyAreaInfos(canFlyTreeCount); for (int i = 0; i < canFlyTreeCount; i++) { areaInfos[i].teamCompareValue = areaInfos[i].distanceInHorizontal(myPos); } System.Array.Sort(areaInfos, new ComparerAreaInfosByDistance()); //如果离这棵树比较近,有几率使其绕着树转 TreeAreaRecorder.AreaInfo firstAreaInfo = areaInfos[0]; bool isNearTree4th = areaInfos.Length == 4 && firstAreaInfo == treeAreaRecorder.getValidAreaInfo(3); if (!isNearTree4th && firstAreaInfo.teamCompareValue < firstAreaInfo.radius + 1.5f && Random.value < 0.5) { willMoveRoundTree = true; Vector3 treePos = areaInfos[0].transform.position; treePos.y = myPos.y; Vector3 pointer = myPos - treePos; roundTreePostions.Clear(); int direction = Random.value >= 0.5 ? 1 : -1; for (int i = 1; i <= 6; i++) { roundTreePostions.Enqueue(treePos + Quaternion.AngleAxis(i * 60 * direction, Vector3.up) * pointer); } return; } TreeAreaRecorder.AreaInfo areaInfo = areaInfos[Random.Range(1, canFlyTreeCount)]; Vector3 newPos = YejiHuntGameMode.CalculateNewPosByTreePos(animalsBaseT, flyPlaneHeight, areaInfo); SetDestination(newPos); } class ComparerAreaInfosByDistance : IComparer { public int Compare(object x, object y) { TreeAreaRecorder.AreaInfo a = (TreeAreaRecorder.AreaInfo)x; TreeAreaRecorder.AreaInfo b = (TreeAreaRecorder.AreaInfo)y; if (a.teamCompareValue - b.teamCompareValue > 0) return 1; if (a.teamCompareValue - b.teamCompareValue < 0) return -1; else return 0; } } void CancelStand() { if (!state.standing) return; state.standing = false; state.up = true; toFlyHeight = RandomOnePreHeight(); animator.CrossFade("FlyFromGround", 0.1f); RandomWillFlyTime(); } void RandomWillFlyTime() { willFlyTime = 4 + Random.value * 3f; } static float[] preFlyHeights; static int[] preFlyHeightCools; public static void InitPreHeights() { preFlyHeights = new float[]{2, 2.5f, 3, 3.5f, 4f}; preFlyHeightCools = new int[]{0, 0, 0, 0, 0}; } public static float RandomOnePreHeight() { List indexes = new List(); for (int i = 0; i < preFlyHeightCools.Length; i++) { if (preFlyHeightCools[i] > 0) preFlyHeightCools[i] = preFlyHeightCools[i] - 1; if (preFlyHeightCools[i] == 0) { indexes.Add(i); } } int index = indexes[Random.Range(0, indexes.Count)]; preFlyHeightCools[index] = 3; return preFlyHeights[index]; } public static bool landLocked = false; //着陆锁,禁止地上同时存在两只鸟,避免穿模 private bool myLandLocked = false; public bool DoLandLock() { if (landLocked) return false; return myLandLocked = landLocked = true; } public void ReleaseLandLock() { if (!myLandLocked) return; myLandLocked = landLocked = false; } #endregion //状态 [System.Serializable] public class State { public bool standing = false; [SerializeField] private bool _dead; public bool dead { get { return _dead; } set { _dead = value; animal.animator.SetBool("dead", value); } } [SerializeField] private bool _down; public bool down { get { return _down; } set { _down = value; animal.animator.SetBool("down", value); } } [SerializeField] private bool _landing; public bool landing { get { return _landing; } set { _landing = value; animal.animator.SetBool("landing", value); } } [SerializeField] private bool _up; public bool up { get { return _up; } set { _up = value; animal.animator.SetBool("up", value); } } [SerializeField] private bool _flying; public bool flying { get { return _flying; } set { _flying = value; animal.animator.SetBool("flying", value); } } public Yeji animal; } [SerializeField] public State state = new State(); //委托 public System.Action onDie; }