/// <summary> /// Unity's Start method. Initializes the NavTileAgent. /// </summary> private void Start() { OnAreaChangeUnityEvent.AddListener(ChangeAnimationOnArea); OnPathAbortedUnityEvent.AddListener(OnPathAbortSetAnimationParameters); // Initialize Conflict handling varaibles when enabled. if (_conflictOption != EConflictOptions.Disabled) { if (!NavTileManager.Instance.SurfaceManager.IsDataInitialized || NavTileManager.Instance.SurfaceManager.Data.HasNoTiles || NavTileManager.Instance.SurfaceManager.Grid == null) { Debug.LogWarning("Data is not initialized for this scene. Please bake in the navigation 2D window."); return; } Vector2Int currentTilePosition = NavTileManager.Instance.SurfaceManager.Grid.WorldToCell(transform.position).GetVector2Int(); NavTileManager.Instance.SurfaceManager.Data.GetTileData(currentTilePosition).OccupyingAgents.Add(this); _occupyingTilePosition = currentTilePosition; } _pathStatus = EPathStatus.NotAvailable; _movementStatus = EMovementStatus.Stationary; }
public void CancelPath() { if (_followingPathCoroutine != null) { StopCoroutine(_followingPathCoroutine); _pathStatus = EPathStatus.NotAvailable; _movementStatus = EMovementStatus.Stationary; UpdateTileOccupancy(_occupyingTilePosition, NavTileManager.Instance.SurfaceManager.Grid.WorldToCell(transform.position).GetVector2Int()); } _path = null; }
/// <summary> /// Coroutine for following a given path. /// </summary> /// <param name="inPath">The path that should be followed.</param> private IEnumerator FollowPath(NavTilePath inPath) { if (inPath == null) { yield break; } // Prepare path traversal. _currentPathNodeIndex = 0; PathNode currentNode = new PathNode(transform.position); PathNode nextNode = inPath[_currentPathNodeIndex]; _pathStatus = EPathStatus.Traversing; _movementStatus = EMovementStatus.Traversing; float timeInPath = 0; float timeInCurrentNode = 0; float currentSpeed = GetAreaSpeed(nextNode.GetAreaIndex()); OnAreaChangeUnityEvent.Invoke(nextNode.GetAreaIndex()); float timeToNextNode = inPath.GetDurationBetweenNodes(currentNode, nextNode, currentSpeed); // Initial animation triggers. SetAnimationParameters(nextNode.WorldPosition - currentNode.WorldPosition, currentSpeed); // Do conflict handling if enabled if (_conflictOption != EConflictOptions.Disabled) { // Check if there is a conflict before starting to traverse. EConflictStatus conflictStatus; EAbortReason abortReason; do { conflictStatus = HandleConflict(currentNode, nextNode, out abortReason); // In case the conflict is not completely resolved, yield the coroutine and check again next frame. if (conflictStatus == EConflictStatus.Processing) { _movementStatus = EMovementStatus.Waiting; yield return(null); } else if (conflictStatus == EConflictStatus.Abort) // In case the conflict cannot be resolved, abort path traversing. { _pathStatus = EPathStatus.NotAvailable; _movementStatus = EMovementStatus.Stationary; OnPathAbortedUnityEvent.Invoke(abortReason, nextNode.TilePosition); yield break; } } while (conflictStatus == EConflictStatus.Processing); // If this is false, the 'do-while' will stop and thus a conflict has not been encountered. } // Start traversing. while (true) { // Check if the agent will reach the next node in this frame or not. // If so, start walking towards the subsequent node. if (timeInCurrentNode + Time.deltaTime >= timeToNextNode) { // Switch to next node. _currentPathNodeIndex++; // Goal reached? if (_currentPathNodeIndex == inPath.Count) { transform.position = nextNode.WorldPosition; _pathStatus = EPathStatus.NotAvailable; _movementStatus = EMovementStatus.Stationary; // Reset animation parameters. ResetAnimationParameters(); OnPathFinishedUnityEvent.Invoke(); yield break; } currentNode = nextNode; nextNode = inPath[_currentPathNodeIndex]; // The 'previous' position is the tile which the agent is walking from. _previousPosition = currentNode.TilePosition; // Check if next tile is still walkable. if (!NavTileManager.Instance.SurfaceManager.Data.IsTileWalkable(nextNode.TilePosition, AreaMask)) { OnPathAbortedUnityEvent.Invoke(EAbortReason.EncounteredObstruction, nextNode.TilePosition); yield break; } // Check for conflicts if enabled. if (_conflictOption != EConflictOptions.Disabled) { // Check if there is a conflict on the next node. EConflictStatus conflictStatus; EAbortReason abortReason; do { conflictStatus = HandleConflict(currentNode, nextNode, out abortReason); // In case the conflict is not completely resolved, yield the coroutine and check again next frame. if (conflictStatus == EConflictStatus.Processing) { _movementStatus = EMovementStatus.Waiting; yield return(null); } else if (conflictStatus == EConflictStatus.Abort) // In case the conflict cannot be resolved, abort path traversing. { _pathStatus = EPathStatus.NotAvailable; _movementStatus = EMovementStatus.Stationary; OnPathAbortedUnityEvent.Invoke(abortReason, nextNode.TilePosition); yield break; } } while (conflictStatus == EConflictStatus.Processing); // If this is false, the 'do-while' will stop and thus a conflict has not been encountered. // Set movementstatus just in case it has been changed due to a conflict. _movementStatus = EMovementStatus.Traversing; } // Check whether the agent has changed areas. int nextAreaIndex = nextNode.GetAreaIndex(); if (currentNode.GetAreaIndex() != nextAreaIndex) { currentSpeed = GetAreaSpeed(nextAreaIndex); OnAreaChangeUnityEvent.Invoke(nextAreaIndex); } // Calculate the time which might be left over from the previous node to already move in towards the subsequent node. // This prevents the agent to first exactly stop on a tile before starting to move again, creating jittering. timeInCurrentNode += Time.deltaTime - timeToNextNode; timeToNextNode = inPath.GetDurationBetweenNodes(currentNode, nextNode, currentSpeed); transform.position = Vector3.Lerp(currentNode.WorldPosition, nextNode.WorldPosition, timeInCurrentNode / timeToNextNode); // Set animation parameters SetAnimationParameters(nextNode.WorldPosition - currentNode.WorldPosition, currentSpeed); yield return(null); } else // The next node has not been reached, so keep moving towards it. { timeInPath += Time.deltaTime; timeInCurrentNode += Time.deltaTime; transform.position = Vector3.Lerp(currentNode.WorldPosition, nextNode.WorldPosition, timeInCurrentNode / timeToNextNode); yield return(null); } } }