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; } static int _avoidancePriority = 0; static int avoidancePriority { get { if (_avoidancePriority < 50) { _avoidancePriority++; } else { _avoidancePriority = 1; } return _avoidancePriority; } } void OnDestroy() { tryReleaseOccupyTree(); } 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_injured", AudioMgr.GetAudioSource(this.gameObject)); if (currentHeight < 1.5f) { arrow.arrowCameraComp.arrowCameraTemplate.SendMsg(0, null); } } void Hurt() { CancelStand(); CancelFlyStay(); 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; float canFlyStayTime = 3f; //能够在前几棵树徘徊 [System.NonSerialized] public int canFlyTreeCount = 4; const float downSpeed = 2.0f; //更新逻辑-起飞降落 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; } } 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 * downSpeed; 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 = 8f + Random.value * 2f; animator.CrossFade("IdleOnGround2", 0.2f); } } return; } } TreeAreaRecorder.AreaInfo targetAreaInfo; bool hasCheckFlyDown = false; void UpdateBehavior() { if (state.flyStaying) { canFlyStayTime -= Time.deltaTime; if (canFlyStayTime <= 0) { state.flyStaying = false; } return; } if (state.standing) { canStandTime -= Time.deltaTime; if (canStandTime <= 0) { CancelStand(); } return; } if (state.landing) return; if (state.flying && !state.down && !state.up) { if (!hasCheckFlyDown) { float downNeedTime = flyPlaneHeight / downSpeed; float keyDistance = this.agent.speed * downNeedTime; //触发下降的水平距离阈值 if (Vector3.Distance(this.transform.position, this.agent.destination) < keyDistance) { hasCheckFlyDown = true; if (Random.value < 0.6 && tryOccupyTree()) { state.down = true; } else { state.flyStaying = true; canFlyStayTime = 4f + Random.value * 2f; } } } } if (moving) return; //飞向别的树 #region 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 areaInfo = areaInfos[Random.Range(1, canFlyTreeCount)]; Vector3 newPos = YejiHuntGameMode.CalculateNewPosByTreePos(animalsBaseT, flyPlaneHeight, areaInfo); SetDestination(newPos); targetAreaInfo = areaInfo; hasCheckFlyDown = false; #endregion } 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); tryReleaseOccupyTree(); } void CancelFlyStay() { if (!state.flyStaying) return; canFlyStayTime = 0; } bool tryOccupyTree() { if (targetAreaInfo == null) return false; if (targetAreaInfo.occupy != null) return false; else { targetAreaInfo.occupy = this; return true; } } void tryReleaseOccupyTree() { if (targetAreaInfo != null && targetAreaInfo.occupy != null && targetAreaInfo.occupy.Equals(this)) { targetAreaInfo.occupy = null; } } static float[] preFlyHeights; static bool[] preFlyHeightCools; public static void InitPreHeights() { preFlyHeights = new float[]{1.7f, 2.2f}; preFlyHeightCools = new bool[]{false, false}; } public static float RandomOnePreHeight() { List indexes = new List(); for (int i = 0; i < preFlyHeightCools.Length; i++) { if (!preFlyHeightCools[i]) { indexes.Add(i); } if (preFlyHeightCools[i]) preFlyHeightCools[i] = false; } int index = indexes[Random.Range(0, indexes.Count)]; preFlyHeightCools[index] = true; return preFlyHeights[index]; } #endregion //状态 [System.Serializable] public class State { public bool standing = false; public bool flyStaying = 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; }