Beispiel #1
0
    static Entity FindTargetShortestPathLength(CartesianGridCoordinates gridCoordinates, CartesianGridOnCubeFace cubeFace, int rowCount, NativeArray <CartesianGridCoordinates> targetCoordinates, NativeArray <Entity> targetEntities, BufferFromEntity <CartesianGridTargetDistance> getCartesianGridTargetDistanceFromEntity)
    {
        var targetEntity = Entity.Null;

        if (!gridCoordinates.OnGrid(rowCount, rowCount))
        {
            return(targetEntity);
        }

        var targetBestDistance = 6 * ((rowCount * rowCount) + 1);

        for (int i = 0; i < targetCoordinates.Length; i++)
        {
            // Note targets will be invisible (off grid) when transitioning between cube faces
            if (!targetCoordinates[i].OnGrid(rowCount, rowCount))
            {
                continue;
            }

            var targetDistances = getCartesianGridTargetDistanceFromEntity[targetEntities[i]].Reinterpret <int>().AsNativeArray();
            var targetDistance  = CartesianGridOnCubeShortestPath.LookupDistanceToTarget(gridCoordinates, cubeFace, rowCount, targetDistances);
            if (targetDistance < targetBestDistance)
            {
                targetEntity       = targetEntities[i];
                targetBestDistance = targetDistance;
            }
        }
        return(targetEntity);
    }
Beispiel #2
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);
                }
            }
        }
    public static int CellIndex(CartesianGridCoordinates cellPosition, CartesianGridOnCubeFace cubeFace, int rowCount)
    {
        var rowStride  = rowCount;
        var faceStride = rowCount * rowStride;
        var cellIndex  = (cubeFace.Value * faceStride) + (cellPosition.y * rowStride) + cellPosition.x;

        return(cellIndex);
    }
    /// <summary>
    /// Pick valid direction to "bounce" off (move along) walls.
    /// </summary>
    /// <param name="cellPosition">Position to test (must be valid on grid)</param>
    /// <param name="dir">Current movement direction</param>
    /// <param name="rowCount">Height of grid</param>
    /// <param name="colCount">Width of grid</param>
    /// <param name="gridWalls">Table representing walls of grid.</param>
    /// <param name="pathIndex">Index to select from multiple valid options. See: m_NextDirection</param>
    /// <returns></returns>
    // gridPosition needs to be on-grid (positive and < [colCount, rowCount]) when looking up next direction.
    public static byte BounceDirectionOffWalls(CartesianGridCoordinates cellPosition, byte dir, int rowCount, int colCount, byte *gridWalls, int pathIndex)
    {
        var pathOffset = pathIndex * 16;
        var walls      = LookupWalls(cellPosition, rowCount, colCount, gridWalls);

        // New direction = f( grid index, movement direction )
        return((byte)((m_NextDirection[pathOffset + walls] >> (dir * 2)) & 0x03));
    }
    static byte LookupWalls(CartesianGridCoordinates cartesianGridCoordinates, int rowCount, int colCount, byte *gridWalls)
    {
        var rowStride      = ((colCount + 1) / 2);
        var gridWallsIndex = (cartesianGridCoordinates.y * rowStride) + (cartesianGridCoordinates.x / 2);

        // Walls in current grid element (odd columns in upper 4 bits of byte)
        var walls = (gridWalls[gridWallsIndex] >> ((cartesianGridCoordinates.x & 1) * 4)) & 0x0f;

        return((byte)walls);
    }
    /// <summary>
    /// Find directions along shortest path(s) to target.
    /// </summary>
    /// <param name="cellPosition">Current position</param>
    /// <param name="rowCount">Height of grid</param>
    /// <param name="colCount">Width of grid</param>
    /// <param name="targetDirections">Lookup table generated by CalculatePathsToTarget</param>
    /// <returns>Any (or none) best directions along any shortest path in form of CartesianGridDirectionBit</returns>
    public static byte LookupDirectionToTarget(CartesianGridCoordinates cellPosition, int rowCount, int colCount, NativeArray <byte> targetDirections)
    {
        var rowStride      = ((colCount + 1) / 2);
        var gridWallsIndex = (cellPosition.y * rowStride) + (cellPosition.x / 2);

        // Walls in current grid element (odd columns in upper 4 bits of byte)
        var directions = (targetDirections[gridWallsIndex] >> ((cellPosition.x & 1) * 4)) & 0x0f;

        return((byte)directions);
    }
Beispiel #7
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);
                }
            }
        }
    // gridPosition needs to be on-grid (positive and < [colCount, rowCount]) when looking up next direction.
    public static byte LookupGridDirectionFromWalls(CartesianGridCoordinates cartesianGridCoordinates, byte dir, int rowStride, byte *gridWalls, int pathIndex)
    {
        var pathOffset = pathIndex * 16;

        // Index into grid array
        var gridWallsIndex = (cartesianGridCoordinates.y * rowStride) + (cartesianGridCoordinates.x / 2);

        // Walls in current grid element (odd columns in upper 4 bits of byte)
        var walls = (gridWalls[gridWallsIndex] >> ((cartesianGridCoordinates.x & 1) * 4)) & 0x0f;

        // New direction = f( grid index, movement direction )
        return((byte)((NextDirection[pathOffset + walls] >> (dir * 2)) & 0x03));
    }
Beispiel #9
0
        public void PathOnEmptyGrid()
        {
            using (var grid = new TestGrid(16))
            {
                var targetPosition = new CartesianGridCoordinates {
                    x = 15, y = 15
                };
                var targetCubeFace = new CartesianGridOnCubeFace {
                    Value = 0
                };
                var sourcePosition = new CartesianGridCoordinates {
                    x = 8, y = 8
                };
                var sourceCubeFace0 = new CartesianGridOnCubeFace {
                    Value = 0
                };
                var sourceCubeFace1 = new CartesianGridOnCubeFace {
                    Value = 1
                };
                var sourceCubeFace2 = new CartesianGridOnCubeFace {
                    Value = 2
                };
                var sourceCubeFace3 = new CartesianGridOnCubeFace {
                    Value = 3
                };
                var sourceCubeFace4 = new CartesianGridOnCubeFace {
                    Value = 4
                };
                var sourceCubeFace5 = new CartesianGridOnCubeFace {
                    Value = 5
                };

                var dist0 = grid.WalkPathDistance(sourcePosition, sourceCubeFace0, targetPosition, targetCubeFace);
                Assert.AreEqual(dist0, 14);

                var dist1 = grid.WalkPathDistance(sourcePosition, sourceCubeFace1, targetPosition, targetCubeFace);
                Assert.AreEqual(dist1, 32);

                var dist2 = grid.WalkPathDistance(sourcePosition, sourceCubeFace2, targetPosition, targetCubeFace);
                Assert.AreEqual(dist2, 30);

                var dist3 = grid.WalkPathDistance(sourcePosition, sourceCubeFace3, targetPosition, targetCubeFace);
                Assert.AreEqual(dist3, 16);

                var dist4 = grid.WalkPathDistance(sourcePosition, sourceCubeFace4, targetPosition, targetCubeFace);
                Assert.AreEqual(dist4, 17);

                var dist5 = grid.WalkPathDistance(sourcePosition, sourceCubeFace5, targetPosition, targetCubeFace);
                Assert.AreEqual(dist5, 31);
            }
        }
    /// <summary>
    /// Which edge of GridCube face is being exited (if any)
    /// </summary>
    /// <param name="cellPosition">Position to test</param>
    /// <param name="rowCount">Height/Width of cube face</param>
    /// <returns></returns>
    public static int CubeExitEdge(CartesianGridCoordinates cellPosition, int rowCount)
    {
        // Which edge of GridCube face is being exited (if any)
        var edge = -1;

        // Edge is in order specified in m_NextFaceIndex and m_NextFaceDirection
        // - Matches GridDirection values.
        edge = math.select(edge, 0, cellPosition.y >= rowCount);
        edge = math.select(edge, 1, cellPosition.y < 0);
        edge = math.select(edge, 2, cellPosition.x < 0);
        edge = math.select(edge, 3, cellPosition.x >= rowCount);

        return(edge);
    }
    public static float3 ClampToGrid(float3 v, byte dir, CartesianGridCoordinates cartesianGridCoordinates, float2 cellCenterOffset)
    {
        // When (dir == N,S) clamp to grid cell center x
        // When (dir == W,E) clamp to grid cell center y
        var mx = (dir >> 1) * 1.0f;
        var my = ((dir >> 1) ^ 1) * 1.0f;

        return(new float3
        {
            x = (mx * v.x) + (my * (cartesianGridCoordinates.x - cellCenterOffset.x)),
            z = (my * v.z) + (mx * (cartesianGridCoordinates.y - cellCenterOffset.y)),
            y = v.y
        });
    }
    /// <summary>
    /// Snap translation to center of grid cell along the direction movement.
    /// This ensures translation does not drift substantially over time with accumulated errors.
    /// </summary>
    /// <param name="translation">Translation (in grid space) to snap</param>
    /// <param name="dir">Direction of movement. See: CartesianGridDirection</param>
    /// <param name="cellPosition">Current cell to snap to</param>
    /// <param name="cellCenterOlffset">Offset relative to center of grid.</param>
    /// <returns></returns>
    public static float3 SnapToGridAlongDirection(float3 translation, byte dir, CartesianGridCoordinates cellPosition, float2 cellCenterOffset)
    {
        // When (dir == N,S) clamp to grid cell center x
        // When (dir == W,E) clamp to grid cell center y
        var mx = (dir >> 1) * 1.0f;
        var my = ((dir >> 1) ^ 1) * 1.0f;

        return(new float3
        {
            x = (mx * translation.x) + (my * (cellPosition.x - cellCenterOffset.x)),
            z = (my * translation.z) + (mx * (cellPosition.y - cellCenterOffset.y)),
            y = translation.y
        });
    }
Beispiel #13
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);
            }
Beispiel #14
0
    static Entity FindTargetShortestManhattanDistance(CartesianGridCoordinates gridCoordinates, int rowCount, int colCount, NativeArray <CartesianGridCoordinates> targetCoordinates, NativeArray <Entity> targetEntities)
    {
        var targetEntity       = Entity.Null;
        var targetBestDistance = (rowCount * colCount) + 1;

        for (int i = 0; i < targetCoordinates.Length; i++)
        {
            var targetDistance = math.abs(targetCoordinates[i].x - gridCoordinates.x) + math.abs(targetCoordinates[i].y - gridCoordinates.y);
            if (targetDistance < targetBestDistance)
            {
                targetEntity       = targetEntities[i];
                targetBestDistance = targetDistance;
            }
        }
        return(targetEntity);
    }
        public void PathOnEmptyGrid()
        {
            using (var grid = new TestGrid(16, 16))
            {
                var targetPosition = new CartesianGridCoordinates {
                    x = 15, y = 15
                };
                var dist = 0;

                dist = grid.WalkPathDistance(new CartesianGridCoordinates {
                    x = 0, y = 0
                }, targetPosition);
                Assert.AreEqual(dist, 30);
                dist = grid.WalkPathDistance(new CartesianGridCoordinates {
                    x = 8, y = 7
                }, targetPosition);
                Assert.AreEqual(dist, 15);
            }
        }
    public static int CellIndexFromExitEdge(int edge, int faceIndex, CartesianGridCoordinates facePosition, int rowCount)
    {
        var cellCount     = rowCount * rowCount;
        var nextFaceIndex = NextFaceIndex[(edge * 6) + faceIndex];
        var cx            = (rowCount - 1) * 0.5f;
        var px            = math.clamp(facePosition.x, 0, rowCount - 1);
        var py            = math.clamp(facePosition.y, 0, rowCount - 1);
        var x0            = px - cx;
        var y0            = py - cx;
        var ax            = m_GridToGrid[(edge * 6 * 2) + (faceIndex * 2) + 0];
        var ay            = m_GridToGrid[(edge * 6 * 2) + (faceIndex * 2) + 1];
        var x1            = (ax.x * x0) + (ax.y * y0);
        var y1            = (ay.x * x0) + (ay.y * y0);
        var nx            = (short)(x1 + cx + 0.5f);
        var ny            = (short)(y1 + cx + 0.5f);

        var nextFaceCellIndex = (ny * rowCount) + nx;

        return((nextFaceIndex * cellCount) + nextFaceCellIndex);
    }
        public void PathOnIsland()
        {
            using (var grid = new TestGrid(16, 16))
            {
                // create island left/right side
                for (int y = 0; y < grid.RowCount; y++)
                {
                    grid.AddWallWest(new CartesianGridCoordinates {
                        x = 8, y = (short)y
                    });
                }

                var targetPosition = new CartesianGridCoordinates {
                    x = 15, y = 15
                };
                var dist = 0;

                // right side has open path
                dist = grid.WalkPathDistance(new CartesianGridCoordinates {
                    x = 8, y = 8
                }, targetPosition);
                Assert.AreEqual(dist, 14);
                dist = grid.WalkPathDistance(new CartesianGridCoordinates {
                    x = 8, y = 0
                }, targetPosition);
                Assert.AreEqual(dist, 22);

                // left side blocked
                dist = grid.WalkPathDistance(new CartesianGridCoordinates {
                    x = 0, y = 8
                }, targetPosition);
                Assert.AreEqual(dist, 0);
                dist = grid.WalkPathDistance(new CartesianGridCoordinates {
                    x = 0, y = 0
                }, targetPosition);
                Assert.AreEqual(dist, 0);
            }
        }
        public void PathRandomObstacles()
        {
            using (var grid = new TestGrid(32, 15))
            {
                int obstacleCount = 32;
                var rand          = new Unity.Mathematics.Random(0xF545AA3F);
                for (int i = 0; i < obstacleCount; i++)
                {
                    var xy = rand.NextInt2(new int2(grid.ColCount, grid.RowCount));
                    if (rand.NextBool())
                    {
                        grid.AddWallWest(new CartesianGridCoordinates {
                            x = (short)xy.x, y = (short)xy.y
                        });
                    }
                    else
                    {
                        grid.AddWallSouth(new CartesianGridCoordinates {
                            x = (short)xy.x, y = (short)xy.y
                        });
                    }
                }

                int testCount = 64;
                for (int i = 0; i < testCount; i++)
                {
                    var sourceXY       = rand.NextInt2(new int2(grid.ColCount, grid.RowCount));
                    var targetXY       = rand.NextInt2(new int2(grid.ColCount, grid.RowCount));
                    var sourcePosition = new CartesianGridCoordinates {
                        x = (short)sourceXY.x, y = (short)sourceXY.y
                    };
                    var targetPosition = new CartesianGridCoordinates {
                        x = (short)targetXY.x, y = (short)targetXY.y
                    };
                    grid.WalkPathDistance(sourcePosition, targetPosition);
                }
            }
        }
Beispiel #19
0
    protected override JobHandle OnUpdate(JobHandle lastJobHandle)
    {
        int pathOffset = CartesianGridMovement.NextPathIndex(ref m_NextPathCounter);

        // Get component data from the Grid (GridPlane or GridCube)
        var cartesianGridPlane = GetSingleton <CartesianGridOnPlane>();
        var rowCount           = cartesianGridPlane.Blob.Value.RowCount;
        var colCount           = cartesianGridPlane.Blob.Value.ColCount;
        var rowStride          = ((colCount + 1) / 2);
        var gridWalls          = (byte *)cartesianGridPlane.Blob.Value.Walls.GetUnsafePtr();
        var trailingOffsets    = (float2 *)cartesianGridPlane.Blob.Value.TrailingOffsets.GetUnsafePtr();

        // Offset center to grid cell
        var cellCenterOffset = new float2(((float)colCount * 0.5f) - 0.5f, ((float)rowCount * 0.5f) - 0.5f);

        lastJobHandle = Entities
                        .WithName("CartesianGridPlaneChangeDirection")
                        .WithNativeDisableUnsafePtrRestriction(trailingOffsets)
                        .WithNativeDisableUnsafePtrRestriction(gridWalls)
                        .ForEach((ref CartesianGridDirection gridDirection,
                                  ref CartesianGridCoordinates gridCoordinates,
                                  ref Translation translation) =>
        {
            var dir = gridDirection.Value;
            var nextGridPosition = new CartesianGridCoordinates(translation.Value.xz + trailingOffsets[dir], rowCount, colCount);
            if (gridCoordinates.Equals(nextGridPosition))
            {
                // Don't allow translation to drift
                translation.Value = CartesianGridMovement.ClampToGrid(translation.Value, dir, gridCoordinates, cellCenterOffset);
                return;     // Still in the same grid cell. No need to change direction.
            }

            gridCoordinates     = nextGridPosition;
            gridDirection.Value = CartesianGridMovement.LookupGridDirectionFromWalls(gridCoordinates, dir, rowStride, gridWalls, pathOffset);
        }).Schedule(lastJobHandle);

        return(lastJobHandle);
    }
            public int WalkPathDistance(CartesianGridCoordinates sourcePosition, CartesianGridCoordinates targetPosition)
            {
                var directionsRowStride = (ColCount + 1) / 2;
                var directionsSize      = RowCount * directionsRowStride;

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

                // For testing purposes, recalculate paths every time.
                CartesianGridOnPlaneShortestPath.CalculateShortestPathsToTarget(targetDirections, RowCount, ColCount, targetPosition, Walls);
                CartesianGridOnPlaneShortestPath.CalculateShortestPathsToTarget(sourceDirections, RowCount, ColCount, sourcePosition, Walls);

                // Test distance form source->target is same as target->source
                var sourceToTargetDistance = WalkPath(sourcePosition, targetDirections, 0);
                var targetToSourceDistance = WalkPath(targetPosition, sourceDirections, 0);

                Assert.AreEqual(sourceToTargetDistance, targetToSourceDistance);

                var expectedDistance = sourceToTargetDistance;

                // 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(sourcePosition, targetDirections, i);
                    Assert.AreEqual(expectedDistance, sourceToTargetDistance);
                    targetToSourceDistance = WalkPath(targetPosition, sourceDirections, i);
                    Assert.AreEqual(expectedDistance, targetToSourceDistance);
                }

                targetDirections.Dispose();
                sourceDirections.Dispose();

                return(expectedDistance);
            }
 /// <summary>
 /// Return directions from cellPosition which are not blocked by walls.
 /// </summary>
 /// <param name="cellPosition">Position to test</param>
 /// <param name="rowCount">Height of grid</param>
 /// <param name="colCount">Width of grid</param>
 /// <param name="gridWalls">Table representing walls of grid.</param>
 /// <returns></returns>
 public static byte ValidDirections(CartesianGridCoordinates cellPosition, int rowCount, int colCount, byte *gridWalls)
 {
     return((byte)(0x0f & ~LookupWalls(cellPosition, rowCount, colCount, gridWalls)));
 }
Beispiel #22
0
 /// <summary>
 /// Find shortest path for every position on grid to target position
 /// </summary>
 /// <param name="targetDirections">Result table of all shortest paths for every position on grid to targetPosition</param>
 /// <param name="rowCount">Height of grid</param>
 /// <param name="targetPosition">Generate all shortest paths to this position.</param>
 /// <param name="gridWalls">Table representing walls/obstacles in grid. See: CartesianGridMovement</param>
 public static void CalculateShortestPathsToTarget(NativeArray <byte> targetDirections, NativeArray <int> targetDistances, int rowCount, CartesianGridCoordinates targetPosition, CartesianGridOnCubeFace cubeFace, byte *gridWalls, float4x4 *faceLocalToLocal)
 {
     CalculateShortestWalkableDistancesToTarget(targetDistances, rowCount, gridWalls, targetPosition, cubeFace, faceLocalToLocal);
     CalculateShortestPathGivenDistancesToTarget(targetDirections, rowCount, targetDistances, gridWalls, faceLocalToLocal);
 }
Beispiel #23
0
 /// <summary>
 /// Find directions along shortest path(s) to target.
 /// </summary>
 /// <param name="cellPosition">Current position</param>
 /// <param name="rowCount">Height of grid</param>
 /// <param name="targetDirections">Lookup table generated by CalculatePathsToTarget</param>
 /// <returns>Any (or none) best directions along any shortest path in form of CartesianGridDirectionBit</returns>
 public static byte LookupDirectionToTarget(CartesianGridCoordinates cellPosition, CartesianGridOnCubeFace cubeFace, int rowCount, NativeArray <byte> targetDirections)
 {
     return(LookupDirectionToTarget(cellPosition.x, cellPosition.y, cubeFace.Value, rowCount, targetDirections));
 }
Beispiel #24
0
    protected override JobHandle OnUpdate(JobHandle lastJobHandle)
    {
        int pathOffset = m_PathVariationOffset;

        m_PathVariationOffset = (m_PathVariationOffset + 1) & 3;

        // Get component data from the GridPlane
        var cartesianGridPlane = GetSingleton <CartesianGridOnPlane>();
        var rowCount           = cartesianGridPlane.Blob.Value.RowCount;
        var colCount           = cartesianGridPlane.Blob.Value.ColCount;
        var trailingOffsets    = (float2 *)cartesianGridPlane.Blob.Value.TrailingOffsets.GetUnsafePtr();

        var targetEntities    = m_TargetQuery.ToEntityArray(Allocator.TempJob);
        var targetCoordinates = m_TargetQuery.ToComponentDataArray <CartesianGridCoordinates>(Allocator.TempJob);
        var getCartesianGridTargetDirectionFromEntity = GetBufferFromEntity <CartesianGridTargetDirection>(true);

        // Offset center to grid cell
        var cellCenterOffset = new float2(((float)colCount * 0.5f) - 0.5f, ((float)rowCount * 0.5f) - 0.5f);

        // Whenever a CartesianGridFollowTarget reaches a new grid cell, make a decision about what next direction to turn.
        lastJobHandle = Entities
                        .WithName("ChangeDirectionTowardNearestTarget")
                        .WithNativeDisableUnsafePtrRestriction(trailingOffsets)
                        .WithEntityQueryOptions(EntityQueryOptions.FilterWriteGroup)
                        .WithReadOnly(targetCoordinates)
                        .WithReadOnly(getCartesianGridTargetDirectionFromEntity)
                        .WithAll <CartesianGridFollowTarget>()
                        .ForEach((ref CartesianGridDirection gridDirection,
                                  ref CartesianGridCoordinates gridCoordinates,
                                  ref Translation translation) =>
        {
            var dir = gridDirection.Value;
            if (dir != 0xff)         // If moving, update grid based on trailing direction.
            {
                var nextGridPosition = new CartesianGridCoordinates(translation.Value.xz + trailingOffsets[dir], rowCount, colCount);
                if (gridCoordinates.Equals(nextGridPosition))
                {
                    // Don't allow translation to drift
                    translation.Value = CartesianGridMovement.SnapToGridAlongDirection(translation.Value, dir, gridCoordinates, cellCenterOffset);
                    return;         // Still in the same grid cell. No need to change direction.
                }

                gridCoordinates = nextGridPosition;
            }

            var targetEntity = FindTargetShortestManhattanDistance(gridCoordinates, rowCount, colCount, targetCoordinates, targetEntities);
            if (targetEntity == Entity.Null)
            {
                // No target for whatever reason, don't move.
                gridDirection.Value = 0xff;
                return;
            }

            // Lookup next direction along shortest path to target from table stored in CartesianGridTargetDirection
            // - When multiple shortest path available, use pathOffset to select which option.
            var targetDirections = getCartesianGridTargetDirectionFromEntity[targetEntity].Reinterpret <byte>().AsNativeArray();
            var validDirections  = CartesianGridOnPlaneShortestPath.LookupDirectionToTarget(gridCoordinates, rowCount, colCount, targetDirections);
            gridDirection.Value  = CartesianGridMovement.PathVariation[(pathOffset * 16) + validDirections];
        }).Schedule(lastJobHandle);

        lastJobHandle = targetEntities.Dispose(lastJobHandle);
        lastJobHandle = targetCoordinates.Dispose(lastJobHandle);

        return(lastJobHandle);
    }
Beispiel #25
0
    protected override JobHandle OnUpdate(JobHandle lastJobHandle)
    {
        int pathOffset = CartesianGridMovement.NextPathIndex(ref m_NextPathCounter);

        // Get component data from the Grid (GridCube or GridCube)
        var cartesianGridCube = GetSingleton <CartesianGridOnCube>();
        var rowCount          = cartesianGridCube.Blob.Value.RowCount;
        var gridWalls         = (byte *)cartesianGridCube.Blob.Value.Walls.GetUnsafePtr();
        var trailingOffsets   = (float2 *)cartesianGridCube.Blob.Value.TrailingOffsets.GetUnsafePtr();
        var faceLocalToLocal  = (float4x4 *)cartesianGridCube.Blob.Value.FaceLocalToLocal.GetUnsafePtr();

        // Offset center to grid cell
        var cellCenterOffset = new float2(((float)rowCount * 0.5f) - 0.5f, ((float)rowCount * 0.5f) - 0.5f);

        lastJobHandle = Entities
                        .WithName("CartesianGridOnCubeChangeDirection")
                        .WithNativeDisableUnsafePtrRestriction(gridWalls)
                        .WithNativeDisableUnsafePtrRestriction(faceLocalToLocal)
                        .WithNativeDisableUnsafePtrRestriction(trailingOffsets)
                        .WithEntityQueryOptions(EntityQueryOptions.FilterWriteGroup)
                        .ForEach((ref CartesianGridDirection gridDirection,
                                  ref Translation translation,
                                  ref CartesianGridCoordinates gridCoordinates,
                                  ref CartesianGridOnCubeFace cubeFace) =>
        {
            var prevDir          = gridDirection.Value;
            var trailingOffset   = trailingOffsets[prevDir];
            var pos              = translation.Value.xz + trailingOffset;
            var nextGridPosition = new CartesianGridCoordinates(pos, rowCount, rowCount);
            if (gridCoordinates.Equals(nextGridPosition))
            {
                // Don't allow translation to drift
                translation.Value = CartesianGridMovement.SnapToGridAlongDirection(translation.Value, prevDir, gridCoordinates, cellCenterOffset);
                return;         // Still in the same grid cell. No need to change direction.
            }

            var edge = CartesianGridMovement.CubeExitEdge(nextGridPosition, rowCount);

            // Change direction based on wall layout (within current face.)
            if (edge == -1)
            {
                var faceIndex           = cubeFace.Value;
                var rowStride           = (rowCount + 1) / 2;
                var faceStride          = rowCount * rowStride;
                var faceGridWallsOffset = faceIndex * faceStride;

                gridCoordinates     = nextGridPosition;
                gridDirection.Value = CartesianGridMovement.BounceDirectionOffWalls(gridCoordinates, prevDir, rowCount, rowCount, gridWalls + faceGridWallsOffset, pathOffset);
            }
            // Exiting face of GridCube, change face and direction relative to new face.
            else
            {
                int prevFaceIndex = cubeFace.Value;

                // Look up next direction given previous face and exit edge.
                var nextDir         = CartesianGridOnCubeUtility.NextFaceDirection[(edge * 6) + prevFaceIndex];
                gridDirection.Value = nextDir;

                // Lookup next face index given previous face and exit edge.
                var nextFaceIndex = CartesianGridOnCubeUtility.NextFaceIndex[(edge * 6) + prevFaceIndex];
                cubeFace.Value    = nextFaceIndex;

                // Transform translation relative to next face's grid-space
                // - This transform is only done to "smooth" the transition around the edges.
                // - Alternatively, you could "snap" to the same relative position in the next face by rotating the translation components.
                // - Note that Y position won't be at target value from one edge to another, so that is interpolated in movement update,
                //   purely for "smoothing" purposes.
                var localToLocal      = faceLocalToLocal[((prevFaceIndex * 6) + nextFaceIndex)];
                translation.Value.xyz = math.mul(localToLocal, new float4(translation.Value, 1.0f)).xyz;

                // Update gridPosition relative to new face.
                gridCoordinates = new CartesianGridCoordinates(translation.Value.xz + trailingOffsets[nextDir], rowCount, rowCount);
            }
        }).Schedule(lastJobHandle);

        return(lastJobHandle);
    }
 public void AddWallWest(CartesianGridCoordinates cellPosition)
 {
     SetWallBit(cellPosition.x, cellPosition.y, CartesianGridDirectionBit.West);
     SetWallBit(cellPosition.x - 1, cellPosition.y, CartesianGridDirectionBit.East);
 }
 public void AddWallSouth(CartesianGridCoordinates cellPosition)
 {
     SetWallBit(cellPosition.x, cellPosition.y, CartesianGridDirectionBit.South);
     SetWallBit(cellPosition.x, cellPosition.y - 1, CartesianGridDirectionBit.North);
 }
Beispiel #28
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();
    }
Beispiel #29
0
 /// <summary>
 /// Find directions along shortest path(s) to target.
 /// </summary>
 /// <param name="cellPosition">Current position</param>
 /// <param name="rowCount">Height of grid</param>
 /// <param name="targetDistances">Lookup table generated by CalculatePathsToTarget</param>
 /// <returns>Any (or none) best directions along any shortest path in form of CartesianGridDirectionBit</returns>
 public static int LookupDistanceToTarget(CartesianGridCoordinates cellPosition, CartesianGridOnCubeFace cubeFace, int rowCount, NativeArray <int> targetDistances)
 {
     return(LookupDistanceToTarget(cellPosition.x, cellPosition.y, cubeFace.Value, rowCount, targetDistances));
 }
            int WalkPath(CartesianGridCoordinates startPosition, NativeArray <byte> targetDirections, int pathOffset)
            {
                var validDirections = CartesianGridOnPlaneShortestPath.LookupDirectionToTarget(startPosition, RowCount, ColCount, targetDirections);
                var direction       = CartesianGridMovement.PathVariation[((pathOffset & 3) * 16) + validDirections];

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

                var nextPosition = new CartesianGridCoordinates();

                if (direction == 0)
                {
                    nextPosition = new CartesianGridCoordinates {
                        x = startPosition.x, y = (short)(startPosition.y + 1)
                    }
                }
                ;
                else if (direction == 1)
                {
                    nextPosition = new CartesianGridCoordinates {
                        x = startPosition.x, y = (short)(startPosition.y - 1)
                    }
                }
                ;
                else if (direction == 2)
                {
                    nextPosition = new CartesianGridCoordinates {
                        x = (short)(startPosition.x - 1), y = (short)startPosition.y
                    }
                }
                ;
                else if (direction == 3)
                {
                    nextPosition = new CartesianGridCoordinates {
                        x = (short)(startPosition.x + 1), y = (short)startPosition.y
                    }
                }
                ;
                else
                {
                    Assert.Fail();
                }

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

                return(1 + WalkPath(nextPosition, targetDirections, pathOffset));
            }