Ejemplo n.º 1
0
        /// <summary>
        /// Links two GraphNode objects as Neighbour. Does the slightly expensive task of calculating actual
        /// A* path between the two, and caches that value as the cost between the two.
        /// </summary>
        /// <param name="a">A node to link to <paramref name="b"/>.</param>
        /// <param name="b">A node to link to <paramref name="a"/>.</param>
        /// <param name="cluster">The cluster containing both nodes.</param>
        private void LinkGraphNodes(GraphNode a, GraphNode b, Cluster cluster, Boolean oneWay = false)
        {
            mPlanner.Reset();
            mPlanner.SetSource((a.pData as Level.Tile).mGraphNode);
            mPlanner.SetDestination((b.pData as Level.Tile).mGraphNode);

            // Do a standard A* search with the adde constraint of staying within the
            // bounds of this cluster.
            Planner.Result result = mPlanner.PlanPath(cluster.pBounds, false);

            // Keep searching unti we either fail or succeed.
            while (result == Planner.Result.InProgress)
            {
                result = mPlanner.PlanPath(cluster.pBounds, false);
            }

            // Only connect the nodes if they can be reached from one another within the same cluster.
            if (result == Planner.Result.Solved)
            {
                PathNode path = mPlanner.pCurrentBest;

                // Link the two neightbours.
                a.AddNeighbour(b, path.pFinalCost);
                if (!oneWay)
                {
                    b.AddNeighbour(a, path.pFinalCost);
                }
            }

            mPlanner.ClearDestination();

            //if (a != null)
            //{
            //DebugWalkGraphForErrors(a as NavMeshTileGraphNode);
            //}

            //DebugCheckNode(a);
            //DebugCheckNode(b);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Called once per frame by the game object.
        /// </summary>
        /// <param name="gameTime">The amount of time that has passed this frame.</param>
        public override void Update(GameTime gameTime)
        {
            // Does this instance of the behaviour just want to automatically update the source
            // based on our parents position?
            if (mUpdateSourceAutomatically)
            {
                SetSource(mParentGOH.pPosition);
            }

            if (mGetNavMeshMsg.mNavMesh_Out != null)
            {
                //mGetNavMeshMsg.mNavMesh_Out.DebugCheckNodes();
            }

            // Plan the path at a high level.
            MBHEngine.PathFind.GenericAStar.Planner.Result res = mPlannerNavMesh.PlanPath();

            // If the search is anything but InProgress it is safe to research the pass counter.
            if (res == Planner.Result.InProgress)
            {
                mSearchPassCount++;
            }
            else
            {
                mSearchPassCount = 0;
            }

            // If the planner failed to find the destination tell the other behaviours.
            if (res == MBHEngine.PathFind.GenericAStar.Planner.Result.Failed ||
                res == Planner.Result.InvalidLocation ||
                mSearchPassCount > mSearchPassLimit)
            {
                if (res == Planner.Result.Failed)
                {
                    mOnPathFindFailedMsg.mReason = OnPathFindFailedMessage.Reason.Failed;
                }
                else if (res == Planner.Result.InvalidLocation)
                {
                    mOnPathFindFailedMsg.mReason = OnPathFindFailedMessage.Reason.InvalidLocation;
                }
                else if (mSearchPassCount > mSearchPassLimit)
                {
                    mOnPathFindFailedMsg.mReason = OnPathFindFailedMessage.Reason.Timeout;
                }

                mParentGOH.OnMessage(mOnPathFindFailedMsg);

                mSearchPassCount = 0;
            }
            else if (res == Planner.Result.Solved)// && InputManager.pInstance.CheckAction(InputManager.InputActions.B))
            {
                // When the high level path finding is solved, start path finding at a lower
                // level betwen PathNodes within the higher level.
                //

                PathNode node = mPlannerNavMesh.pCurrentBest;

                // We need to do more than just walk the list until we hit the mLastHighLevelSearched. There
                // are cases (such as 2 nodes at the same position) where we don't want to use a Node.
                // This tracks which was the last VALID node.
                PathNode lastValid = node;

                // Loop all the way back to one after the starting point avoiding the starting node, as
                // well as previously searched nodes.
                // We don't want the starting node because that is where we are likely already standing.
                while (
                    node != mLastHighLevelSearched &&                                          // Was the first node actually the one we looked at last Update?
                    node.pPrevious != mLastHighLevelSearched &&                                // Is the next one the Node looked at last Update?
                    node.pPrevious.pGraphNode.pPosition != mPlannerTileMap.pStart.pPosition && // Is this Node at the same position as the starting position? We are already there so trying to get there again would cause issues.
                    node.pPrevious.pPrevious != null)
                {
                    node = node.pPrevious;

                    // Only choose nodes that are not at the same position as the last valid node. Trying to path find between
                    // 2 nodes at the TileMap level causes problems. It will solve the path fine, but the issue is when it goes
                    // back to the HPA level, since it hasn't moved the starting position, HPA will find the exact same path.
                    // This cycle repeats forever.
                    if (lastValid.pGraphNode.pPosition != node.pGraphNode.pPosition)
                    {
                        lastValid = node;
                    }
                }

                node = lastValid;

                // The lower level search uses the Level.Tile Graph, not the NavMesh, so we need to
                // use the node in NavMesh to find a node in the main tile map.
                mGetTileAtPositionMsg.Reset();
                mGetTileAtPositionMsg.mPosition_In = node.pGraphNode.pPosition;
                WorldManager.pInstance.pCurrentLevel.OnMessage(mGetTileAtPositionMsg);

                // If this is the first time we are searching at the low level (for this particular
                // search) we want to SET the destination. If that isn't the case, we want to extend
                // the search to continue to a new destination.
                if (null == mLowLevelBest)
                {
                    mPlannerTileMap.SetDestination(mGetTileAtPositionMsg.mTile_Out.mGraphNode);
                }
                else
                {
                    mPlannerTileMap.ExtendDestination(mGetTileAtPositionMsg.mTile_Out.mGraphNode);
                }

                // Start/Continue planning the path.
                Planner.Result tileRes = mPlannerTileMap.PlanPath();

                // Once the path has been solved, store out that this node in the high level search has
                // been completed, so the next time through this function, the next node in the path w
                // will be the target.
                if (tileRes == Planner.Result.Solved)
                {
                    mLowLevelBest = mPlannerTileMap.pCurrentBest;

                    mLastHighLevelSearched = node;
                }
            }
        }