protected override JobHandle OnUpdate(JobHandle lastJobHandle) { // Get component data from the GridPlane var cartesianGridPlane = GetSingleton <CartesianGridOnPlane>(); var rowCount = cartesianGridPlane.Blob.Value.RowCount; var colCount = cartesianGridPlane.Blob.Value.ColCount; var targetDirectionsBufferCapacity = ((colCount + 1) / 2) * rowCount; var gridWalls = (byte *)cartesianGridPlane.Blob.Value.Walls.GetUnsafePtr(); // Initialize the buffer for the target paths with size appropriate to grid. Entities .WithName("InitializeTargets") .WithAll <CartesianGridTarget>() .WithNone <CartesianGridTargetDirection>() .WithStructuralChanges() .ForEach((Entity entity) => { var buffer = EntityManager.AddBuffer <CartesianGridTargetDirection>(entity); buffer.ResizeUninitialized(targetDirectionsBufferCapacity); }).Run(); // Rebuild all the paths to the target *only* when the target's grid position changes. Entities .WithName("UpdateTargetPaths") .WithAll <CartesianGridTarget>() .ForEach((Entity entity, ref CartesianGridTargetCoordinates prevTargetPosition, in CartesianGridCoordinates targetPosition, in DynamicBuffer <CartesianGridTargetDirection> targetDirections) => { if (prevTargetPosition.Equals(targetPosition)) { return; } prevTargetPosition = new CartesianGridTargetCoordinates(targetPosition); CartesianGridOnPlaneShortestPath.CalculateShortestPathsToTarget(targetDirections.Reinterpret <byte>().AsNativeArray(), rowCount, colCount, targetPosition, gridWalls); }).Run();
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); }
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)); }
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); }