/// <summary>
        /// Determines whether an unobstructed path exists between two points.
        /// </summary>
        /// <param name="from">The from point.</param>
        /// <param name="to">The to point.</param>
        /// <param name="unitProps">The unit properties to test against (Walkability).</param>
        /// <param name="matrix">The matrix where both <paramref name="from"/> and <paramref name="to"/> must be part of.</param>
        /// <param name="costStrategy">The cost strategy.</param>
        /// <returns><c>true</c> if an unobstructed path exists between the two points; otherwise <c>false</c>.</returns>
        public static bool CanReducePath(IPositioned from, IPositioned to, IUnitProperties unitProps, CellMatrix matrix, ICellCostStrategy costStrategy)
        {
            //The reference cell is always the from cell. The fact the points are swapped to simplify tangents does not affect this.
            var refCell = matrix.GetCell(from.position, false);

            Vector3 p1;
            Vector3 p2;

            //Assign the points so we start with the point with the lowest x-value to simplify things
            if (from.position.x > to.position.x)
            {
                p1 = to.position;
                p2 = from.position;
            }
            else
            {
                p1 = from.position;
                p2 = to.position;
            }

            var unitRadius = unitProps.radius;
            var tan        = Tangents.Create(p1, p2, unitRadius, matrix);

            var slopeDir = tan.slopeDir;
            var cellSize = matrix.cellSize;
            var halfCell = cellSize * 0.5f;

            //Get the start and end cells, get the cost of the actual start and end, and then reassign the start and end to accommodate for unit size.
            var startCell = matrix.GetCell(p1, false);
            var startCost = costStrategy.GetCellCost(startCell, unitProps);

            startCell = matrix.GetCell(new Vector3(p1.x - unitRadius, p1.y, p1.z), true);

            var endCell = matrix.GetCell(p2, false);
            var endCost = costStrategy.GetCellCost(endCell, unitProps);

            endCell = matrix.GetCell(new Vector3(p2.x + unitRadius, p2.y, p2.z), true);

            //The movement across the x-axis
            float minXCoord = (startCell.position.x - matrix.start.x) - halfCell;
            float maxXCoord = (startCell.position.x - matrix.start.x) + halfCell;

            int minZ;
            int maxZ;

            if (slopeDir < 0)
            {
                var distLowerCellBoundary = halfCell - (endCell.position.z - p2.z);
                var minOverlap            = Mathf.CeilToInt((unitRadius - distLowerCellBoundary) / cellSize);
                minZ = Math.Max(0, endCell.matrixPosZ - Math.Max(0, minOverlap));

                var distUpperCellBoundary = halfCell - (p1.z - startCell.position.z);
                var maxOverlap            = Mathf.CeilToInt((unitRadius - distUpperCellBoundary) / cellSize);
                maxZ = Math.Min(matrix.rows - 1, startCell.matrixPosZ + Math.Max(0, maxOverlap));
            }
            else
            {
                var distLowerCellBoundary = halfCell - (startCell.position.z - p1.z);
                var minOverlap            = Mathf.CeilToInt((unitRadius - distLowerCellBoundary) / cellSize);
                minZ = Math.Max(0, startCell.matrixPosZ - Math.Max(0, minOverlap));

                var distUpperCellBoundary = halfCell - (p2.z - endCell.position.z);
                var maxOverlap            = Mathf.CeilToInt((unitRadius - distUpperCellBoundary) / cellSize);
                maxZ = Math.Min(matrix.rows - 1, endCell.matrixPosZ + Math.Max(0, maxOverlap));
            }

            int  startX     = startCell.matrixPosX;
            int  endX       = endCell.matrixPosX;
            bool isVertical = tan.isVertical;
            var  cellMatrix = matrix.rawMatrix;

            for (int x = startX; x <= endX; x++)
            {
                int startZ;
                int endZ;

                if (isVertical)
                {
                    startZ = Math.Min(startCell.matrixPosZ, endCell.matrixPosZ);
                    endZ   = Math.Max(startCell.matrixPosZ, endCell.matrixPosZ);
                }
                else
                {
                    if (slopeDir < 0)
                    {
                        startZ = Math.Max((int)(tan.LowTangent(maxXCoord) / cellSize), minZ);
                        endZ   = Math.Min((int)(tan.HighTangent(minXCoord) / cellSize), maxZ);
                    }
                    else
                    {
                        startZ = Math.Max((int)(tan.LowTangent(minXCoord) / cellSize), minZ);
                        endZ   = Math.Min((int)(tan.HighTangent(maxXCoord) / cellSize), maxZ);
                    }
                }

                for (int z = startZ; z <= endZ; z++)
                {
                    var intermediary = cellMatrix[x, z];
                    if (!isVertical && tan.IsOutsideSecants(intermediary.position))
                    {
                        continue;
                    }

                    var intermediaryCost = costStrategy.GetCellCost(intermediary, unitProps);
                    if (!intermediary.IsWalkableFrom(refCell, unitProps) || (startCost < intermediaryCost || endCost < intermediaryCost))
                    {
                        return(false);
                    }
                }

                minXCoord = maxXCoord;
                maxXCoord = maxXCoord + cellSize;
            }

            return(true);
        }
示例#2
0
        public static bool CanReducePath(IPositioned point1, IPositioned point3, IUnitProperties unitProps, CellMatrix matrix, ICellCostStrategy costStrategy)
        {
            Vector3 p1;
            Vector3 p3;

            //Assign the points so we start with the point with the lowest x-value to simplify things
            if (point1.position.x > point3.position.x)
            {
                p1 = point3.position;
                p3 = point1.position;
            }
            else
            {
                p1 = point1.position;
                p3 = point3.position;
            }

            var requesterRadius = unitProps.radius;
            var tan = Tangents.Create(p1, p3, requesterRadius);

            var incZ = tan.slopeDir;
            var cellSize = matrix.cellSize;
            var halfCell = cellSize / 2.0f;

            //Adjust the start and end cells to possibly include their immediate neighbour if the unit's radius crossed said boundary.
            var radiusAdjust = new Vector3(requesterRadius, 0.0f, requesterRadius * incZ);

            //Get the start and end cells, get the cost of the actual start and end, and then reassign the start and end with the above adjustment.
            var startCell = matrix.GetCell(p1, true);
            var startCost = costStrategy.GetCellCost(startCell, unitProps);

            startCell = matrix.GetCell(p1 - radiusAdjust, true);

            var endCell = matrix.GetCell(p3, true);
            var endCost = costStrategy.GetCellCost(endCell, unitProps);

            endCell = matrix.GetCell(p3 + radiusAdjust, true);

            //We want x to end up on cell boundaries, the first of which is this far from the first points position
            var xAdj = p1.x + (startCell.position.x - p1.x) + halfCell;

            //We want to adjust z so that we correctly count impacted cells, this adjusts z so it starts at the bottom boundary of the first cell (for purposes of calculation)
            var zAdj = p1.z - (halfCell + ((p1.z - startCell.position.z) * incZ));

            //The movement across the x-axis
            float deltaX = 0.0f;

            var cellMatrix = matrix.rawMatrix;
            int indexX = 0;
            for (int x = startCell.matrixPosX; x <= endCell.matrixPosX; x++)
            {
                //So instead of just checking all cells in the bounding rect defined by the two cells p1 and p3,
                //we limit it to the cells immediately surrounding the proposed line (tangents), including enough cells that we ensure the unit will be able to pass through,
                //at the extreme routes between the two cells (i.e top corner to top corner and bottom corner to bottom corner
                int startZ;
                int endZ;

                //If the tangents are horizontal or vertical z range is obvious
                if (tan.isAxisAligned)
                {
                    startZ = startCell.matrixPosZ;
                    endZ = endCell.matrixPosZ + incZ;
                }
                else
                {
                    if (indexX == 0)
                    {
                        startZ = startCell.matrixPosZ;
                    }
                    else
                    {
                        var startCellsPassed = Mathf.FloorToInt((tan.LowTangent(deltaX) - zAdj) / cellSize) * incZ;

                        startZ = LimitStart(
                            startCell.matrixPosZ + startCellsPassed,
                            startCell.matrixPosZ,
                            incZ);
                    }

                    //The movement this step will perform across the x-axis
                    deltaX = xAdj + (indexX * cellSize);

                    var endCellsIntercepted = Mathf.FloorToInt((tan.HighTangent(deltaX) - zAdj) / cellSize) * incZ;

                    endZ = LimitEnd(
                        startCell.matrixPosZ + endCellsIntercepted,
                        endCell.matrixPosZ,
                        incZ) + incZ;
                }

                indexX++;

                for (int z = startZ; z != endZ; z += incZ)
                {
                    var intermediary = cellMatrix[x, z];
                    var intermediaryCost = costStrategy.GetCellCost(intermediary, unitProps);
                    if (!intermediary.isWalkableFrom(startCell, unitProps) || (startCost < intermediaryCost && endCost < intermediaryCost))
                    {
                        return false;
                    }
                }
            }

            return true;
        }
示例#3
0
        private static bool CanReducePath(IPositioned point1, IPositioned point3, IUnitProperties unitProps, CellMatrix matrix, ICellCostStrategy costStrategy)
        {
            Vector3 p1;
            Vector3 p3;

            //Assign the points so we start with the point with the lowest x-value to simplify things
            if (point1.position.x > point3.position.x)
            {
                p1 = point3.position;
                p3 = point1.position;
            }
            else
            {
                p1 = point1.position;
                p3 = point3.position;
            }

            var requesterRadius = unitProps.radius;
            var tan             = Tangents.Create(p1, p3, requesterRadius);

            var incZ     = tan.slopeDir;
            var cellSize = matrix.cellSize;
            var halfCell = cellSize / 2.0f;

            //Adjust the start and end cells to possibly include their immediate neighbour if the unit's radius crossed said boundary.
            var radiusAdjust = new Vector3(requesterRadius, 0.0f, requesterRadius * incZ);

            //Get the start and end cells, get the cost of the actual start and end, and then reassign the start and end with the above adjustment.
            var startCell = matrix.GetCell(p1, true);
            var startCost = costStrategy.GetCellCost(startCell, unitProps);

            startCell = matrix.GetCell(p1 - radiusAdjust, true);

            var endCell = matrix.GetCell(p3, true);
            var endCost = costStrategy.GetCellCost(endCell, unitProps);

            endCell = matrix.GetCell(p3 + radiusAdjust, true);

            //We want x to end up on cell boundaries, the first of which is this far from the first points position
            var xAdj = p1.x + (startCell.position.x - p1.x) + halfCell;

            //We want to adjust z so that we correctly count impacted cells, this adjusts z so it starts at the bottom boundary of the first cell (for purposes of calculation)
            var zAdj = p1.z - (halfCell + ((p1.z - startCell.position.z) * incZ));

            //The movement across the x-axis
            float deltaX = 0.0f;

            var cellMatrix = matrix.rawMatrix;
            int indexX     = 0;

            for (int x = startCell.matrixPosX; x <= endCell.matrixPosX; x++)
            {
                //So instead of just checking all cells in the bounding rect defined by the two cells p1 and p3,
                //we limit it to the cells immediately surrounding the proposed line (tangents), including enough cells that we ensure the unit will be able to pass through,
                //at the extreme routes between the two cells (i.e top corner to top corner and bottom corner to bottom corner
                int startZ;
                int endZ;

                //If the tangents are horizontal or vertical z range is obvious
                if (tan.isAxisAligned)
                {
                    startZ = startCell.matrixPosZ;
                    endZ   = endCell.matrixPosZ + incZ;
                }
                else
                {
                    if (indexX == 0)
                    {
                        startZ = startCell.matrixPosZ;
                    }
                    else
                    {
                        var startCellsPassed = Mathf.FloorToInt((tan.LowTangent(deltaX) - zAdj) / cellSize) * incZ;

                        startZ = LimitStart(
                            startCell.matrixPosZ + startCellsPassed,
                            startCell.matrixPosZ,
                            incZ);
                    }

                    //The movement this step will perform across the x-axis
                    deltaX = xAdj + (indexX * cellSize);

                    var endCellsIntercepted = Mathf.FloorToInt((tan.HighTangent(deltaX) - zAdj) / cellSize) * incZ;

                    endZ = LimitEnd(
                        startCell.matrixPosZ + endCellsIntercepted,
                        endCell.matrixPosZ,
                        incZ) + incZ;
                }

                indexX++;

                for (int z = startZ; z != endZ; z += incZ)
                {
                    var intermediary     = cellMatrix[x, z];
                    var intermediaryCost = costStrategy.GetCellCost(intermediary, unitProps);
                    if (!intermediary.isWalkableFrom(startCell, unitProps) || (startCost < intermediaryCost && endCost < intermediaryCost))
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }