/// <summary> /// Update the mover. /// </summary> /// <remarks> /// <para> /// This method will never fail. /// </para> /// <para> /// With the exception of forced moves, updates will temporarily halt during planner /// failure. /// </para> /// </remarks> /// <returns>True. (Never returns false.)</returns> public bool Update() { Transform trans = agent.transform; if ((agent.data.flags & NavFlag.ForceMove) != 0) { agent.data.position = agent.data.goal; if ((agent.data.flags & NavFlag.GoalRotationEnabled) != 0) { agent.data.rotation = agent.data.goalRotation; } agent.transform.position = agent.data.position.point; agent.transform.rotation = agent.data.rotation; agent.data.flags = (agent.data.flags | NavFlag.HasNewPosition) & ~NavFlag.ForceMove; Debug.Log(agent.transform.name + ": Force Move"); return(true); } // Note: Keep this after the force move handling. if ((agent.data.flags & NavFlag.PlannerFailed) != 0) { // Planner has failed. Early exit. return(true); } agent.data.position = agent.data.desiredPosition; trans.position = agent.data.position.point; Quaternion rotation = agent.data.rotation; if ((agent.data.flags & NavFlag.GoalRotationEnabled) != 0 && agent.IsNear(agent.data.position.point, agent.data.goal.point)) { // Close to goal. So use the goal rotation. rotation = agent.data.goalRotation; } else if (agent.data.desiredSpeedSq > agent.agentGroup.turnThreshold) { // Agent is not near the goal, so use the planner's rotation. // The conditional test prevents unnecessary seeking // at low speeds. rotation = Quaternion.LookRotation( new Vector3(agent.data.desiredVelocity.x, 0, agent.data.desiredVelocity.z)); } trans.rotation = Quaternion.Slerp( trans.rotation, rotation, Time.deltaTime * agent.agentGroup.maxTurnSpeed); agent.data.rotation = trans.rotation; return(true); }
/// <summary> /// Manages the crowd agent target, replanning the long distance path if needed. /// </summary> /// <returns>False on critical failure.</returns> private bool HandleNormalPlanning(bool hasNewGoal) { // Re-targetting will only occur if there is a new goal or getting close to an // intermediate target. if (hasNewGoal || !mGoalInRange && agent.IsNear(agent.data.desiredPosition.point, mTarget.point)) { // First try to set the target without replanning. if (!SetLocalTarget()) { // This can happen when approaching the target from // outside the planned path, which can happen when // the crowd manager takes a different path. // (Or a new goal was set.) if (agent.PlanPath(agent.data.desiredPosition, agent.data.plannerGoal) <= 0) { // Critical failure. Debug.LogError(string.Format( "{0}: Path replan failed. Position: {1}, Goal: {2}" , agent.transform.name , agent.data.desiredPosition.ToString() , agent.data.plannerGoal.ToString())); return(false); } // Next call should not not fail since the path is known to be good. return(SetLocalTarget()); } } else if (mGoalInRange) { /* * Need to monitor to see if the goal has been reached. This can be tricky. * The optimal 'at' and 'near' range ranges are usually too restrictive and too * large respectively. So a fixed value, based on experience, is used instead. * (OK to make this value configurable.) * * Assumption: Other parts of the code detect if a re-evaluation of position * is needed. */ if (mAtGoalState == StateNormal) { if (Vector3Util.IsInRange(agent.data.desiredPosition.point , agent.data.plannerGoal.point , 0.12f, agent.data.heightTolerence)) { // Remove from the crowd and add back, exactly at the goal with a zero max // speed. Basically, puts a static obstacle into the crowd. mAtGoalState = StateInRange; agent.RemoveFromCrowd(); CrowdAgentParams config = agent.crowdConfig; config.maxSpeed = 0; agent.crowdAgent = agent.navGroup.crowd.AddAgent(agent.data.plannerGoal.point, config); agent.data.flags &= ~NavFlag.CrowdConfigUpdated; // Debug.Log("Transition to 'at goal' task."); } } } return(true); }