/// <summary> /// Starts moving to the specified start location. /// </summary> /// <param name="startLocations">The locations the character can move towards. If multiple locations are possible then the closest valid location will be used.</param> /// <param name="onArriveAbility">The ability that should be started as soon as the character arrives at the location.</param> /// <returns>True if the MoveTowards ability is started.</returns> public bool StartMoving(AbilityStartLocation[] startLocations, Ability onArriveAbility) { // MoveTowards doesn't need to start if there is no start location. if (startLocations == null || startLocations.Length == 0) { return(false); } // The arrive ability must exist and be unique. If the ability is already set then StartMoving may have been triggered because the arrive ability // should start. if (onArriveAbility == null || onArriveAbility == m_OnArriveAbility) { return(false); } // No reason to start if the character is already in a valid start location. for (int i = 0; i < startLocations.Length; ++i) { if (startLocations[i].IsPositionValid(m_Transform.position, m_Transform.rotation, m_CharacterLocomotion.Grounded) && startLocations[i].IsRotationValid(m_Transform.rotation)) { return(false); } } // The character needs to move - start the ability. m_StartLocation = GetClosestStartLocation(startLocations); m_OnArriveAbility = onArriveAbility; // The movement speed will depend on the current speed the character is moving. m_MovementMultiplier = m_StartLocation.MovementMultiplier; if (m_SpeedChangeAbilities != null) { for (int i = 0; i < m_SpeedChangeAbilities.Length; ++i) { if (m_SpeedChangeAbilities[i].IsActive) { m_MovementMultiplier = m_SpeedChangeAbilities[i].SpeedChangeMultiplier; break; } } } if (m_OnArriveAbility.Index < Index) { Debug.LogWarning("Warning: " + m_OnArriveAbility.GetType().Name + " has a higher priority then the MoveTowards ability. This will cause unintended behavior."); } StartAbility(); // MoveTowards may be starting when all of the inputs are being checked. If it has a lower index then the update loop won't run initially // which will prevent the TargetDirection from having a valid value. Run the Update loop immediately so TargetDirection is correct. if (Index < onArriveAbility.Index) { Update(); } return(true); }
private static void DrawStartLocationGizmo(AbilityStartLocation startLocation, GizmoType gizmoType) { var transform = startLocation.transform; Handles.matrix = Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, transform.lossyScale); // Wire lines will indicate the valid starting positions. Gizmos.color = new Color(1, 0.6f, 0, 0.7f); Gizmos.DrawWireCube(startLocation.Offset, startLocation.Size); // Draw an arc indicating the direction that the character can face. Handles.matrix = Gizmos.matrix = Matrix4x4.TRS(transform.TransformPoint(startLocation.Offset), transform.rotation * Quaternion.Euler(0, startLocation.YawOffset, 0), transform.lossyScale); Handles.color = new Color(0, 1, 0, 0.7f); var radius = Mathf.Min(0.5f, Mathf.Max(Mathf.Abs(startLocation.Offset.z), 0.2f)); Handles.DrawWireDisc(Vector3.zero, Vector3.up, radius); Handles.color = new Color(0, 1, 0, 0.2f); Handles.DrawSolidArc(Vector3.zero, Vector3.up, Quaternion.AngleAxis(startLocation.Angle / 2 - startLocation.Angle, Vector3.up) * Vector3.forward, startLocation.Angle, radius); // Draw arrows pointing in the direction that the character can face. radius /= 2; Handles.color = new Color(0, 1, 0, 0.7f); Handles.DrawLine(Vector3.zero, (Vector3.forward * 2f) * radius); Handles.DrawLine((Vector3.forward * 2) * radius, ((Vector3.forward * 1.5f * radius) + (Vector3.left * 0.5f) * radius)); Handles.DrawLine((Vector3.forward * 2) * radius, ((Vector3.forward * 1.5f * radius) + (Vector3.right * 0.5f) * radius)); if (startLocation.Angle >= 180) { Handles.DrawLine((Vector3.left * 2) * radius, (Vector3.right * 2) * radius); Handles.DrawLine((Vector3.left * 2) * radius, ((Vector3.left * 1.5f * radius) + (Vector3.forward * 0.5f) * radius)); Handles.DrawLine((Vector3.left * 2) * radius, ((Vector3.left * 1.5f * radius) + (Vector3.back * 0.5f) * radius)); Handles.DrawLine((Vector3.right * 2) * radius, ((Vector3.right * 1.5f * radius) + (Vector3.forward * 0.5f) * radius)); Handles.DrawLine((Vector3.right * 2) * radius, ((Vector3.right * 1.5f * radius) + (Vector3.back * 0.5f) * radius)); if (startLocation.Angle == 360) { Handles.DrawLine(Vector3.zero, (Vector3.back * 2f) * radius); Handles.DrawLine((Vector3.back * 2) * radius, ((Vector3.back * 1.5f * radius) + (Vector3.left * 0.5f) * radius)); Handles.DrawLine((Vector3.back * 2) * radius, ((Vector3.back * 1.5f * radius) + (Vector3.right * 0.5f) * radius)); } } }
/// <summary> /// Returns the closest start location out of the possible AbilityStartLocations. /// </summary> /// <param name="startLocations">The locations the character can move towards.</param> /// <returns>The best location out of the possible AbilityStartLocations.</returns> private AbilityStartLocation GetClosestStartLocation(AbilityStartLocation[] startLocations) { // If only one location is available then it is the closest. if (startLocations.Length == 1) { return(startLocations[0]); } // Multiple locations are available. Choose the closest location. AbilityStartLocation startLocation = null; var closestDistance = float.MaxValue; float distance; for (int i = 0; i < startLocations.Length; ++i) { if ((distance = startLocations[i].GetTargetDirection(m_Transform.position, m_Transform.rotation).sqrMagnitude) < closestDistance) { closestDistance = distance; startLocation = startLocations[i]; } } return(startLocation); }