using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; using DG.Tweening; /* 动物组件-野狼 */ public class Wolf : TargetAnimal { //动画播放器 AnimationPlayer ap; //寻路代理 NavMeshAgent agent; //血量 [System.NonSerialized] public int hp = 1; void Awake() { wolfSet.Add(this); state = new State(); ap = GetComponent(); agent = GetComponent(); this.onlineHandler.InitOnAwake(this); if (GameMgr.ins) { GameObject stoneObstacle = GameMgr.ins.transform.Find("StoneObstacle").gameObject; if (!stoneObstacle.activeSelf) stoneObstacle.SetActive(true); } } void Start() { if (onlineHandler.isMirror) return; initAniListener(); } static int _avoidancePriority = 0; static int avoidancePriority { get { if (_avoidancePriority < 50) { _avoidancePriority++; } else { _avoidancePriority = 1; } return _avoidancePriority; } } void Update() { this.onlineHandler.Update(); if (this.onlineHandler.isMirror) { UpdateOutline(); return; } //寻路过程监测 if (HasCloseToDestination()) { OnReachDestination(); } UpdateAutoStrategy(); UpdateAction(); UpdateOutline(); } void OnDestroy() { wolfSet.Remove(this); WolfActGrid.ins.areaMatrix.releaseOccupy(this); } //可选皮肤材质 [SerializeField] Material[] materials; [SerializeField] Material[] outlineMaterials; [System.NonSerialized] public int colorType = -1; public void ChangeColorByType(int type) { colorType = type; if (skinnedMeshRenderer == null) skinnedMeshRenderer = GetComponentInChildren(); baseMaterial = materials[type - 1]; outlineMaterial = outlineMaterials[type - 1]; skinnedMeshRenderer.material = baseMaterial; } Material baseMaterial; Material outlineMaterial; SkinnedMeshRenderer skinnedMeshRenderer = null; bool hasOutLine = false; void UpdateOutline() { Camera camera = Camera.main; if (!camera) return; float distance = Vector3.Distance(this.transform.position, camera.transform.position); if (distance > 20) { if (!hasOutLine) { hasOutLine = true; skinnedMeshRenderer.material = outlineMaterial; } } else { if (hasOutLine) { hasOutLine = false; skinnedMeshRenderer.material = baseMaterial; } } } public override void OnHit(Arrow arrow, Vector3 hitPoint, string partName) { arrow.Head().position = hitPoint + arrow.transform.forward * 0.1f; arrow.Hit(); if (onlineHandler.isMirror) { onlineHandler.onHitData = onlineHandler.uid.ToString() + "," + partName; return; } OnHitLogic(arrow, partName); } public void OnHitLogic(Arrow arrow, string partName) { int hurtValue = 0; if (partName == "Leg" || partName == "Tail") { hurtValue = 2; } else if (partName == "Body") { hurtValue = 3; } else if (partName == "Head") { hurtValue = 10; } hp -= hurtValue; GameEventCenter.ins.onTargetAnimalHurt?.Invoke(this, hurtValue); if (hp > 0) { Hurt(); } else { Die(arrow); } } void Die(Arrow arrow) { if (state.dead) return; if (arrow != null) { arrow.onDoNextShoot += delegate() { Destroy(this.gameObject); }; } else { //需要借住关卡的GameMode来清除 onlineHandler.onDoNextShootWillDestroy = true; } state.ResetActionState(); state.dead = true; this.agent.enabled = false; onDie?.Invoke(this); AudioMgr.ins.PlayAnimalEffect("wolf_die", AudioMgr.GetAudioSource(this.gameObject)); this.onlineHandler.deadID++; } void Hurt() { if (!state.attacking) { if (Random.value < 0.2f) { CancelLockTarget(); state.ResetActionState(); state.lockingTarget = true; needAmbush = false; } else { //未锁定目标阶段时被击退,因为可能会再次满足z路径条件,因此需要重置Z路径记录 if (!state.lockingTarget) ResetZPathRecord(); CancelLockTarget(1); RunAwayFromHunter(); } } AudioMgr.ins.PlayAnimalEffect("wolf_injured", AudioMgr.GetAudioSource(this.gameObject)); this.onlineHandler.injuredID++; } void SetAgentStopped(bool value) { //要捕捉错误,不然会报错 try { this.agent.isStopped = value; } catch (System.Exception) {} } //启动寻路 void SetDestination(Vector3 pos) { state.ResetActionState(); state.moving = true; SetAgentStopped(false); if (useRunbackPoint) { useRunbackPoint = false; WolfActGrid.ins.areaMatrix.releaseOccupy(this); } else { bool occupySuccess = WolfActGrid.ins.areaMatrix.occupyPos(pos, this); if (!occupySuccess) { pos = transform.position; } } this.agent.destination = pos; } //寻路结束 void OnReachDestination() { if (!state.moving) return; SetAgentStopped(true); state.ResetActionState(); } //是否已经接近目的地(寻路完成判断) bool HasCloseToDestination() { if (!state.moving) return true; if (Vector3.Distance(this.agent.nextPosition, this.agent.destination) < 0.25f) { return true; } if (state.movingTime > 0.1f && this.agent.velocity.magnitude < 0.05f) { return true; } return false; } int lastAutoType; float willStayTime; bool willAttack = false; bool hasRunAround = false; int needRunAroundCount = 0; bool hasRunToHunter = false; //取消锁定状态 //cancelType==1时,表示触发逃跑 void CancelLockTarget(int cancelType = 0) { if (!state.lockingTarget) return; state.lockingTarget = false; if (cancelType == 1) state.lockingTarget = true; RandomWillStayTime(); willAttack = false; hasRunAround = false; if (cancelType == 1) hasRunAround = true; needRunAroundCount = 0; if (cancelType == 1) needRunAroundCount = 2; hasRunToHunter = false; if (cancelType == 1) hasRunToHunter = true; } //帧更新逻辑-自动策略 void UpdateAutoStrategy() { if (state.dead) return; if (state.lockingTarget) { // if (state.attacking) { // this.transform.LookAt(this.hunterPosition); // } if (!hasRunToHunter) { hasRunToHunter = true; RunToAttackHunter(true); willAttack = true; return; } if (!state.moving && !state.attacking) { if (willAttack) { willAttack = false; Attack(); needRunAroundCount = 3; return; } if (needRunAroundCount > 0) { needRunAroundCount--; useRunbackPoint = !hasRunAround; RunAroundHunter(!hasRunAround); hasRunAround = true; return; } if (hasRunAround) { hasRunAround = false; RunToAttackHunter(false); } willAttack = true; return; } return; } //以下为未锁定目标时的自动策略 if (state.staying) { if (state.stayingTime > willStayTime) { MoveSlowlyInZPath(); } return; } if (state.moving) { lastAutoType = 2; } if (!state.staying && !state.moving) { if (lastAutoType == 1 || lastAutoType == 0) { MoveSlowlyInZPath(); } else { if (!canCreateZPath && zPathPoints.Count == 0) { MoveSlowlyInZPath();//内含判断,状态将会转化为锁定目标 } else { RandomWillStayTime(); Stay(true); } } } } void RandomWillStayTime() { this.willStayTime = Random.value * 4 + 2; // this.willStayTime = 0; } void LookAtHunter() { this.transform.LookAt(hunterPosition); } bool needAmbush = true; //攻击 void Attack() { state.ResetActionState(); state.attacking = true; curAnimIndex = -1; /*注意:这样可避免狼两次使用同一攻击动作而第二次无法播放的情况 */ int hurtValue = 2; if (canAttackID == "A") { int avoidancePriority = this.agent.avoidancePriority; float baseoffset = this.agent.baseOffset; Vector3 hunterPos = hunterPosition; Vector3 jumpPoint = default; //起跳点 Vector3 landPoint = default; //落地点 Vector3 displace = default; //位移 Sequence seq = DOTween.Sequence(); //伏击动作 + 转头 Quaternion ambushQuaStart = transform.rotation; Vector3 rotateDir = hunterPos - transform.position; rotateDir.y = 0; Quaternion ambushQuaEnd = Quaternion.FromToRotation(Vector3.forward, rotateDir); float needRotateAngle = Quaternion.Angle(ambushQuaStart, ambushQuaEnd); this.agent.avoidancePriority = 0; if (needRotateAngle > 20) { state.ambushRotating = true; seq.Append(DOTween.To(() => 0f, value => { transform.rotation = Quaternion.Lerp(ambushQuaStart, ambushQuaEnd, value); }, 1f, needRotateAngle / 180f * 0.5f)); } else { ambushQuaEnd = ambushQuaStart; } seq.AppendCallback(delegate() { state.ambushRotating = false; if (needAmbush) state.ambushing = true; }); if (needAmbush) { seq.Append(DOTween.To(() => 0f, value => { this.transform.rotation = ambushQuaEnd; }, 1f, 2f)); } seq.AppendCallback(delegate() { if (state.dead) { seq.Kill(); return; } needAmbush = true; state.ambushing = false; state.attackA = true; #region //起点和终点计算 jumpPoint = this.transform.position; hunterPos.y = jumpPoint.y; Vector3 deltaPointer = jumpPoint - hunterPos; landPoint = hunterPos + deltaPointer.normalized * 1f; displace = landPoint - jumpPoint; #endregion }); //跳跃 seq.Append(DOTween.To(() => 0f, value => { this.transform.position = jumpPoint + displace * value; LookAtHunter(); if (value < 0.5) { this.agent.baseOffset = baseoffset + value; } else { this.agent.baseOffset = baseoffset + (1 - value); if (!state.dead && state.attackA) { state.attackA = false; playAniJumpDown(); } } }, 1f, 0.8f)); seq.AppendCallback(delegate() { this.agent.avoidancePriority = avoidancePriority; this.transform.position = landPoint; LookAtHunter(); if (!state.dead) onAttack?.Invoke(this, hurtValue); }); seq.Append(DOTween.To(() => 0f, value => { this.transform.position = landPoint; LookAtHunter(); }, 1f, 0.3f)); seq.AppendCallback(delegate() { if (!state.dead) stopAniJumpDown(); //停止动画,则动画自带的位移也停止变化 this.transform.position = landPoint; LookAtHunter(); }); seq.Append(DOTween.To(() => 0f, value => { this.transform.position = landPoint; LookAtHunter(); }, 1f, 0.1f)); //通过dotween是它不被动画的位移影响; seq.AppendCallback(delegate() { state.ResetActionState(); }); } else if (canAttackID == "B") { state.attackB = true; hurtValue = 3; onAttack?.Invoke(this, hurtValue); } } //逃跑远离猎人 void RunAwayFromHunter() { Vector3 backVec = GetPointerHunterToMe(); SetDestination(transform.position + backVec.normalized * 6); state.moveQuickly = true; this.agent.speed = 5f; } //跑到能攻击玩家的坐标点 string canAttackID = null; void RunToAttackHunter(bool quickly) { float needDistance; Vector3 displace = GetPointerHunterToMe().normalized; // if (Random.value < 0.33f) { // needDistance = 2; // canAttackID = "B"; // } else { needDistance = 11.5f; canAttackID = "A"; // } Vector3 newPos = animalsBaseT.position + displace * needDistance; SetDestination(newPos); state.moveSlowly = !quickly; state.moveQuickly = quickly; agent.speed = quickly ? 5f : 0.8f; } //在敌人身边奔跑徘徊 bool useRunbackPoint = false; void RunAroundHunter(bool quickly) { Vector3 newPos; if (useRunbackPoint) { newPos = WolfActGrid.ins.GetRunBackPointAfterPounce(); } else { float baseDistance = 13; float baseDistanceMoveRange = 5; Vector3 standardPointer = animalsBaseT.forward; Vector3 pointerWithLen = standardPointer.normalized * (baseDistance + Random.value * baseDistanceMoveRange); pointerWithLen = Quaternion.AngleAxis(Random.Range(-14f, 14f), Vector3.up) * pointerWithLen; newPos = animalsBaseT.position + pointerWithLen; } SetDestination(newPos); state.moveSlowly = !quickly; state.moveQuickly = quickly; agent.speed = quickly ? 5f : 0.8f; } //Z字型路径 Queue zPathPoints = new Queue(); bool canCreateZPath = true; void ResetZPathRecord() { zPathPoints.Clear(); canCreateZPath = true; } void MoveSlowlyInZPath() { if (zPathPoints.Count > 0) { SetDestination(zPathPoints.Dequeue()); state.moveSlowly = true; agent.speed = 0.8f; return; } if (!canCreateZPath) { state.lockingTarget = true; return; } //构建Z字型路径 zPathPoints.Clear(); Vector3 standardVec = animalsBaseT.forward; Vector3 hunterPos = animalsBaseT.position; Vector3 myPos = this.transform.position; float minDistance = 13; float maxDistance = 25; while (true) { myPos.y = hunterPos.y; float distance = Vector3.Distance(myPos, hunterPos); if (distance > minDistance) { Vector3 vecToMyPos = myPos - hunterPos; //判断位置在猎人左边还是右边 float crossY = Vector3.Cross(standardVec, vecToMyPos).y; float nextAngle; if (crossY >= 0) { //在右边 nextAngle = Random.Range(-14f, -10f);//下次就向左边走 } else { //在左边 nextAngle = Random.Range(10f, 14f);;//下次就向右边走 } float nextDistance = distance - Random.Range(3.5f, 4.5f); if (nextDistance > maxDistance) { nextDistance = maxDistance; } if (nextDistance < minDistance) { nextDistance = minDistance; } if (Mathf.Abs(nextDistance - distance) < 1.2f) { break; } Vector3 displace = (Quaternion.AngleAxis(nextAngle, Vector3.up) * standardVec) * nextDistance; myPos = hunterPos + displace; zPathPoints.Enqueue(myPos); // Debug.Log(Quaternion.AngleAxis(-180f, Vector3.up) * (myPos - hunterPos)); } else { break; } } canCreateZPath = false; } //停留 void Stay(bool correct = false) { if (state.moving || correct) { this.agent.destination = this.agent.nextPosition; SetAgentStopped(true); } state.ResetActionState(); state.staying = true; } //动画播放 [System.NonSerialized] public int curAnimIndex = -1; void playAniStay() { ap.play(curAnimIndex = 7, WrapMode.Loop); } bool isAniStay() { return curAnimIndex == 7; } void playAniMoveSlowly() { ap.play(curAnimIndex = 5, WrapMode.Loop); } bool isAniMoveSlowly() { return curAnimIndex == 5; } void playAniMoveQuickly() { ap.play(curAnimIndex = 4, WrapMode.Loop); } bool isAniMoveQuickly() { return curAnimIndex == 4; } void playAniAmbush() { ap.play(curAnimIndex = 3, WrapMode.Loop); } bool isAniAmbush() { return curAnimIndex == 3; } void playAniAttakA() { ap.play(curAnimIndex = 1, WrapMode.Once); } bool isAniAttakA() { return curAnimIndex == 1; } void playAniJumpDown() { ap.play(curAnimIndex = 6, WrapMode.Once); } void stopAniJumpDown() { ap.StopAnimation(6); } void playAniAttakB() { ap.play(curAnimIndex = 0, WrapMode.Once); } bool isAniAttakB() { return curAnimIndex == 0; } void playAniDie() { ap.play(curAnimIndex = 2, WrapMode.Once); } bool isAniDie() { return curAnimIndex == 2; } void initAniListener() { this.ap.completeCallback = delegate(AnimationPlayerCompleteResult res) { if (res.index == 0) { this.state.ResetActionState(); } }; } //帧更新逻辑-通过状态更新动作动画 void UpdateAction() { if (state.staying) { state.stayingTime += Time.deltaTime; if (!isAniStay()) playAniStay(); } else if (state.moving) { state.movingTime += Time.deltaTime; if (state.moveSlowly && !isAniMoveSlowly()) playAniMoveSlowly(); if (state.moveQuickly && !isAniMoveQuickly()) playAniMoveQuickly(); } else if (state.attacking) { if (state.ambushRotating && !isAniMoveQuickly()) playAniMoveQuickly(); if (state.ambushing && !isAniAmbush()) playAniAmbush(); if (state.attackA && !isAniAttakA()) playAniAttakA(); if (state.attackB && !isAniAttakB()) playAniAttakB(); } else if (state.dead) { if (!isAniDie()) playAniDie(); } } //状态 [System.Serializable] public class State { //动作区 public bool staying = false; public bool moving = false; public bool moveSlowly = false; //慢走 public bool moveQuickly = false; //跑动 public bool attacking = false; public bool ambushRotating = false; //扑击前的转身 public bool ambushing = false; //扑击前的伏击状态 public bool attackA = false; //扑击 public bool attackB = false; //撕咬 public bool dead = false; public float stayingTime = 0; public float movingTime = 0; //特定区 public bool lockingTarget = false; //锁定目标,只有锁定目标后,才能调用攻击接口,这是给自己的规定 //重置动作区状态 public void ResetActionState() { this.staying = false; this.moving = false; this.moveSlowly = false; this.moveQuickly = false; this.attacking = false; this.ambushRotating = false; this.ambushing = false; this.attackA = false; this.attackB = false; this.dead = false; this.stayingTime = 0; this.movingTime = 0; } } [SerializeField] public State state; //委托 public System.Action onDie; public System.Action onAttack; #region 联机附加部分 public static HashSet wolfSet = new HashSet(); public OnlineHandler onlineHandler = new OnlineHandler(); public class OnlineHandler { Wolf animal; public int uid; public bool isMirror; public WolfSyncData outputSyncData; public void InitOnAwake(Wolf animal) { this.animal = animal; if (isMirror) { GameObject.Destroy(this.animal.agent); } } public void Update() { if (isMirror) { if (inputSyncData == null) return; if (!hasUpdateBySyncData) { this.animal.transform.position = syncPosition; this.animal.transform.rotation = syncRotation; hasUpdateBySyncData = true; } else { this.animal.transform.position = Vector3.Lerp(this.animal.transform.position, syncPosition, Time.deltaTime * 15); this.animal.transform.rotation = Quaternion.Lerp(this.animal.transform.rotation, syncRotation, Time.deltaTime * 15); } if (this.animal.colorType != inputSyncData.ct) { this.animal.ChangeColorByType(inputSyncData.ct); } if (inputSyncData.ai != this.animal.curAnimIndex) { this.animal.curAnimIndex = inputSyncData.ai; if (this.animal.isAniStay()) { this.animal.playAniStay(); } else if (this.animal.isAniMoveSlowly()) { this.animal.playAniMoveSlowly(); } else if (this.animal.isAniMoveQuickly()) { this.animal.playAniMoveQuickly(); } else if (this.animal.isAniAmbush()) { this.animal.playAniAmbush(); } else if (this.animal.curAnimIndex >= 0) { this.animal.ap.play(this.animal.curAnimIndex, WrapMode.Once); } } if (inputSyncData.ii != injuredID) { injuredID = inputSyncData.ii; AudioMgr.ins.PlayAnimalEffect("wolf_injured", AudioMgr.GetAudioSource(this.animal.gameObject)); } if (inputSyncData.di != deadID) { deadID = inputSyncData.di; AudioMgr.ins.PlayAnimalEffect("wolf_die", AudioMgr.GetAudioSource(this.animal.gameObject)); } return; } if (GlobalData.pkMatchType == PKMatchType.OnlinePK) { if (outputSyncData == null) outputSyncData = new WolfSyncData(); outputSyncData.SetData(this.animal); } } private WolfSyncData _inputSyncData; public WolfSyncData inputSyncData { get { return _inputSyncData; } set { _inputSyncData = value; uid = value.id; syncRotation.x = value.rx; syncRotation.y = value.ry; syncRotation.z = value.rz; syncRotation.w = value.rw; syncPosition.x = value.px; syncPosition.y = value.py; syncPosition.z = value.pz; } } private Quaternion syncRotation; private Vector3 syncPosition; private bool hasUpdateBySyncData = false; public bool isInvalid = false; //外部运算用的 public string onHitData = null; public bool onDoNextShootWillDestroy = false; public int injuredID = 0; public int deadID = 0; } #endregion public override int GetOnlineID() { return onlineHandler.uid; } }