示例#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);
    }
示例#2
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));
            }
示例#3
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);
            }
示例#4
0
    protected override JobHandle OnUpdate(JobHandle lastJobHandle)
    {
        // Get component data from the GridPlane
        var cartesianGridCube = GetSingleton <CartesianGridOnCube>();
        var rowCount          = cartesianGridCube.Blob.Value.RowCount;
        var targetDirectionsBufferCapacity = 6 * (((rowCount + 1) / 2) * rowCount);
        var targetDistancesBufferCapacity  = 6 * (rowCount * rowCount);
        var gridWalls        = (byte *)cartesianGridCube.Blob.Value.Walls.GetUnsafePtr();
        var faceLocalToLocal = (float4x4 *)cartesianGridCube.Blob.Value.FaceLocalToLocal.GetUnsafePtr();

        Entities
        .WithName("InitializeTargets")
        .WithAll <CartesianGridTarget>()
        .WithAll <CartesianGridOnCubeFace>()
        .WithNone <CartesianGridTargetDirection>()
        .WithNone <CartesianGridTargetDistance>()
        .WithStructuralChanges()
        .ForEach((Entity entity) =>
        {
            var directionBuffer = EntityManager.AddBuffer <CartesianGridTargetDirection>(entity);
            directionBuffer.ResizeUninitialized(targetDirectionsBufferCapacity);

            var distanceBuffer = EntityManager.AddBuffer <CartesianGridTargetDistance>(entity);
            distanceBuffer.ResizeUninitialized(targetDistancesBufferCapacity);
        }).Run();

        // Rebuild all the paths to the target *only* when the target's grid position changes.
        Entities
        .WithName("UpdateTargetPaths")
        .WithNativeDisableUnsafePtrRestriction(faceLocalToLocal)
        .WithAll <CartesianGridTarget>()
        .ForEach((Entity entity, ref CartesianGridTargetCoordinates prevTargetPosition, in CartesianGridOnCubeFace cubeFace, in CartesianGridCoordinates targetPosition, in DynamicBuffer <CartesianGridTargetDirection> targetDirections, in DynamicBuffer <CartesianGridTargetDistance> targetDistances) =>
        {
            if (prevTargetPosition.Equals(targetPosition))
            {
                return;
            }

            if (targetPosition.OnGrid(rowCount, rowCount))
            {
                prevTargetPosition = new CartesianGridTargetCoordinates(targetPosition);
                CartesianGridOnCubeShortestPath.CalculateShortestPathsToTarget(targetDirections.Reinterpret <byte>().AsNativeArray(), targetDistances.Reinterpret <int>().AsNativeArray(), rowCount, targetPosition, cubeFace, gridWalls, faceLocalToLocal);
            }
        }).Run();
示例#5
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);
    }