/// <summary>
        /// See parent.
        /// </summary>
        /// <param name="msg"></param>
        public override void OnMessage(ref MBHEngine.Behaviour.BehaviourMessage msg)
        {
            base.OnMessage(ref msg);

            if (msg is HitCountDisplay.TrialScoreLimitReachedMessage)
            {
                if (GetCurrentState() is StateEmpty)
                {
                    AdvanceToState("StateTrialModeLimitRoot");
                }
            }
        }
        /// <summary>
        /// Perform the path finding. Call repeatedly to continue to try and find the path over a number
        /// of frames.
        /// </summary>
        /// <param name="restrictedArea">Restrict choosen nodes to this area.</param>
        /// <returns>The result of the path finding for this frame.</returns>
        public Result PlanPath(MBHEngine.Math.Rectangle restrictedArea, Boolean drawDebug)
        {
            // If there is no tile at the destination then there is no path finding to do.
            if (mEnd == null)
            {
                return Result.NotStarted;
            }

            if (drawDebug)
            {
                // If we have a destination draw it. Even if there isn't a source yet.
                DebugShapeDisplay.pInstance.AddPoint(mEnd.pPosition, 2.0f, Color.Yellow);
            }

            // If the destination is a solid tile then we will never be able to solve the path.
            if (null != mEnd && !mEnd.IsEmpty())
            {
                // We consider this a failure, similar to if a destination was surrounded by solid.
                return Result.InvalidLocation;
            }

            // If our source position is not on a tile then there is no path finding to do.
            if (mStart == null)
            {
                return Result.NotStarted;
            }

            // If our source position is not on a tile, or that tile is solid we cannot ever solve
            // this path, so abort right away.
            if (mStart != null && !mStart.IsEmpty())
            {
                // Trying to path find to a solid tile is considered a failure.
                return Result.InvalidLocation;
            }

            if (drawDebug)
            {
                // If there is a source, draw it.
                DebugShapeDisplay.pInstance.AddPoint(mStart.pPosition, 2.0f, Color.Orange);
            }

            // If the path hasn't already been invalidated this frame, we need to check that
            // the path didn't get blocked from something like the Player placing blocks.
            // TODO: This could be changed to only do this check when receiving specific events,
            //       such as the ObjectPlacement Behaviour telling it that a new block has been
            //       placed.
            if (!mPathInvalidated)
            {
                // Loop through the current path and check for any tiles that are not
                // empty. If they aren't empty this path is no longer valid as there is 
                // something now blocking it.
                //
                PathNode node = mBestPathEnd;

                while (null != node)
                {
                    if (!node.pGraphNode.IsEmpty())
                    {
                        // Setting this flag will force the path finder to start from 
                        // the begining.
                        mPathInvalidated = true;

                        // No need to loop any further. One blockade is enough.
                        break;
                    }

                    node = node.pPrevious;
                }
            }

            // If the path has become invalid, we need to restart the pathing algorithm.
            if (mPathInvalidated)
            {
                ClearNodeLists();

                // First thing we need to do is add the first node to the open list.
                PathNode p = mUnusedNodes.Pop();
                p.pGraphNode = mStart;

                // There is no cost because it is the starting node.
                p.pCostFromStart = 0;

                // For H we use the actual distance to the destination.  The Manhattan Heuristic method.
                /*
                p.pCostToEnd = System.Math.Max(System.Math.Abs(
                        p.pGraphNode.pPosition.X - mEnd.pPosition.X),
                        System.Math.Abs(p.pGraphNode.pPosition.Y - mEnd.pPosition.Y));
                */

                Vector2 source = p.pGraphNode.pPosition;

                Single h_diagonal = System.Math.Min(System.Math.Abs(source.X - mEnd.pPosition.X), System.Math.Abs(source.Y - mEnd.pPosition.Y));
                Single h_straight = System.Math.Abs(source.X - mEnd.pPosition.X) + System.Math.Abs(source.Y - mEnd.pPosition.Y);
                p.pCostToEnd = (11.314f) * h_diagonal + 8.0f * (h_straight - 2 * h_diagonal);

                // Add it to the list, and start the search!
                mOpenNodes.Add(p);

                // If the path was invalidated that assume that it is not longer solved.
                mSolved = false;

                // The path is no longer invalid.  It has begun.
                mPathInvalidated = false;
            }

            // Track how many times this planner has looped this time.
            Int32 count = 0;

            const Int32 maxLoops = 30;

            // Loop until all possibilities have been exhusted, the time slice is expired or the 
            // path is solved.
            while (mOpenNodes.Count > 0 && count < maxLoops && !mSolved)// && (!drawDebug || Input.InputManager.pInstance.CheckAction(Input.InputManager.InputActions.B, true)))
            {
                count++;

                mBestPathEnd = mOpenNodes[0];

                //HPAStar.NavMesh.DebugCheckNode(mBestPathEnd.pGraphNode);

                for (Int32 i = 0; i < mOpenNodes.Count; i++)
                {
                    if (mOpenNodes[i].pFinalCost <= mBestPathEnd.pFinalCost)
                    {
                        mBestPathEnd = mOpenNodes[i];
                    }
                }

                mOpenNodes.Remove(mBestPathEnd);
                mClosedNodes.Add(mBestPathEnd);                
                
                // End the search once the destination node is added to the closed list.
                //
                if (mBestPathEnd.pGraphNode == mEnd)
                {
                    OnPathSolved(drawDebug);

                    mSolved = true;

                    break;
                }

                for (Int32 i = 0; i < mBestPathEnd.pGraphNode.pNeighbours.Count; i++)
                {
                    GraphNode.Neighbour nextNode = mBestPathEnd.pGraphNode.pNeighbours[i];

                    if (nextNode.mGraphNode.IsPassable(mBestPathEnd.pGraphNode) && (restrictedArea == null || restrictedArea.Intersects(nextNode.mGraphNode.pPosition)))
                    {
                        Boolean found = false;
                        for (Int32 j = 0; j < mClosedNodes.Count; j++)
                        {
                            if (mClosedNodes[j].pGraphNode == nextNode.mGraphNode)
                            {
                                found = true;
                                break;
                            }
                        }

                        // This node is already in the closed list, so move on to the next node.
                        if (found)
                        {
                            continue;
                        }

                        // 3-b. If it isn’t on the open list, add it to the open list. 
                        // Make the current square the parent of this square. Record the F, G, and H costs of the square. 

                        // TODO: Get a better way to know if something is in the opened list already.
                        //
                        PathNode foundNode = null;

                        for (Int32 j = 0; j < mOpenNodes.Count; j++)
                        {
                            if (mOpenNodes[j].pGraphNode == nextNode.mGraphNode)
                            {
                                foundNode = mOpenNodes[j];
                                break;
                            }
                        }

                        // Calculate the cost of moving to this node.  This is the distance between the two nodes.
                        // This will be needed in both the case where the node is in the open list already,
                        // and the case where it is not.
                        // The cost is the cost of the previous node plus the distance cost to this node.
                        Single costFromCurrentBest = mBestPathEnd.pCostFromStart + nextNode.mCostToTravel;

                        // If the node was not found it needs to be added to the open list.
                        if (foundNode == null)
                        {
                            // Create a new node and add it to the open list so it can been considered for pathing
                            // in the updates to follow.
                            PathNode p = mUnusedNodes.Pop(); 
                            p.pGraphNode = nextNode.mGraphNode;

                            // For now it points back to the current node.  This can be overwritten if another node
                            // leads here with a lower cost (see else statement below).
                            p.pPrevious = mBestPathEnd;

                            // The cost to get to this node (G) is calculated above.
                            p.pCostFromStart = costFromCurrentBest;

                            // Combo
                            
                            Vector2 source = p.pGraphNode.pPosition;

                            Single h_diagonal = System.Math.Min(System.Math.Abs(source.X - mEnd.pPosition.X), System.Math.Abs(source.Y - mEnd.pPosition.Y));
                            Single h_straight = System.Math.Abs(source.X - mEnd.pPosition.X) + System.Math.Abs(source.Y - mEnd.pPosition.Y);
                            p.pCostToEnd = (11.314f) * h_diagonal + 8.0f * (h_straight - 2 * h_diagonal);
                            //p.mCostToEnd *= (10.0f + (1.0f/1000.0f));
                            

                            /*
                            p.pCostToEnd = System.Math.Max(System.Math.Abs(
                                                    p.pGraphNode.pPosition.X - mEnd.pPosition.X),
                                                    System.Math.Abs(p.pGraphNode.pPosition.Y - mEnd.pPosition.Y));
                            */
                            mOpenNodes.Add(p);

                            // Ending the search now will alomost always result in the best path
                            // but it is possible for it to fail.
                            if (p.pGraphNode == mEnd)
                            {
                                // Since the path is now solved, update mCurBest so that it is used for
                                // tracing back through the path from now on.
                                mBestPathEnd = p;
                                
                                OnPathSolved(drawDebug);
                                
                                break;
                            }
                        }
                        else
                        {
                            // If it is on the open list already, check to see if this path to that square is better, 
                            // using G cost as the measure. A lower G cost means that this is a better path. If so, 
                            // change the parent of the square to the current square, and recalculate the G and F 
                            // scores of the square. If you are keeping your open list sorted by F score, you may need
                            // to resort the list to account for the change.
                            if (foundNode.pCostFromStart > costFromCurrentBest)
                            {
                                foundNode.pPrevious = mBestPathEnd;
                                foundNode.pCostFromStart = costFromCurrentBest;
                            }
                        }
                    }
                }
            }

            // Draw the path.
            if (drawDebug && mBestPathEnd != null)
            {
                DebugDraw();
            }

            if (mSolved)
            {
                return Result.Solved;
            }
            else if (mOpenNodes.Count > 0)
            {
                return Result.InProgress;
            }
            else
            {
                return Result.Failed;
            }
        }
Exemple #3
0
        /// <summary>
        /// Does a collision check between every tile in the level and a specified rectangle.  Requires
        /// both the starting position and the desired position, so that it can figure out details about
        /// how it collided with different tiles.
        /// </summary>
        /// <param name="startRect">The location of this collision rect at the start of this movement.</param>
        /// <param name="endRect">The rectangle representing the final position that it is trying to get to.</param>
        /// <param name="collideX">Did this check yield a collision in the X direction?</param>
        /// <param name="collideY">Did this check yield a collision in the Y direction?</param>
        /// <param name="collidePointX">Where did the X collision happen (if it did)?</param>
        /// <param name="collidePointY">Where did the Y collision happen (if it did)?</param>
        /// <returns></returns>
        private Boolean CheckForCollision(
            MBHEngine.Math.Rectangle startRect, 
            MBHEngine.Math.Rectangle endRect, 
            out Boolean collideX, 
            out Boolean collideY,
            out Single collidePointX,
            out Single collidePointY)
        {
            // Which direction is this thing heading?
            // Needed to determine which walls to check against (eg. no need to check left wall if we are moving left).
            Vector2 dir = endRect.pCenterPoint - startRect.pCenterPoint;

            // Start by assuming no collisions occured.
            Boolean hit = false;
            collideX = false;
            collideY = false;
            collidePointX = collidePointY = 0;

            // We don't want to check against every tile in the world.  Instead attempt to narrow it down
            // based on the fact that the tiles can be mapped from their x,y position to their index
            // into the array.
            Int32 checkRange = 5;
            Single x2 = (endRect.pCenterPoint.X / mMapInfo.mTileWidth) - ((Single)checkRange * 0.5f);
            Int32 startX = System.Math.Max((Int32)System.Math.Round(x2), 0);

            Single y2 = (endRect.pCenterPoint.Y / mMapInfo.mTileHeight) - ((Single)checkRange * 0.5f);
            Int32 startY = System.Math.Max((Int32)System.Math.Round(y2), 0);

            // Loop through every time checking for collisions.
            for (Int32 y = startY; y < mMapInfo.mMapHeight && y < startY + checkRange; y++)
            {
                for (Int32 x = startX; x < mMapInfo.mMapWidth && x < startX + checkRange; x++)
                {

                    // Is this tile solid and does it have any active walls?
                    // It may be solid with no walls in the case of one completly surrounded.
                    if (mCollisionGrid[x, y].mType != Level.Tile.TileTypes.Empty && mCollisionGrid[x, y].mActiveWalls != Tile.WallTypes.None)
                    {
                        // This tile has been considered for a collision.  It will be changed to type 2 if there is a
                        // collision.
                        mCollisionGrid[x, y].SetAttribute(Tile.Attribute.CollisionChecked);

                        // Calculate the center point of the tile.
                        Vector2 cent = new Vector2((x * mMapInfo.mTileWidth) + (mMapInfo.mTileWidth * 0.5f), (y * mMapInfo.mTileHeight) + (mMapInfo.mTileHeight * 0.5f));

                        // Create a rectangle to represent the tile.
                        //MBHEngine.Math.Rectangle tileRect = new MBHEngine.Math.Rectangle(mTileWidth, mTileHeight, cent);

                        // Does the place we are trying to move to intersect with the tile? 
                        if (mCollisionGrid[x, y].mCollisionRect.Intersects(endRect))
                        {
                            // If we are moving right, and we hit a tile with a left wall...
                            if (dir.X > 0 &&
                                (mCollisionGrid[x, y].mActiveWalls & Tile.WallTypes.Left) != Tile.WallTypes.None)
                            {
                                // Create a line from the center of our destination...
                                mCollisionRectMovement.pPointA = mCollisionGrid[x, y].mCollisionRect.pCenterPoint;
                                mCollisionRectMovement.pPointB = endRect.pCenterPoint;

                                // ...and a line representing the wall we are testing against.
                                mCollisionGrid[x, y].mCollisionRect.GetLeftEdge(ref mCollisionWall);

                                // If those two lines intersect, then we count this collision.
                                // We do this line check to avoid colliding with both top/bottom and
                                // left/right in the same movement.  That causes issues like getting stuck in
                                // tight corridors.
                                Vector2 intersect = new Vector2();
                                if (mCollisionWall.Intersects(mCollisionRectMovement, ref intersect))
                                {
                                    DebugShapeDisplay.pInstance.AddSegment(mCollisionRectMovement, Color.DarkRed);
                                    DebugShapeDisplay.pInstance.AddPoint(intersect, 1, Color.Orange);
                                    // We have collide along the x axis.
                                    collideX = true;
                                    collidePointX = x * mMapInfo.mTileWidth;
                                }
                            }
                            // If we are moving left, and we hit a tile with a right wall...
                            else if (dir.X < 0 &&
                                (mCollisionGrid[x, y].mActiveWalls & Tile.WallTypes.Right) != Tile.WallTypes.None)
                            {
                                mCollisionRectMovement.pPointA = mCollisionGrid[x, y].mCollisionRect.pCenterPoint;
                                mCollisionRectMovement.pPointB = endRect.pCenterPoint;

                                mCollisionGrid[x, y].mCollisionRect.GetRightEdge(ref mCollisionWall);

                                Vector2 intersect = new Vector2();

                                if (mCollisionWall.Intersects(mCollisionRectMovement, ref intersect))
                                {
                                    DebugShapeDisplay.pInstance.AddSegment(mCollisionRectMovement, Color.DarkRed);
                                    DebugShapeDisplay.pInstance.AddPoint(intersect, 1, Color.Orange);
                                    // We have collide along the x axis.
                                    collideX = true;
                                    collidePointX = x * mMapInfo.mTileWidth + mMapInfo.mTileWidth;
                                }
                            }
                            if (dir.Y > 0 &&
                                (mCollisionGrid[x, y].mActiveWalls & Tile.WallTypes.Top) != Tile.WallTypes.None)
                            {
                                mCollisionRectMovement.pPointA = mCollisionGrid[x, y].mCollisionRect.pCenterPoint;
                                mCollisionRectMovement.pPointB = endRect.pCenterPoint;

                                mCollisionGrid[x, y].mCollisionRect.GetTopEdge(ref mCollisionWall);

                                Vector2 intersect = new Vector2();

                                if (mCollisionWall.Intersects(mCollisionRectMovement, ref intersect))
                                {
                                    DebugShapeDisplay.pInstance.AddSegment(mCollisionRectMovement, Color.DarkRed);
                                    DebugShapeDisplay.pInstance.AddPoint(intersect, 1, Color.Orange);
                                    // We have collide along the y axis.
                                    collideY = true;
                                    collidePointY = y * mMapInfo.mTileHeight;
                                }
                            }
                            else if (dir.Y < 0 &&
                                (mCollisionGrid[x, y].mActiveWalls & Tile.WallTypes.Bottom) != Tile.WallTypes.None)
                            {
                                mCollisionRectMovement.pPointA = mCollisionGrid[x, y].mCollisionRect.pCenterPoint;
                                mCollisionRectMovement.pPointB = endRect.pCenterPoint;

                                mCollisionGrid[x, y].mCollisionRect.GetBottomEdge(ref mCollisionWall);

                                Vector2 intersect = new Vector2();

                                if (mCollisionWall.Intersects(mCollisionRectMovement, ref intersect))
                                {
                                    DebugShapeDisplay.pInstance.AddSegment(mCollisionRectMovement, Color.DarkRed);
                                    DebugShapeDisplay.pInstance.AddPoint(intersect, 1, Color.Orange);
                                    // We have collide along the y axis.
                                    collideY = true;
                                    collidePointY = y * mMapInfo.mTileHeight + mMapInfo.mTileHeight;
                                }
                            }

                            //DebugMessageDisplay.pInstance.AddDynamicMessage("Collide Dir: " + dir);

                            // Set the collision type temporarily to 2, to signal that it collided.
                            mCollisionGrid[x, y].SetAttribute(Tile.Attribute.Collision);

                            // If we make it to this point there was a collision of some type.
                            hit = true;
                        }
                    }
                }
            }

            return hit;
        }
 /// <summary>
 /// Checks if a Rectangle is on screen at all.
 /// </summary>
 /// <param name="rect">The rectangle to check.</param>
 /// <returns></returns>
 public Boolean IsOnCamera(MBHEngine.Math.Rectangle rect)
 {
     return mViewRectangle.Intersects(rect);
 }