using UnityEngine; #if !(UNITY_5_1 || UNITY_5_2 || UNITY_5_3 || UNITY_5_4) using UnityEngine.AI; #endif using BehaviorDesigner.Runtime; using BehaviorDesigner.Runtime.Tasks; using BehaviorDesigner.Runtime.Tasks.Unity.Timeline; namespace Valheim { public class PlayerFollow : NavMeshGroupMovement2 { public SharedFloat neighborDistance = 10; public SharedFloat leaderBehindDistance = 2; public SharedFloat separationDistance = 2; public SharedFloat aheadDistance = 2; public SharedFloat moveDistance = 2; public SharedGameObject leader = null; public SharedBool hasInit = false; // component cache private Transform leaderTransform; // private NavMeshAgent leaderAgent; private Rigidbody leaderRigidbody; private Vector3[] lastTargetPositions; private bool[] hasMoveds; private Vector3[] aroundOffsets; public SharedFloat followRadius = 2f; // 默认离玩家 4 米,你可以在 Inspector 里调节 // The agents will always be following the leader so always return running public override TaskStatus OnUpdate() { // 初始化 if (!hasInit.Value && leader.Value != null && agents.Value.Count > 0) { hasInit.Value = true; //leaderTransform = leader.Value.GetComponent().leaderPoint; leaderTransform = leader.Value.transform; // leaderAgent = leader.Value.GetComponent(); leaderRigidbody = leader.Value.GetComponent(); hasMoveds = new bool[agents.Value.Count]; lastTargetPositions = new Vector3[agents.Value.Count]; navMeshAgents = new NavMeshAgent[agents.Value.Count]; transforms = new Transform[agents.Value.Count]; for (int i = 0; i < agents.Value.Count; i++) { hasMoveds[i] = false; lastTargetPositions[i] = LeaderBehindPosition() + Vector3.one * (moveDistance.Value + 1); transforms[i] = agents.Value[i].transform; navMeshAgents[i] = agents.Value[i].GetComponent(); navMeshAgents[i].speed = agents.Value[i].GetComponent().speed; navMeshAgents[i].angularSpeed = angularSpeed.Value; navMeshAgents[i].isStopped = false; } aroundOffsets = new Vector3[agents.Value.Count]; float radius = followRadius.Value; for (int i = 0; i < agents.Value.Count; i++) { float angle = 2 * Mathf.PI * i / agents.Value.Count; aroundOffsets[i] = new Vector3( Mathf.Cos(angle) * radius, 0, Mathf.Sin(angle) * radius ); } } // 有leader与跟随者才进行更新 if (leader.Value != null && agents.Value.Count > 0) { Vector3 behindPosition = LeaderBehindPosition(); if (leader.Value == null || agents.Value.Count == 0) return TaskStatus.Running; // 1. 每帧重新取玩家中心点 Vector3 center = leader.Value.transform.position; // Debug.Log("x-" + behindPosition.x + " y-" + behindPosition.y + " z-" + behindPosition.z); // Determine a destination for each agent for (int i = 0; i < agents.Value.Count; ++i) { var pet = agents.Value[i].GetComponent(); if (pet != null && (pet.state == PetState.Terminal || pet.order == OrderType.Attack|| pet.state== PetState.CurWin)) continue; // 基础环绕位置(始终保持在 followRadius 半径处) Vector3 targetPosition = center + aroundOffsets[i]; //(可选)再加一点分离,避免宠物互相重叠 targetPosition += DetermineSeparation(i) * 0.5f; // 发起移动 if ((targetPosition - lastTargetPositions[i]).sqrMagnitude >= moveDistance.Value * moveDistance.Value) { SetDestination(i, targetPosition); lastTargetPositions[i] = targetPosition; hasMoveds[i] = true; } else if (hasMoveds[i] && (targetPosition - transforms[i].position).magnitude < moveDistance.Value) { navMeshAgents[i].isStopped = true; hasMoveds[i] = false; lastTargetPositions[i] = targetPosition; } } } return TaskStatus.Running; } // 获得leader后方的一个位置,这个根据leader的移动速度来计算 private Vector3 LeaderBehindPosition() { // The behind position is the normalized inverse of the leader's velocity multiplied by the leaderBehindDistance // return leaderTransform.position + (-leaderAgent.velocity).normalized * leaderBehindDistance.Value + (-leaderTransform.forward) * leaderBehindDistance.Value; //return leaderTransform.position + (-leaderRigidbody.velocity).normalized * leaderBehindDistance.Value; return leaderTransform.position; } // Determine the separation between the current agent and all of the other agents also following the leader private Vector3 DetermineSeparation(int agentIndex) { var separation = Vector3.zero; int neighborCount = 0; var agentTransform = transforms[agentIndex]; // Loop through each agent to determine the separation for (int i = 0; i < agents.Value.Count; ++i) { // The agent can't compare against itself if (agentIndex != i) { // Only determine the parameters if the other agent is its neighbor if (Vector3.SqrMagnitude(transforms[i].position - agentTransform.position) < neighborDistance.Value) { // This agent is the neighbor of the original agent so add the separation separation += transforms[i].position - agentTransform.position; neighborCount++; } } } // Don't move if there are no neighbors if (neighborCount == 0) { return Vector3.zero; } // Normalize the value return ((separation / neighborCount) * -1).normalized * separationDistance.Value; } // Use the dot product to determine if the leader is looking at the current agent public bool LeaderLookingAtAgent(int agentIndex) { return Vector3.Dot(leaderTransform.forward, transforms[agentIndex].forward) < -0.5f; } // Reset the public variables public override void OnReset() { base.OnReset(); neighborDistance = 10; leaderBehindDistance = 2; separationDistance = 2; aheadDistance = 2; leader = null; } } }