Exemplo n.º 1
0
        /// <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;
        }
Exemplo n.º 2
0
        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;
        }
Exemplo n.º 3
0
        /// <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);
                }
            }
        }