Esempio n. 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);
                }
            }
        }
    // 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(int rowCount, int colCount, byte *gridWalls, CartesianGridCoordinates targetPosition, NativeArray <int> targetDistances)
    {
        var cellCount = rowCount * colCount;
        var closed    = new UnsafeBitArray(cellCount, Allocator.Temp, NativeArrayOptions.ClearMemory);
        var pending   = new UnsafeBitArray(cellCount, Allocator.Temp, NativeArrayOptions.ClearMemory);
        var open      = new UnsafeRingQueue <int>(cellCount, Allocator.Temp);

        var targetCellIndex = (targetPosition.y * colCount) + targetPosition.x;
        var cellIndexNorth  = targetCellIndex + colCount;
        var cellIndexSouth  = targetCellIndex - colCount;
        var cellIndexWest   = targetCellIndex - 1;
        var cellIndexEast   = targetCellIndex + 1;

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

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

        var validDirections = CartesianGridMovement.ValidDirections(targetPosition, rowCount, colCount, gridWalls);
        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(rowCount, colCount, gridWalls, targetDistances, pending, closed, open);

        closed.Dispose();
        pending.Dispose();
        open.Dispose();
    }
    public static int CellIndexEast(int cellIndex, int rowCount, float4x4 *faceLocalToLocal)
    {
        var faceIndex    = CellFaceIndex(cellIndex, rowCount);
        var facePosition = CellFaceCoordinates(cellIndex, rowCount);

        facePosition.x += 1;

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

        if (edge == -1)
        {
            return(cellIndex + 1);
        }

        return(CellIndexFromExitEdge(edge, faceIndex, facePosition, rowCount));
    }
Esempio n. 4
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);
    }
Esempio n. 5
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);
    }
Esempio n. 6
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);
    }
Esempio n. 7
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();
    }
Esempio n. 8
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);
        }
    }
Esempio n. 9
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)));
            }
        }
    }
Esempio n. 10
0
    protected override JobHandle OnUpdate(JobHandle lastJobHandle)
    {
        int pathOffset = m_PathVariationOffset;

        m_PathVariationOffset = (m_PathVariationOffset + 1) & 3;

        // Get component data from the 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();

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

        // Offset center to grid cell
        var cellCenterOffset = new float2(((float)rowCount * 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)
                        .WithNativeDisableUnsafePtrRestriction(faceLocalToLocal)
                        .WithNativeDisableUnsafePtrRestriction(gridWalls)
                        .WithEntityQueryOptions(EntityQueryOptions.FilterWriteGroup)
                        .WithReadOnly(targetCoordinates)
                        .WithReadOnly(getCartesianGridTargetDirectionFromEntity)
                        .WithReadOnly(getCartesianGridTargetDistanceFromEntity)
                        .WithAll <CartesianGridFollowTarget>()
                        .ForEach((ref CartesianGridDirection gridDirection,
                                  ref CartesianGridCoordinates gridCoordinates,
                                  ref Translation translation,
                                  ref CartesianGridOnCubeFace cubeFace) =>
        {
            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, rowCount);
                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.
                }

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

                // Change direction based on wall layout (within current face.)
                if (edge == -1)
                {
                    gridCoordinates = nextGridPosition;
                }
                // 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);
                }
            }

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

            var targetEntity = FindTargetShortestPathLength(gridCoordinates, cubeFace, rowCount, targetCoordinates, targetEntities, getCartesianGridTargetDistanceFromEntity);
            if (targetEntity == Entity.Null)
            {
                // No target for whatever reason, look busy.
                int faceIndex           = cubeFace.Value;
                var rowStride           = (rowCount + 1) / 2;
                var faceStride          = rowCount * rowStride;
                var faceGridWallsOffset = faceIndex * faceStride;
                gridDirection.Value     = CartesianGridMovement.BounceDirectionOffWalls(gridCoordinates, dir, rowCount, rowCount, gridWalls + faceGridWallsOffset, pathOffset);
                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  = CartesianGridOnCubeShortestPath.LookupDirectionToTarget(gridCoordinates, cubeFace, rowCount, targetDirections);
            gridDirection.Value  = CartesianGridMovement.PathVariation[(pathOffset * 16) + validDirections];
        }).Schedule(lastJobHandle);

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

        return(lastJobHandle);
    }
    static void CalculateShortestWalkableDistancesToTargetInner(int rowCount, int colCount, byte *gridWalls, NativeArray <int> targetDistances, UnsafeBitArray pending, UnsafeBitArray closed, UnsafeRingQueue <int> open)
    {
        var cellCount = rowCount * colCount;

        while (open.Count > 0)
        {
            var cellIndex    = open.Dequeue();
            var y            = cellIndex / colCount;
            var x            = cellIndex - (y * colCount);
            var cellPosition = new CartesianGridCoordinates {
                x = (short)x, y = (short)y
            };

            var validDirections = CartesianGridMovement.ValidDirections(cellPosition, rowCount, colCount, gridWalls);
            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 = cellIndex + colCount;
            var cellIndexSouth = cellIndex - colCount;
            var cellIndexWest  = cellIndex - 1;
            var cellIndexEast  = cellIndex + 1;

            var distanceNorth = cellCount + 1;
            var distanceSouth = cellCount + 1;
            var distanceEast  = cellCount + 1;
            var distanceWest  = cellCount + 1;

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

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

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

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

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

            targetDistances[cellIndex] = bestDist;
            closed.Set(cellIndex, true);
        }
    }
    // 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, int colCount, NativeArray <int> cellDistances, byte *gridWalls)
    {
        var cellCount = rowCount * colCount;
        var rowStride = ((colCount + 1) / 2);

        for (var i = 0; i < (rowStride * rowCount); i++)
        {
            targetDirections[i] = 0;
        }

        for (var cellIndex = 0; cellIndex < cellCount; cellIndex++)
        {
            var y            = cellIndex / colCount;
            var x            = cellIndex - (y * colCount);
            var cellPosition = new CartesianGridCoordinates {
                x = (short)x, y = (short)y
            };

            var validDirections = CartesianGridMovement.ValidDirections(cellPosition, rowCount, colCount, gridWalls);
            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 = cellIndex + colCount;
            var cellIndexSouth = cellIndex - colCount;
            var cellIndexWest  = cellIndex - 1;
            var cellIndexEast  = cellIndex + 1;

            var distanceNorth = cellCount + 1;
            var distanceSouth = cellCount + 1;
            var distanceEast  = cellCount + 1;
            var distanceWest  = cellCount + 1;

            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 <= cellCount))
            {
                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 = (y * rowStride) + (x / 2);

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