/// <summary> /// The ability has stopped running. /// </summary> /// <param name="force">Was the ability force stopped?</param> protected override void AbilityStopped(bool force) { base.AbilityStopped(force); m_MoveTowardsLocation = null; if (force) { m_OnArriveAbility = null; } if (m_ForceStartEvent != null) { Scheduler.Cancel(m_ForceStartEvent); m_ForceStartEvent = null; } if (m_DisableGameplayInput) { EventHandler.ExecuteEvent(m_GameObject, "OnEnableGameplayInput", true); } // Reset the force independet look parameter set within StartAbility. EventHandler.ExecuteEvent(m_GameObject, "OnCharacterForceIndependentLook", false); // Start the OnArriveAbility after MoveTowards has stopped to prevent MoveTowards from affecting the arrive ability. if (m_OnArriveAbility != null) { m_CharacterLocomotion.TryStartAbility(m_OnArriveAbility, true, true); m_OnArriveAbility = null; } }
/// <summary> /// Initialize a new MoveTowardsLocation. /// </summary> private void InitializeMoveTowardsLocation() { if (m_IndependentMoveTowardsLocation != null) { return; } m_IndependentMoveTowardsLocation = new GameObject("Move Towards Location").AddComponent <MoveTowardsLocation>(); m_IndependentMoveTowardsLocation.Offset = Vector3.zero; m_IndependentMoveTowardsLocation.YawOffset = 0; m_IndependentMoveTowardsLocation.PrecisionStart = false; m_IndependentMoveTowardsLocation.Distance = 1; }
/// <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(MoveTowardsLocation[] 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_OnArriveAbility = onArriveAbility; 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."); } m_MoveTowardsLocation = GetClosestStartLocation(startLocations); 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); }
/// <summary> /// The ability has started. /// </summary> protected override void AbilityStarted() { if (m_OnArriveAbility != null) { m_AllowEquippedSlotsMask = m_OnArriveAbility.AllowEquippedSlotsMask; m_OnArriveAbility.AbilityMessageCanStart = false; } base.AbilityStarted(); m_Arrived = false; if (m_DisableGameplayInput) { EventHandler.ExecuteEvent(m_GameObject, "OnEnableGameplayInput", false); } // The MoveTowardsLocation may already be set by the starting ability within StartMoving. if (m_MoveTowardsLocation == null) { m_MoveTowardsLocation = m_IndependentMoveTowardsLocation; } m_StartMoveTowardsPosition = m_MoveTowardsLocation.TargetPosition; // The movement speed will depend on the current speed the character is moving. m_MovementMultiplier = m_MoveTowardsLocation.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; } } } // Use the pathfinding ability if the destination is a valid pathfinding destination. if (m_PathfindingMovement != null && m_PathfindingMovement.Index < Index) { m_PathfindingMovement.SetDestination(m_MoveTowardsLocation.TargetPosition); } // Force independent look so the ability will have complete control over the rotation. EventHandler.ExecuteEvent(m_GameObject, "OnCharacterForceIndependentLook", true); }
private static void DrawMoveTowardsLocationGizmo(MoveTowardsLocation 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 MoveTowardsLocations. /// </summary> /// <param name="startLocations">The locations the character can move towards.</param> /// <returns>The best location out of the possible MoveTowardsLocations.</returns> private MoveTowardsLocation GetClosestStartLocation(MoveTowardsLocation[] 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. MoveTowardsLocation 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); }