예제 #1
0
        void TestWrappingEdgeEast(TestGrid grid)
        {
            int x;
            int y;

            for (int faceIndex = 0; faceIndex < 6; faceIndex++)
            {
                x = grid.RowCount - 1;
                for (y = 0; y < grid.RowCount; y++)
                {
                    var sourcePosition = new CartesianGridCoordinates {
                        x = (short)x, y = (short)y
                    };
                    var sourceCubeFace = new CartesianGridOnCubeFace {
                        Value = (byte)faceIndex
                    };
                    var sourceCellIndex = CartesianGridOnCubeUtility.CellIndex(sourcePosition, sourceCubeFace, grid.RowCount);
                    var exitPosition    = new CartesianGridCoordinates {
                        x = (short)(x + 1), y = (short)y
                    };
                    var edge = CartesianGridMovement.CubeExitEdge(exitPosition, grid.RowCount);
                    Assert.AreEqual(edge, 3);

                    var nextCellIndex    = CartesianGridOnCubeUtility.CellIndexEast(sourceCellIndex, grid.RowCount, grid.FaceLocalToLocal);
                    var nextDirection    = CartesianGridOnCubeUtility.NextFaceDirection[(edge * 6) + faceIndex];
                    var reverseDirection = CartesianGridMovement.ReverseDirection[nextDirection];
                    var returnCellIndex  = CartesianGridOnCubeUtility.CellIndexFromExitEdge(reverseDirection, nextCellIndex, grid.RowCount);

                    Assert.AreEqual(returnCellIndex, sourceCellIndex);
                }
            }
        }
예제 #2
0
            public void AddWallSouth(int cellIndex)
            {
                SetWallBit(cellIndex, CartesianGridDirectionBit.South);

                var cellIndexSouth = CartesianGridOnCubeUtility.CellIndexSouth(cellIndex, RowCount, FaceLocalToLocal);

                SetWallBit(cellIndexSouth, CartesianGridDirectionBit.North);
            }
예제 #3
0
            public void AddWallWest(int cellIndex)
            {
                SetWallBit(cellIndex, CartesianGridDirectionBit.West);

                var cellIndexWest = CartesianGridOnCubeUtility.CellIndexWest(cellIndex, RowCount, FaceLocalToLocal);

                SetWallBit(cellIndexWest, CartesianGridDirectionBit.East);
            }
예제 #4
0
            int WalkPath(int cellIndex, NativeArray <byte> targetDirections, int pathOffset)
            {
                var cellPosition = CartesianGridOnCubeUtility.CellFaceCoordinates(cellIndex, RowCount);
                var faceIndex    = CartesianGridOnCubeUtility.CellFaceIndex(cellIndex, RowCount);

                var validDirections = CartesianGridOnCubeShortestPath.LookupDirectionToTarget(cellPosition.x, cellPosition.y, faceIndex, RowCount, targetDirections);
                var direction       = CartesianGridMovement.PathVariation[((pathOffset & 3) * 16) + validDirections];

                if (direction == 0xff) // No path
                {
                    return(0);
                }

                var nextCellIndex = -1;

                if (direction == 0)
                {
                    nextCellIndex = CartesianGridOnCubeUtility.CellIndexNorth(cellIndex, RowCount, FaceLocalToLocal);
                }
                else if (direction == 1)
                {
                    nextCellIndex = CartesianGridOnCubeUtility.CellIndexSouth(cellIndex, RowCount, FaceLocalToLocal);
                }
                else if (direction == 2)
                {
                    nextCellIndex = CartesianGridOnCubeUtility.CellIndexWest(cellIndex, RowCount, FaceLocalToLocal);
                }
                else if (direction == 3)
                {
                    nextCellIndex = CartesianGridOnCubeUtility.CellIndexEast(cellIndex, RowCount, FaceLocalToLocal);
                }
                else
                {
                    Assert.Fail();
                }

                // Test no wall in the direction given
                if (direction == 0)
                {
                    Assert.IsFalse(TestWallBit(cellIndex, CartesianGridDirectionBit.North));
                }
                else if (direction == 1)
                {
                    Assert.IsFalse(TestWallBit(cellIndex, CartesianGridDirectionBit.South));
                }
                else if (direction == 2)
                {
                    Assert.IsFalse(TestWallBit(cellIndex, CartesianGridDirectionBit.West));
                }
                else if (direction == 3)
                {
                    Assert.IsFalse(TestWallBit(cellIndex, CartesianGridDirectionBit.East));
                }

                return(1 + WalkPath(nextCellIndex, targetDirections, pathOffset));
            }
예제 #5
0
        public void PathRandomObstacles()
        {
            using (var grid = new TestGrid(31))
            {
                int obstacleCount = 32;
                var rand          = new Unity.Mathematics.Random(0xF545AA3F);
                for (int i = 0; i < obstacleCount; i++)
                {
                    var faceIndex      = rand.NextInt(0, 6);
                    var xy             = rand.NextInt2(new int2(grid.RowCount, grid.RowCount));
                    var sourcePosition = new CartesianGridCoordinates {
                        x = (short)xy.x, y = (short)xy.y
                    };
                    var cubeFace = new CartesianGridOnCubeFace {
                        Value = (byte)faceIndex
                    };
                    var cellIndex = CartesianGridOnCubeUtility.CellIndex(sourcePosition, cubeFace, grid.RowCount);
                    if (rand.NextBool())
                    {
                        grid.AddWallWest(cellIndex);
                    }
                    else
                    {
                        grid.AddWallSouth(cellIndex);
                    }
                }

                int testCount = 64;
                for (int i = 0; i < testCount; i++)
                {
                    var sourceXY        = rand.NextInt2(new int2(grid.RowCount, grid.RowCount));
                    var sourceFaceIndex = rand.NextInt(0, 6);
                    var sourceCubeFace  = new CartesianGridOnCubeFace {
                        Value = (byte)sourceFaceIndex
                    };
                    var sourcePosition = new CartesianGridCoordinates {
                        x = (short)sourceXY.x, y = (short)sourceXY.y
                    };

                    var targetXY        = rand.NextInt2(new int2(grid.RowCount, grid.RowCount));
                    var targetFaceIndex = rand.NextInt(0, 6);
                    var targetCubeFace  = new CartesianGridOnCubeFace {
                        Value = (byte)targetFaceIndex
                    };
                    var targetPosition = new CartesianGridCoordinates {
                        x = (short)targetXY.x, y = (short)targetXY.y
                    };

                    grid.WalkPathDistance(sourcePosition, sourceCubeFace, targetPosition, targetCubeFace);
                }
            }
        }
예제 #6
0
            public int WalkPathDistance(CartesianGridCoordinates sourcePosition, CartesianGridOnCubeFace sourceCubeFace, CartesianGridCoordinates targetPosition, CartesianGridOnCubeFace targetCubeFace)
            {
                var directionsRowStride = (RowCount + 1) / 2;
                var directionsSize      = 6 * RowCount * directionsRowStride;
                var distancesSize       = RowCount * RowCount * 6;

                var targetDirections = new NativeArray <byte>(directionsSize, Allocator.Temp);
                var sourceDirections = new NativeArray <byte>(directionsSize, Allocator.Temp);
                var targetDistances  = new NativeArray <int>(distancesSize, Allocator.Temp);
                var sourceDistances  = new NativeArray <int>(distancesSize, Allocator.Temp);

                // For testing purposes, recalculate paths every time.
                CartesianGridOnCubeShortestPath.CalculateShortestPathsToTarget(targetDirections, targetDistances, RowCount, targetPosition, targetCubeFace, Walls, FaceLocalToLocal);
                CartesianGridOnCubeShortestPath.CalculateShortestPathsToTarget(sourceDirections, sourceDistances, RowCount, sourcePosition, sourceCubeFace, Walls, FaceLocalToLocal);

                // Test distance form source->target is same as target->source
                var sourceCellIndex = CartesianGridOnCubeUtility.CellIndex(sourcePosition, sourceCubeFace, RowCount);
                var targetCellIndex = CartesianGridOnCubeUtility.CellIndex(targetPosition, targetCubeFace, RowCount);

                var sourceToTargetDistance = WalkPath(sourceCellIndex, targetDirections, 0);
                var targetToSourceDistance = WalkPath(targetCellIndex, sourceDirections, 0);

                Assert.AreEqual(sourceToTargetDistance, targetToSourceDistance);

                var expectedDistance = sourceToTargetDistance;

                // No surprises on stored distances
                Assert.AreEqual(sourceToTargetDistance, sourceDistances[targetCellIndex]);
                Assert.AreEqual(sourceToTargetDistance, targetDistances[sourceCellIndex]);

                // Sample path variations (not exhaustive, always follow the variation path option)
                for (int i = 1; i < 4; i++)
                {
                    // Test distance form source->target is same as target->source
                    // Test distance is same for all variations
                    sourceToTargetDistance = WalkPath(sourceCellIndex, targetDirections, i);
                    Assert.AreEqual(expectedDistance, sourceToTargetDistance);

                    targetToSourceDistance = WalkPath(targetCellIndex, sourceDirections, i);
                    Assert.AreEqual(expectedDistance, targetToSourceDistance);
                }

                targetDirections.Dispose();
                sourceDirections.Dispose();
                targetDistances.Dispose();
                sourceDistances.Dispose();

                return(expectedDistance);
            }
예제 #7
0
            public bool TestWallBit(int cellIndex, CartesianGridDirectionBit directionBit)
            {
                var cellPosition        = CartesianGridOnCubeUtility.CellFaceCoordinates(cellIndex, RowCount);
                var faceIndex           = CartesianGridOnCubeUtility.CellFaceIndex(cellIndex, RowCount);
                var rowStride           = (RowCount + 1) / 2;
                var faceStride          = RowCount * rowStride;
                var faceGridWallsOffset = faceIndex * faceStride;
                var x = cellPosition.x;
                var y = cellPosition.y;
                var gridWallsIndex = faceGridWallsOffset + ((y * ((RowCount + 1) / 2)) + (x / 2));

                Assert.IsTrue(cellPosition.OnGrid(RowCount, RowCount));

                return(((Walls[gridWallsIndex] >> (4 * (x & 1))) & (byte)directionBit) == (byte)directionBit);
            }
예제 #8
0
    // Simple grassfire to calculate shortest distance along path to target position for every position.
    // - i.e. breadth-first expansion from target position.
    static void CalculateShortestWalkableDistancesToTarget(NativeArray <int> targetDistances, int rowCount, byte *gridWalls, CartesianGridCoordinates targetPosition, CartesianGridOnCubeFace cubeFace, float4x4 *faceLocalToLocal)
    {
        var cellCount = rowCount * rowCount;
        var closed    = new UnsafeBitArray(6 * cellCount, Allocator.Temp, NativeArrayOptions.ClearMemory);
        var pending   = new UnsafeBitArray(6 * cellCount, Allocator.Temp, NativeArrayOptions.ClearMemory);
        var open      = new UnsafeRingQueue <int>(6 * cellCount, Allocator.Temp);

        var faceIndex           = cubeFace.Value;
        var faceTargetCellIndex = (targetPosition.y * rowCount) + targetPosition.x;

        for (int i = 0; i < targetDistances.Length; i++)
        {
            targetDistances[i] = -1;
        }

        var targetCellIndex = (faceIndex * cellCount) + faceTargetCellIndex;

        targetDistances[targetCellIndex] = 0;

        pending.Set(targetCellIndex, true);
        closed.Set(targetCellIndex, true);

        var cellIndexNorth = CartesianGridOnCubeUtility.CellIndexNorth(targetCellIndex, rowCount, faceLocalToLocal);
        var cellIndexSouth = CartesianGridOnCubeUtility.CellIndexSouth(targetCellIndex, rowCount, faceLocalToLocal);
        var cellIndexWest  = CartesianGridOnCubeUtility.CellIndexWest(targetCellIndex, rowCount, faceLocalToLocal);
        var cellIndexEast  = CartesianGridOnCubeUtility.CellIndexEast(targetCellIndex, rowCount, faceLocalToLocal);

        var rowStride           = (rowCount + 1) / 2;
        var faceStride          = rowCount * rowStride;
        var faceGridWallsOffset = faceIndex * faceStride;

        var validDirections = CartesianGridMovement.ValidDirections(targetPosition, rowCount, rowCount, gridWalls + faceGridWallsOffset);
        var validNorth      = ((validDirections & (byte)CartesianGridDirectionBit.North) != 0);
        var validSouth      = ((validDirections & (byte)CartesianGridDirectionBit.South) != 0);
        var validWest       = ((validDirections & (byte)CartesianGridDirectionBit.West) != 0);
        var validEast       = ((validDirections & (byte)CartesianGridDirectionBit.East) != 0);

        if (validNorth)
        {
            open.Enqueue(cellIndexNorth);
            pending.Set(cellIndexNorth, true);
        }

        if (validSouth)
        {
            open.Enqueue(cellIndexSouth);
            pending.Set(cellIndexSouth, true);
        }

        if (validWest)
        {
            open.Enqueue(cellIndexWest);
            pending.Set(cellIndexWest, true);
        }

        if (validEast)
        {
            open.Enqueue(cellIndexEast);
            pending.Set(cellIndexEast, true);
        }

        CalculateShortestWalkableDistancesToTargetInner(targetDistances, rowCount, gridWalls, faceLocalToLocal, pending, closed, open);

        closed.Dispose();
        pending.Dispose();
        open.Dispose();
    }
예제 #9
0
    static void CalculateShortestWalkableDistancesToTargetInner(NativeArray <int> targetDistances, int rowCount, byte *gridWalls, float4x4 *faceLocalToLocal, UnsafeBitArray pending, UnsafeBitArray closed, UnsafeRingQueue <int> open)
    {
        var cellCount     = rowCount * rowCount;
        var maxPathLength = 6 * (cellCount + 1);

        while (open.Length > 0)
        {
            var cellIndex    = open.Dequeue();
            var cellPosition = CartesianGridOnCubeUtility.CellFaceCoordinates(cellIndex, rowCount);
            var faceIndex    = CartesianGridOnCubeUtility.CellFaceIndex(cellIndex, rowCount);

            var rowStride           = (rowCount + 1) / 2;
            var faceStride          = rowCount * rowStride;
            var faceGridWallsOffset = faceIndex * faceStride;

            var validDirections = CartesianGridMovement.ValidDirections(cellPosition, rowCount, rowCount, gridWalls + faceGridWallsOffset);
            var validNorth      = ((validDirections & (byte)CartesianGridDirectionBit.North) != 0);
            var validSouth      = ((validDirections & (byte)CartesianGridDirectionBit.South) != 0);
            var validWest       = ((validDirections & (byte)CartesianGridDirectionBit.West) != 0);
            var validEast       = ((validDirections & (byte)CartesianGridDirectionBit.East) != 0);

            var cellIndexNorth = CartesianGridOnCubeUtility.CellIndexNorth(cellIndex, rowCount, faceLocalToLocal);
            var cellIndexSouth = CartesianGridOnCubeUtility.CellIndexSouth(cellIndex, rowCount, faceLocalToLocal);
            var cellIndexWest  = CartesianGridOnCubeUtility.CellIndexWest(cellIndex, rowCount, faceLocalToLocal);
            var cellIndexEast  = CartesianGridOnCubeUtility.CellIndexEast(cellIndex, rowCount, faceLocalToLocal);

            var distanceNorth = maxPathLength;
            var distanceSouth = maxPathLength;
            var distanceEast  = maxPathLength;
            var distanceWest  = maxPathLength;

            var closedNorth = false;
            var closedSouth = false;
            var closedWest  = false;
            var closedEast  = false;

            if (validNorth)
            {
                if (closed.IsSet(cellIndexNorth))
                {
                    distanceNorth = targetDistances[cellIndexNorth];
                    closedNorth   = true;
                }
                else if (!pending.IsSet(cellIndexNorth))
                {
                    open.Enqueue(cellIndexNorth);
                    pending.Set(cellIndexNorth, true);
                }
            }

            if (validSouth)
            {
                if (closed.IsSet(cellIndexSouth))
                {
                    distanceSouth = targetDistances[cellIndexSouth];
                    closedSouth   = true;
                }
                else if (!pending.IsSet(cellIndexSouth))
                {
                    open.Enqueue(cellIndexSouth);
                    pending.Set(cellIndexSouth, true);
                }
            }

            if (validWest)
            {
                if (closed.IsSet(cellIndexWest))
                {
                    distanceWest = targetDistances[cellIndexWest];
                    closedWest   = true;
                }
                else if (!pending.IsSet(cellIndexWest))
                {
                    open.Enqueue(cellIndexWest);
                    pending.Set(cellIndexWest, true);
                }
            }

            if (validEast)
            {
                if (closed.IsSet(cellIndexEast))
                {
                    distanceEast = targetDistances[cellIndexEast];
                    closedEast   = true;
                }
                else if (!pending.IsSet(cellIndexEast))
                {
                    open.Enqueue(cellIndexEast);
                    pending.Set(cellIndexEast, true);
                }
            }

            var closedNeighbor = closedNorth || closedSouth || closedWest || closedEast;
            Assert.IsTrue(closedNeighbor);

            var bestDist = math.cmin(new int4(distanceNorth, distanceSouth, distanceEast, distanceWest)) + 1;
            Assert.IsFalse(bestDist > (maxPathLength + 1));

            targetDistances[cellIndex] = bestDist;
            closed.Set(cellIndex, true);
        }
    }
예제 #10
0
    // Sample valid neighboring distances from point.
    // - Smallest distance less than current position's distance is best next path to target.
    // - May result in more than one best direction (any of NSWE)
    // - May result in no best direction if on island (result=0xff)
    static void CalculateShortestPathGivenDistancesToTarget(NativeArray <byte> targetDirections, int rowCount, NativeArray <int> cellDistances, byte *gridWalls, float4x4 *faceLocalToLocal)
    {
        var cellCount     = rowCount * rowCount;
        var maxPathLength = 6 * (cellCount + 1);

        for (int i = 0; i < targetDirections.Length; i++)
        {
            targetDirections[i] = 0;
        }

        for (var cellIndex = 0; cellIndex < (6 * cellCount); cellIndex++)
        {
            var cellPosition = CartesianGridOnCubeUtility.CellFaceCoordinates(cellIndex, rowCount);
            var faceIndex    = CartesianGridOnCubeUtility.CellFaceIndex(cellIndex, rowCount);

            var rowStride           = (rowCount + 1) / 2;
            var faceStride          = rowCount * rowStride;
            var faceGridWallsOffset = faceIndex * faceStride;

            var validDirections = CartesianGridMovement.ValidDirections(cellPosition, rowCount, rowCount, gridWalls + faceGridWallsOffset);
            var validNorth      = ((validDirections & (byte)CartesianGridDirectionBit.North) != 0);
            var validSouth      = ((validDirections & (byte)CartesianGridDirectionBit.South) != 0);
            var validWest       = ((validDirections & (byte)CartesianGridDirectionBit.West) != 0);
            var validEast       = ((validDirections & (byte)CartesianGridDirectionBit.East) != 0);

            var cellIndexNorth = CartesianGridOnCubeUtility.CellIndexNorth(cellIndex, rowCount, faceLocalToLocal);
            var cellIndexSouth = CartesianGridOnCubeUtility.CellIndexSouth(cellIndex, rowCount, faceLocalToLocal);
            var cellIndexWest  = CartesianGridOnCubeUtility.CellIndexWest(cellIndex, rowCount, faceLocalToLocal);
            var cellIndexEast  = CartesianGridOnCubeUtility.CellIndexEast(cellIndex, rowCount, faceLocalToLocal);

            var distanceNorth = maxPathLength;
            var distanceSouth = maxPathLength;
            var distanceEast  = maxPathLength;
            var distanceWest  = maxPathLength;

            if (validNorth)
            {
                distanceNorth = cellDistances[cellIndexNorth];
            }
            if (validSouth)
            {
                distanceSouth = cellDistances[cellIndexSouth];
            }
            if (validWest)
            {
                distanceWest = cellDistances[cellIndexWest];
            }
            if (validEast)
            {
                distanceEast = cellDistances[cellIndexEast];
            }

            var bestDist = math.cmin(new int4(distanceNorth, distanceSouth, distanceEast, distanceWest));
            var dist     = cellDistances[cellIndex];

            if ((bestDist < dist) && (bestDist < maxPathLength))
            {
                var bestDir = 0;
                if (distanceNorth == bestDist)
                {
                    bestDir |= (byte)CartesianGridDirectionBit.North;
                }
                if (distanceSouth == bestDist)
                {
                    bestDir |= (byte)CartesianGridDirectionBit.South;
                }
                if (distanceWest == bestDist)
                {
                    bestDir |= (byte)CartesianGridDirectionBit.West;
                }
                if (distanceEast == bestDist)
                {
                    bestDir |= (byte)CartesianGridDirectionBit.East;
                }

                var targetDirectionsIndex = (faceIndex * faceStride) + (cellPosition.y * rowStride) + (cellPosition.x / 2);

                targetDirections[targetDirectionsIndex] |= (byte)(bestDir << (4 * (cellPosition.x & 1)));
            }
        }
    }