public SteeringOutput LookWhereYoureGoing(AutonomousAgent character) { // get direction then with tangent we can calculate the angle float toZAngle = 360f - Mathf.Atan2(character.Velocity.x, character.Velocity.y) * Mathf.Rad2Deg; return(Align(character, new Vector3(0, 0, toZAngle))); }
/// <summary> /// Pursue using the target's current position and it's last position. Plus the time between the two positions /// </summary> public SteeringOutput Pursue(AutonomousAgent character, Vector3 targetPosition, Vector3 targetLastPosition, float timeBetween) { // v = s / t Vector3 targetVelocity = (targetPosition - targetLastPostion).normalized / timeBetween; return(Pursue(character, targetPosition, targetVelocity)); }
/// <summary> /// Evade using the target's current position and it's last position. Plus the time between the two positions /// </summary> public SteeringOutput Evade(AutonomousAgent character, Vector3 targetPosition, Vector3 targetLastPostion, float timeBetween) { // v = s / t Vector3 velocityOfTarget = (targetPosition - targetLastPostion) / timeBetween; return(Evade(character, targetPosition, velocityOfTarget)); }
public SteeringOutput Flee(AutonomousAgent character, Vector3 targetPosition) { SteeringOutput steering = new SteeringOutput(); Vector3 direction = character.transform.position - targetPosition; float distance = direction.magnitude; Vector3 targetVelocity; // if the target is outside of detection radius stop if (distance > detectionRadius) { targetVelocity = new Vector3(); } else if (distance / detectionRadius >= 0.9f) { // () will be a [0;0.1] number, to get a [0;1] multiply it by ten // the reverse it so the most outer point gets the 0 float slowdown = 1f - (distance / detectionRadius - 0.9f) * 10f; targetVelocity = direction.normalized * character.maxSpeed * slowdown; } else { // go in that direction with tha max speed targetVelocity = direction.normalized * character.maxSpeed; } // make it acceleration steering.linear = targetVelocity - character.Velocity; steering.linear /= timeToTarget; return(steering); }
public SteeringOutput Face(AutonomousAgent character, Vector3 position) { // get direction then with tangent we can calculate the angle Vector3 direction = position - character.transform.position; float toZAngle = 360f - Mathf.Atan2(direction.x, direction.y) * Mathf.Rad2Deg; return(Align(character, new Vector3(0, 0, toZAngle))); }
/// <summary> /// Velocity matching using the target's position and it's velocity /// </summary> public SteeringOutput VelocityMatch(AutonomousAgent character, Vector3 velocity) { SteeringOutput output = new SteeringOutput(); // get acceleration a = deltaV / deltaT output.linear = (output.linear - character.Velocity) / timeToTarget; return(output); }
public SteeringOutput Align(AutonomousAgent character, Vector3 angle) { SteeringOutput output = new SteeringOutput(); // rotation between the target and the character // only accounts for the z rotation which counts in 2d float rotateOptionLeft = angle.z - character.transform.eulerAngles.z; // we count the right rotation like this because we get the ang between up and target // by subtracting it from 360f and we have to add target rotation to get the full right rotation float rotateOptionRight = character.transform.eulerAngles.z + (360f - angle.z); float rotation; // Decide which direction is better if (Mathf.Abs(rotateOptionLeft) > Mathf.Abs(rotateOptionRight)) { rotation = -rotateOptionRight; } else { rotation = rotateOptionLeft; } float rotationSize = Mathf.Abs(rotation); float targetRotation; // how much we can rotate // we reached our target and we are within the given radius if (rotationSize < targetRadius) { output.angular = -character.RotationVelocity; return(output); // we are withing the slowdown radius -> start the slowdown } else if (rotationSize < slowdownRadius) { // we add target radius so it multiplies by 0 not when it is exactly at desired rotation // but when it is inside the target radius targetRotation = character.maxRotation * (rotationSize / (slowdownRadius + targetRadius)); // normal aligning without anything } else { targetRotation = character.maxRotation; } // apply direction targetRotation *= rotation / rotationSize; // add acceleration output.angular = targetRotation / timeToTarget; return(output); }
private void UpdateLastWeights(AutonomousAgent agent) { lastVelocityWeights = new float[agent.steeringBehaviours.Count]; lastRotationWeights = new float[agent.steeringBehaviours.Count]; for (int i = 0; i < agent.steeringBehaviours.Count; i++) { lastVelocityWeights[i] = agent.steeringBehaviours[i].velocityWeight; lastRotationWeights[i] = agent.steeringBehaviours[i].rotationWeight; } }
public override SteeringOutput GetSteering(AutonomousAgent character, WeightedSteeringBehaviour agentLocalBehaviour) { SteeringOutput output = new SteeringOutput(); if (targetLastPosition != null) { output = VelocityMatch(character, agentLocalBehaviour.target.transform.position, targetLastPosition, Time.fixedDeltaTime); } targetLastPosition = agentLocalBehaviour.target.transform.position; return(output); }
/// <summary> /// Update the weight proportionally to each other /// </summary> /// <param name="agent">The agent in which we want to update the weights</param> /// <param name="amount">How much to update</param> /// <param name="except">To which we don't want to add</param> private void UpdateRotationWeightsProportionallyExcept(AutonomousAgent agent, float amount, int except) { if (!proportionalEditing) { return; } int till = agent.steeringBehaviours.Count; // Add all the other weights together (excluding the except one) // We are going to use this to calculate how much the other weights should change float sumWithoutChanging = 0f; for (int k = 0; k < till; k++) { if (except != k && agent.steeringBehaviours[k].behaviour.CanChangeRotation()) { sumWithoutChanging += agent.steeringBehaviours[k].rotationWeight; } } // Update all the other weights for (int k = 0; k < till; k++) { if (except != k && agent.steeringBehaviours[k].behaviour.CanChangeRotation()) { agent.steeringBehaviours[k].rotationWeight += amount * agent.steeringBehaviours[k].rotationWeight / sumWithoutChanging; } } if (agent.steeringBehaviours.Count > 0) { // If they don't sum up to 1 then add the missing amount to 0f float sum = 0f; for (int i = 0; i < agent.steeringBehaviours.Count; i++) { if (agent.steeringBehaviours[i].behaviour.CanChangeRotation()) { sum += agent.steeringBehaviours[i].rotationWeight; } } for (int i = 0; i < agent.steeringBehaviours.Count; i++) { if (agent.steeringBehaviours[i].behaviour.CanChangeRotation()) { agent.steeringBehaviours[i].rotationWeight += (1f - sum); } } } }
private void DrawBehaviour(AutonomousAgent agent, int index) { agent.steeringBehaviours[index].behaviour = EditorGUILayout.ObjectField("Behaviour: ", agent.steeringBehaviours[index].behaviour, typeof(SteeringBehaviour), false) as SteeringBehaviour; // Steering behaviour settings if (agent.steeringBehaviours[index].behaviour != null) { // go through the corresponding types (to which class which attribute belongs) for (int j = 0; j < AttributeOfBehaviour.correspondingTypes.GetLength(0); j++) { // agent's behaviour has the same type as we are examining if (agent.steeringBehaviours[index].behaviour.GetType().Equals(AttributeOfBehaviour.correspondingTypes[j, 0])) { // get all fields of the behaviour because we are going to check their attributes var fields = agent.steeringBehaviours[index].GetType().GetFields(); for (int i = 0; i < fields.Length; i++) { // all attributes of the current field var attributes = fields[i].GetCustomAttributes(false); for (int k = 0; k < attributes.Length; k++) { // if the current field has the corresponding attribute (to the current class type) if (attributes[k].GetType().Equals(AttributeOfBehaviour.correspondingTypes[j, 1])) { // serialize the behaviour to get the current field as property and // be able to display it to the editor SerializedObject serializedBehav = new SerializedObject(agent.steeringBehaviours[index]); SerializedProperty property = serializedBehav.FindProperty(fields[i].Name); // display the property serializedBehav.Update(); EditorGUILayout.PropertyField(property); // set the property's value because for sme reason it doesn't update // even if I put the update after the field............................sjnosdjfdjfdjkdfsojfdojfdosko fields[i].SetValue(agent.steeringBehaviours[index], property.objectReferenceValue); } } } } } agent.steeringBehaviours[index].behaviour.DrawOnGUI(); } }
/// <summary> /// Evade using the target's position and it's velocity /// </summary> public SteeringOutput Evade(AutonomousAgent character, Vector3 targetPosition, Vector3 velocityOfTarget) { SteeringOutput output = new SteeringOutput(); // it calculates how long it would take the agent to get to the postion of the target // with the current speed. If that is too much it limits that down to maxPrediction. // We need that time to calculate where the target will be in the future in that time // so we can set the target to that position and flee from it. float distance = (targetPosition - character.transform.position).magnitude; float agentSpeed = character.Velocity.magnitude; float prediction; if (agentSpeed <= distance / maxPrediction) { prediction = maxPrediction; } else { prediction = distance / agentSpeed; } // in which direction to evade Vector3 evadeDirection = character.transform.position - (targetPosition + velocityOfTarget * prediction); // what velocity we want to reach Vector3 targetVelocity; // target is out of detection radius if (distance > detectionRadius) { targetVelocity = new Vector3(); } else { targetVelocity = evadeDirection.normalized * character.maxSpeed; } // make acceleration output.linear = targetVelocity - character.Velocity; output.linear /= timeToTarget; return(output); }
public SteeringOutput Seperate(AutonomousAgent character, Vector3 targetPostion) { SteeringOutput steering = new SteeringOutput(); int count = Physics2D.OverlapCircleNonAlloc(character.transform.position, targetRadius, collidedWith, 1 << layerMask); for (int i = 0; i < count; i++) { // not the same gameobject tht we are currently inspecting if (!collidedWith[i].gameObject.Equals(character.gameObject)) { float distance = Vector3.Distance(character.transform.position, collidedWith[i].transform.position); float strength = Mathf.Min(character.maxAcceleration, decayCoefficient * distance * distance); steering.linear += strength * (character.transform.position - collidedWith[i].transform.position).normalized; } } return(steering); }
public SteeringOutput Seek(AutonomousAgent character, Vector3 targetPostion) { SteeringOutput steering = new SteeringOutput(); // Get the direction Vector3 direction = targetPostion - character.transform.position; float distance = direction.magnitude; // We are inside stopping radius if (distance < targetRadius) { steering.linear = -character.Velocity; return(steering); } float targetSpeed; // We are inside the slowdown radius but not the stopping radius if (distance < slowDownRadius) { // start slowing down linearly targetSpeed = distance / (slowDownRadius - targetRadius) * character.maxSpeed; } else // we are outside of slow down radius { targetSpeed = character.maxSpeed; } // combines the direction and the speed Vector3 targetVelocity = direction.normalized; targetVelocity *= targetSpeed; // Calculate acceleration because we can't just change speed immediatly // Calculate delta v then divide it by t because a = v / t steering.linear = targetVelocity - character.Velocity; steering.linear /= timeToTarget; return(steering); }
public SteeringOutput Wander(AutonomousAgent character, WeightedSteeringBehaviour agentLocalBehaviour) { SteeringOutput output = new SteeringOutput(); // update wander angle with [-wanderRate;wanderRate] agentLocalBehaviour.wanderAngle += (UnityEngine.Random.value * 2f - 1f) * wanderRate; Vector3 targetCircleCenter = character.transform.position + character.transform.up * wanderOffset; // move from center of circle by a vector with wanderAngle angle and wanderRadius length Vector3 targetOnCircleLocation = targetCircleCenter + Quaternion.Euler(0, 0, agentLocalBehaviour.wanderAngle) * character.transform.up * wanderRadius; if (character.showGizmos) { Debug.DrawLine(character.transform.position, targetOnCircleLocation, Color.red); } // get rotation output = Face(character, targetOnCircleLocation); // set linear to full acceleration ahead output.linear = character.transform.up * character.maxAcceleration; return(output); }
/// <summary> /// Pursue using the target's position and it's velocity /// </summary> public SteeringOutput Pursue(AutonomousAgent character, Vector3 targetPosition, Vector3 targetVelocity) { // it calculates how long it would take the agent to get to the postion of the target // with the current speed. If that is too much it limits that down to maxPrediction. // We need that time to calculate where the target will be in the future in that time // so we can set the target to that position. float distance = (targetPosition - character.transform.position).magnitude; float agentSpeed = character.Velocity.magnitude; float prediction; if (agentSpeed <= distance / maxPrediction) { prediction = maxPrediction; } else { prediction = distance / agentSpeed; } return(base.Seek(character, targetPosition + targetVelocity * prediction)); }
public override SteeringOutput GetSteering(AutonomousAgent character, WeightedSteeringBehaviour agentLocalBehaviour) { return(Flee(character, agentLocalBehaviour.target.transform.position)); }
public SteeringOutput FollowPath(AutonomousAgent character, WeightedSteeringBehaviour agentLocalBehaviour) { SteeringOutput output = new SteeringOutput(); // Debug.Log(agentLocalBehaviour.currentPathStation + " " + agentLocalBehaviour.path.GetNextStationIndex(agentLocalBehaviour.currentPathStation)); switch (followType) { case FollowType.Station: #region Station // in agentLocalBehaviour we store the current station that is being followed // so we need to check whether the agent is closer to that station or the next one // to check that we need to get the position // if the current path is farther away then the next path if ((agentLocalBehaviour.path.GetDistanceFromPathTo(character.transform.position, agentLocalBehaviour.currentPathStation) > agentLocalBehaviour.path.GetDistanceFromPathTo(character.transform.position, agentLocalBehaviour.path.GetNextStationIndex(agentLocalBehaviour.currentPathStation))) // or we have reached the station || Vector3.Distance(character.transform.position, agentLocalBehaviour.path.GetStationPosition(agentLocalBehaviour.currentPathStation)) <= targetRadius) { // make the next station the target agentLocalBehaviour.currentPathStation = agentLocalBehaviour.path.GetNextStationIndex(agentLocalBehaviour.currentPathStation); } output = Seek(character, agentLocalBehaviour.path.GetStationPosition(agentLocalBehaviour.currentPathStation)); #endregion break; case FollowType.AlwaysReachStation: #region AlwaysReachStation // we have reached the station if (Vector3.Distance(character.transform.position, agentLocalBehaviour.path.GetStationPosition(agentLocalBehaviour.currentPathStation)) <= targetRadius) { // make the next station the target agentLocalBehaviour.currentPathStation = agentLocalBehaviour.path.GetNextStationIndex(agentLocalBehaviour.currentPathStation); } output = Seek(character, agentLocalBehaviour.path.GetStationPosition(agentLocalBehaviour.currentPathStation)); #endregion break; case FollowType.Path: #region Path // here we look ahead on the path with followAheadPercent and set that as a target float percent = agentLocalBehaviour.path.GetClosestPointOnPathPercent(character.transform.position); Vector3 target = agentLocalBehaviour.path.GetPointOnPathPercent(percent + followAheadPercent); output = Seek(character, target); if (character.showGizmos) { Debug.DrawLine(character.transform.position, target, Color.green); } #endregion break; case FollowType.PredictivePath: #region Predictive Path percent = agentLocalBehaviour.path.GetClosestPointOnPathPercent(character.transform.position + character.Velocity * predictTime); target = agentLocalBehaviour.path.GetPointOnPathPercent(percent + followAheadPercent); output = Seek(character, target); if (character.showGizmos) { Debug.DrawLine(character.transform.position, character.transform.position + character.Velocity * predictTime, Color.yellow); Debug.DrawLine(character.transform.position, target, Color.green); } #endregion break; } return(output); }
public override SteeringOutput GetSteering(AutonomousAgent character, WeightedSteeringBehaviour agentLocalBehaviour) { return(FollowPath(character, agentLocalBehaviour)); }
public override SteeringOutput GetSteering(AutonomousAgent character, WeightedSteeringBehaviour agentLocalBehaviour) { return(Align(character, agentLocalBehaviour.transform.eulerAngles)); }
/// <summary> /// Velocity matching using the target's current position and it's last position. Plus the time between the two positions /// </summary> public SteeringOutput VelocityMatch(AutonomousAgent character, Vector3 targetPosition, Vector3 targetLastPosition, float timeBetween) { // get the velocity of the target based on last position v = s / t return(VelocityMatch(character, (targetPosition - targetLastPosition) / timeBetween)); }
public override void OnInspectorGUI() { serializedObject.Update(); AutonomousAgent agent = (AutonomousAgent)target; // Attributes EditorGUILayout.LabelField("Agent attributes", EditorStyles.boldLabel); agent.maxSpeed = EditorGUILayout.FloatField("Max speed: ", agent.maxSpeed); agent.maxAcceleration = EditorGUILayout.FloatField("Max acceleration: ", agent.maxAcceleration); agent.maxRotation = EditorGUILayout.FloatField("Max rotation: ", agent.maxRotation); agent.maxAngularAcceleration = EditorGUILayout.FloatField("Max angular acceleration: ", agent.maxAngularAcceleration); agent.showGizmos = EditorGUILayout.Toggle("Show gizmos", agent.showGizmos); agent.lookWhereGoingInstantly = EditorGUILayout.Toggle("Look where going", agent.lookWhereGoingInstantly); EditorGUILayout.Separator(); EditorGUILayout.LabelField("Behaviour", EditorStyles.boldLabel); // Blending type EditorGUILayout.PropertyField(blendingTypeProp); agent.blendingType = (SteeringBlendingTypes)blendingTypeProp.enumValueIndex; GUILayoutUtility.GetRect(200f, 10f); // We don't need an add button which adds a behaviours because we only have one if ((SteeringBlendingTypes)blendingTypeProp.enumValueIndex == SteeringBlendingTypes.Single) { #region Single custom editor // If we don't have only one behaviour then make it so if (agent.steeringBehaviours.Count > 1) { for (int i = agent.steeringBehaviours.Count - 1; i > 1; i--) { agent.steeringBehaviours.RemoveAt(i); } } else if (agent.steeringBehaviours.Count == 0) { agent.steeringBehaviours.Add(agent.gameObject.AddComponent <WeightedSteeringBehaviour>()); } // Set steering behaviour's weight to 1 agent.steeringBehaviours[0].velocityWeight = 1f; agent.steeringBehaviours[0].rotationWeight = 1f; DrawBehaviour(agent, 0); #endregion } else { #region Weighted custom editor if ((SteeringBlendingTypes)blendingTypeProp.enumValueIndex == SteeringBlendingTypes.WeightedWithGroups) { groups.DoLayoutList(); } proportionalEditing = EditorGUILayout.ToggleLeft("Proportional editing", proportionalEditing); // add behaviour button if (GUILayout.Button("Add new behaviour")) { agent.steeringBehaviours.Add(agent.gameObject.AddComponent <WeightedSteeringBehaviour>()); // Only the first element can start on 1 because they need to be 1 summed up if (agent.steeringBehaviours.Count == 1) { agent.steeringBehaviours[agent.steeringBehaviours.Count - 1].velocityWeight = 1f; } else { agent.steeringBehaviours[agent.steeringBehaviours.Count - 1].velocityWeight = 0f; } } for (int i = agent.steeringBehaviours.Count - 1; i >= 0; i--) { EditorGUILayout.Separator(); if (agent.steeringBehaviours[i].behaviour != null) { string className = agent.steeringBehaviours[i].behaviour.GetType().Name; EditorGUILayout.LabelField(className.Substring(0, className.Length - 17), EditorStyles.boldLabel); } // delete button EditorGUILayout.BeginHorizontal(); { if (agent.steeringBehaviours[i].behaviour != null) { if (agent.steeringBehaviours[i].behaviour.CanChangeVelocity()) { GUI.color = velocitySliderColor; { // velocity weight slider agent.steeringBehaviours[i].velocityWeight = EditorGUILayout.Slider("Velocity weight: ", agent.steeringBehaviours[i].velocityWeight, 0.01f, 1f); } GUI.color = Color.white; } else if (agent.steeringBehaviours[i].behaviour.CanChangeRotation() && !agent.lookWhereGoingInstantly) { GUI.color = rotationSliderColor; { // rotation weight slider agent.steeringBehaviours[i].rotationWeight = EditorGUILayout.Slider("Rotation weight: ", agent.steeringBehaviours[i].rotationWeight, 0.01f, 1f); } GUI.color = Color.white; } } GUI.color = Color.red; if (agent.steeringBehaviours.Count > 1) { // DELETE if (GUILayout.Button("X", GUILayout.Width(30f))) { float updateAmountVelocity = agent.steeringBehaviours[i].velocityWeight; float updateAmountRotation = agent.steeringBehaviours[i].rotationWeight; DestroyImmediate(agent.steeringBehaviours[i]); agent.steeringBehaviours.RemoveAt(i); // Update because we deleted if (updateAmountVelocity != 0) { UpdateVelocityWeightsProportionallyExcept(agent, updateAmountVelocity, -1); } if (updateAmountRotation != 0) { UpdateRotationWeightsProportionallyExcept(agent, updateAmountRotation, -1); } // Update the last weights so it doesn't detect change later down the code UpdateLastWeights(agent); continue; } } GUI.color = Color.white; } EditorGUILayout.EndHorizontal(); if (agent.steeringBehaviours[i].behaviour != null && !agent.lookWhereGoingInstantly && agent.steeringBehaviours[i].behaviour.CanChangeRotation() && agent.steeringBehaviours[i].behaviour.CanChangeVelocity()) // we check for this because if it can't // rotation weight slider // change velocity it drew { GUI.color = rotationSliderColor; // the rotation weight before { agent.steeringBehaviours[i].rotationWeight = EditorGUILayout.Slider("Rotation weight: ", agent.steeringBehaviours[i].rotationWeight, 0.01f, 1f); } GUI.color = Color.white; } DrawBehaviour(agent, i); GUILayoutUtility.GetRect(200f, 10f); } // Here we are doing the magic of updating all other weights when we change one if (lastVelocityWeights != null) { int till = Mathf.Min(lastVelocityWeights.Length, agent.steeringBehaviours.Count); // Go through each one and check whether it has changed for (int i = 0; i < till; i++) { if (!Mathf.Approximately(lastVelocityWeights[i], agent.steeringBehaviours[i].velocityWeight)) { // If it has changed // Calculate how much it did float changeOthers = (lastVelocityWeights[i] - agent.steeringBehaviours[i].velocityWeight); UpdateVelocityWeightsProportionallyExcept(agent, changeOthers, i); break; } if (!Mathf.Approximately(lastRotationWeights[i], agent.steeringBehaviours[i].rotationWeight)) { // If it has changed // Calculate how much it did float changeOthers = (lastRotationWeights[i] - agent.steeringBehaviours[i].rotationWeight); UpdateRotationWeightsProportionallyExcept(agent, changeOthers, i); break; } } } // Store what the weights are UpdateLastWeights(agent); #endregion } serializedObject.ApplyModifiedProperties(); }
public override SteeringOutput GetSteering(AutonomousAgent character, WeightedSteeringBehaviour agentLocalBehaviour) { return(LookWhereYoureGoing(character)); }
public abstract SteeringOutput GetSteering(AutonomousAgent character, WeightedSteeringBehaviour agentLocalBehaviour);