/// <summary> /// Gets the chunk indices within the given bounds. /// </summary> /// <param name="bounds">The bounds.</param> /// <returns>The chunk indices.</returns> public static Vector2I[] GetChunks(RectangleI bounds) { var chunks = new Vector2I[bounds.Width * bounds.Height]; int i = 0; for (int x = bounds.X; x < bounds.X + bounds.Width; x++) { for (int y = bounds.Y; y > bounds.Y - bounds.Height; y--) { chunks[i++] = new Vector2I(x, y); } } return chunks; }
/// <summary> /// Dig a circle in the terrain with the given origin and radius. /// </summary> /// <param name="origin">The origin.</param> /// <param name="radius">The radius.</param> public void DigCircle(Vector2 origin, float radius) { Vector2I originI = new Vector2I((int)Math.Round(origin.x), (int)Math.Round(origin.y)); int radiusI = (int)radius; Vector2I chunk = Metrics.ChunkIndex(originI.X, originI.Y); // Get the affected chunks var worldBounds = new RectangleI( (int)(origin.x - radius - 0.5f) - 1, (int)(origin.y + radius + 0.5f) + 1, (int)((radius * 2) + 0.5f) + 3, (int)((radius * 2) + 0.5f) + 3); RectangleI chunkBounds = Metrics.WorldToChunk(worldBounds); Vector2I[] chunks = TerrainChunk.GetChunks(chunkBounds); SynchronisedUpdate toSync = chunks.Length > 1 ? new SynchronisedUpdate(chunks) : null; // Enqueue the job JobSystem.Instance.Scheduler.BeginEnqueueChunks(); try { if (JobSystem.Instance.Scheduler.ForAllChunks( chunks, (q) => q.State.CanDigCircle(chunk, originI, radiusI), MissingQueue.Skip)) { JobSystem.Instance.Scheduler.EnqueueChunks( () => this.DigCircleJob(origin, radius), (q) => q.State.ReserveDigCircle(chunk, originI, radiusI, toSync), (q) => q.State.UnreserveDigCircle(chunk, originI, radiusI), false, chunks); } } finally { JobSystem.Instance.Scheduler.EndEnqueueChunks(); } }
/// <summary> /// Gets the chunks that are current active. /// </summary> /// <returns>The active chunks.</returns> private HashSet<Vector2I> GetActiveChunks() { // bool indicates if the chunk is required or if the loading can be deferred to a background thread var activeChunks = new HashSet<Vector2I>(); // Get the bounds within the camera RectangleI cameraPointBounds = this.cCameraBounds.GetBounds(); Vector2I top = Metrics.ChunkIndex(cameraPointBounds.X, cameraPointBounds.Y); Vector2I bottom = Metrics.ChunkIndex(cameraPointBounds.Right - 1, cameraPointBounds.Bottom - 1); var cameraChunkBounds = new RectangleI( top.X - 2 - this.DistanceChunkBeginLoad, top.Y + 2 + this.DistanceChunkBeginLoad, bottom.X - top.X + 4 + (this.DistanceChunkBeginLoad * 2), top.Y - bottom.Y + 4 + (this.DistanceChunkBeginLoad * 2)); // Add the chunks that the camera is pointing directly at. These are required to be loaded this frame this.PopulateActiveChunks(activeChunks, cameraChunkBounds); // Add all other chunks which contain significant actors foreach (ActorBoundsComponent actor in GameObject.FindObjectsOfType(typeof(ActorBoundsComponent))) { this.PopulateActiveChunks(activeChunks, Metrics.WorldToChunk(actor.GetBounds())); } return activeChunks; }
/// <summary> /// Adds each chunk within the given world bounds to the given dictionary. /// </summary> /// <param name="chunks">The dictionary being populated.</param> /// <param name="chunkBounds">The bounds in chunk coordinates.</param> private void PopulateActiveChunks(HashSet<Vector2I> chunks, RectangleI chunkBounds) { for (int x = chunkBounds.X; x < chunkBounds.Right; x++) { for (int y = chunkBounds.Y; y > chunkBounds.Bottom; y--) { chunks.Add(new Vector2I(x, y)); } } }
/// <summary> /// Convert the world bounds into chunk bounds. /// </summary> /// <param name="worldBounds">The bounds.</param> /// <returns>The bounds in chunk coordinates</returns> public static RectangleI WorldToChunk(RectangleI worldBounds) { Vector2I top = Metrics.ChunkIndex(worldBounds.X, worldBounds.Y); Vector2I bottom = Metrics.ChunkIndex(worldBounds.Right - 1, worldBounds.Bottom - 1); return new RectangleI( top.X, top.Y, bottom.X - top.X + 1, top.Y - bottom.Y + 1); }