[Add] UniTask
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Tilemaps;
|
||||
|
||||
@@ -33,11 +35,31 @@ namespace InfiniteWorld
|
||||
[SerializeField] private float environmentNoiseScale = 0.19f;
|
||||
[SerializeField] private float environmentThreshold = 0.7f;
|
||||
|
||||
private readonly Dictionary<Vector2Int, GeneratedChunk> chunks = new Dictionary<Vector2Int, GeneratedChunk>();
|
||||
[Header("Streaming")]
|
||||
[SerializeField, Min(1)] private int maxAsyncChunkJobs = 2;
|
||||
[SerializeField, Min(1)] private int maxChunkRendersPerFrame = 1;
|
||||
[SerializeField, Min(0)] private int blockingGenerationRadius = 0;
|
||||
|
||||
private readonly Dictionary<Vector2Int, ChunkRuntime> chunks = new Dictionary<Vector2Int, ChunkRuntime>();
|
||||
private readonly Queue<ChunkBuildResult> completedBuilds = new Queue<ChunkBuildResult>();
|
||||
private readonly object generationLock = new object();
|
||||
private static readonly Vector2Int[] NeighborOffsets =
|
||||
{
|
||||
Vector2Int.up,
|
||||
Vector2Int.right,
|
||||
Vector2Int.down,
|
||||
Vector2Int.left
|
||||
};
|
||||
|
||||
private Grid grid;
|
||||
private Transform chunkRoot;
|
||||
private Vector2Int lastGeneratedCenter = new Vector2Int(int.MinValue, int.MinValue);
|
||||
private WorldAutotileProfile runtimeFallbackProfile;
|
||||
private int activeGenerationJobs;
|
||||
private bool isLoadingPaused;
|
||||
private float pausedTimeScale = 1f;
|
||||
private TileBase cachedGroundTile;
|
||||
private TileBase[] cachedGroundTiles;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -54,13 +76,20 @@ namespace InfiniteWorld
|
||||
}
|
||||
|
||||
Vector2Int playerChunk = WorldToChunk(player.position);
|
||||
if (playerChunk == lastGeneratedCenter)
|
||||
if (playerChunk != lastGeneratedCenter)
|
||||
{
|
||||
return;
|
||||
lastGeneratedCenter = playerChunk;
|
||||
UnloadDistantChunks(playerChunk);
|
||||
}
|
||||
|
||||
lastGeneratedCenter = playerChunk;
|
||||
UpdateActiveChunks(playerChunk);
|
||||
DrainCompletedBuilds(isLoadingPaused ? int.MaxValue : maxChunkRendersPerFrame);
|
||||
ScheduleChunkGeneration(playerChunk);
|
||||
EnsureBlockingChunksLoaded(playerChunk);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
SetLoadingPaused(false);
|
||||
}
|
||||
|
||||
private void EnsureSceneInfrastructure()
|
||||
@@ -111,37 +140,38 @@ namespace InfiniteWorld
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateActiveChunks(Vector2Int centerChunk)
|
||||
private void ScheduleChunkGeneration(Vector2Int centerChunk)
|
||||
{
|
||||
GenerateAround(centerChunk);
|
||||
UnloadDistantChunks(centerChunk);
|
||||
}
|
||||
|
||||
private void GenerateAround(Vector2Int centerChunk)
|
||||
{
|
||||
for (int y = -generationRadius; y <= generationRadius; y++)
|
||||
List<Vector2Int> coords = GetCoordsByPriority(centerChunk, generationRadius);
|
||||
for (int i = 0; i < coords.Count; i++)
|
||||
{
|
||||
for (int x = -generationRadius; x <= generationRadius; x++)
|
||||
Vector2Int coord = coords[i];
|
||||
ChunkRuntime runtime = GetOrCreateChunkRuntime(coord);
|
||||
if (runtime.IsRendered || runtime.HasData || runtime.State == ChunkState.Generating)
|
||||
{
|
||||
Vector2Int coord = new Vector2Int(centerChunk.x + x, centerChunk.y + y);
|
||||
if (chunks.ContainsKey(coord))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GeneratedChunk chunk = CreateChunk(coord);
|
||||
chunks.Add(coord, chunk);
|
||||
BuildChunkData(coord, chunk);
|
||||
RenderChunk(coord);
|
||||
RefreshNeighborBorders(coord);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsWithinRadius(coord, centerChunk, blockingGenerationRadius))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryReserveGenerationSlot())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
runtime.State = ChunkState.Generating;
|
||||
runtime.Version++;
|
||||
GenerateChunkDataAsync(coord, runtime.Version).Forget();
|
||||
}
|
||||
}
|
||||
|
||||
private void UnloadDistantChunks(Vector2Int centerChunk)
|
||||
{
|
||||
List<Vector2Int> coordsToRemove = new List<Vector2Int>();
|
||||
foreach (KeyValuePair<Vector2Int, GeneratedChunk> pair in chunks)
|
||||
foreach (KeyValuePair<Vector2Int, ChunkRuntime> pair in chunks)
|
||||
{
|
||||
if (IsWithinActiveRadius(pair.Key, centerChunk))
|
||||
{
|
||||
@@ -154,21 +184,18 @@ namespace InfiniteWorld
|
||||
for (int i = 0; i < coordsToRemove.Count; i++)
|
||||
{
|
||||
Vector2Int coord = coordsToRemove[i];
|
||||
if (!chunks.TryGetValue(coord, out GeneratedChunk chunk))
|
||||
if (!chunks.TryGetValue(coord, out ChunkRuntime chunk))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
chunks.Remove(coord);
|
||||
if (chunk.Root != null)
|
||||
if (chunk.Chunk.Root != null)
|
||||
{
|
||||
Destroy(chunk.Root.gameObject);
|
||||
Destroy(chunk.Chunk.Root.gameObject);
|
||||
}
|
||||
|
||||
RefreshNeighborBorders(coord + Vector2Int.up);
|
||||
RefreshNeighborBorders(coord + Vector2Int.right);
|
||||
RefreshNeighborBorders(coord + Vector2Int.down);
|
||||
RefreshNeighborBorders(coord + Vector2Int.left);
|
||||
RefreshNeighborBorders(coord);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +216,7 @@ namespace InfiniteWorld
|
||||
Tilemap walls = CreateTilemap("Walls", chunkObject.transform, 1, true);
|
||||
Tilemap environment = CreateTilemap("Environment", chunkObject.transform, 2, false);
|
||||
|
||||
return new GeneratedChunk(chunkObject.transform, ground, walls, environment, new bool[chunkSize, chunkSize], new bool[chunkSize, chunkSize]);
|
||||
return new GeneratedChunk(chunkObject.transform, ground, walls, environment);
|
||||
}
|
||||
|
||||
private Tilemap CreateTilemap(string name, Transform parent, int sortingOrder, bool addCollision)
|
||||
@@ -227,7 +254,7 @@ namespace InfiniteWorld
|
||||
return tilemapObject.GetComponent<Tilemap>();
|
||||
}
|
||||
|
||||
private void BuildChunkData(Vector2Int coord, GeneratedChunk chunk)
|
||||
private ChunkBuildResult GenerateChunkData(Vector2Int coord, int version)
|
||||
{
|
||||
int margin = Mathf.Max(2, smoothingPasses + 1);
|
||||
int sampleSize = chunkSize + margin * 2;
|
||||
@@ -249,15 +276,17 @@ namespace InfiniteWorld
|
||||
sampled = SmoothSampledMask(sampled);
|
||||
}
|
||||
|
||||
bool[,] wallMask = new bool[chunkSize, chunkSize];
|
||||
for (int y = 0; y < chunkSize; y++)
|
||||
{
|
||||
for (int x = 0; x < chunkSize; x++)
|
||||
{
|
||||
chunk.WallMask[x, y] = sampled[x + margin, y + margin];
|
||||
wallMask[x, y] = sampled[x + margin, y + margin];
|
||||
}
|
||||
}
|
||||
|
||||
BuildEnvironment(coord, chunk);
|
||||
bool[,] environmentMask = BuildEnvironment(coord, wallMask);
|
||||
return new ChunkBuildResult(coord, wallMask, environmentMask, version);
|
||||
}
|
||||
|
||||
private bool[,] SmoothSampledMask(bool[,] source)
|
||||
@@ -346,29 +375,30 @@ namespace InfiniteWorld
|
||||
return warped <= passThreshold + passFeather * detail;
|
||||
}
|
||||
|
||||
private void BuildEnvironment(Vector2Int coord, GeneratedChunk chunk)
|
||||
private bool[,] BuildEnvironment(Vector2Int coord, bool[,] wallMask)
|
||||
{
|
||||
bool[,] environmentMask = new bool[chunkSize, chunkSize];
|
||||
for (int y = 0; y < chunkSize; y++)
|
||||
{
|
||||
for (int x = 0; x < chunkSize; x++)
|
||||
{
|
||||
if (chunk.WallMask[x, y])
|
||||
if (wallMask[x, y])
|
||||
{
|
||||
chunk.EnvironmentMask[x, y] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (HasAdjacentOpenTiles(chunk.WallMask, x, y, 1))
|
||||
if (HasAdjacentOpenTiles(wallMask, x, y, 1))
|
||||
{
|
||||
chunk.EnvironmentMask[x, y] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2Int worldCell = ChunkToWorldCell(coord, x, y);
|
||||
float noise = Mathf.PerlinNoise((worldCell.x + seed * 0.53f) * environmentNoiseScale, (worldCell.y - seed * 0.61f) * environmentNoiseScale);
|
||||
chunk.EnvironmentMask[x, y] = noise >= environmentThreshold;
|
||||
environmentMask[x, y] = noise >= environmentThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
return environmentMask;
|
||||
}
|
||||
|
||||
private bool HasAdjacentOpenTiles(bool[,] wallMask, int x, int y, int radius)
|
||||
@@ -396,7 +426,7 @@ namespace InfiniteWorld
|
||||
|
||||
private void RenderChunk(Vector2Int coord)
|
||||
{
|
||||
if (!chunks.TryGetValue(coord, out GeneratedChunk chunk))
|
||||
if (!chunks.TryGetValue(coord, out ChunkRuntime runtime) || !runtime.HasData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -407,41 +437,51 @@ namespace InfiniteWorld
|
||||
return;
|
||||
}
|
||||
|
||||
chunk.Ground.ClearAllTiles();
|
||||
chunk.Walls.ClearAllTiles();
|
||||
chunk.Environment.ClearAllTiles();
|
||||
if (runtime.Chunk.Root == null)
|
||||
{
|
||||
runtime.Chunk = CreateChunk(coord);
|
||||
}
|
||||
|
||||
GeneratedChunk chunk = runtime.Chunk;
|
||||
int tileCount = chunkSize * chunkSize;
|
||||
TileBase[] groundTiles = GetGroundTiles(activeProfile.baseGroundTile, tileCount);
|
||||
TileBase[] wallTiles = new TileBase[tileCount];
|
||||
TileBase[] environmentTiles = new TileBase[tileCount];
|
||||
|
||||
int index = 0;
|
||||
for (int y = 0; y < chunkSize; y++)
|
||||
{
|
||||
for (int x = 0; x < chunkSize; x++)
|
||||
{
|
||||
Vector3Int localCell = new Vector3Int(x, y, 0);
|
||||
chunk.Ground.SetTile(localCell, activeProfile.baseGroundTile);
|
||||
|
||||
if (chunk.WallMask[x, y])
|
||||
if (runtime.WallMask[x, y])
|
||||
{
|
||||
Vector2Int worldCell = ChunkToWorldCell(coord, x, y);
|
||||
AutoTileShape shape = ResolveWallShape(worldCell);
|
||||
chunk.Walls.SetTile(localCell, activeProfile.GetWallTile(shape));
|
||||
wallTiles[index] = activeProfile.GetWallTile(shape);
|
||||
}
|
||||
else if (chunk.EnvironmentMask[x, y])
|
||||
else if (runtime.EnvironmentMask[x, y])
|
||||
{
|
||||
TileBase tile = PickEnvironmentTile(ChunkToWorldCell(coord, x, y), activeProfile);
|
||||
if (tile != null)
|
||||
{
|
||||
chunk.Environment.SetTile(localCell, tile);
|
||||
}
|
||||
environmentTiles[index] = tile;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
BoundsInt bounds = new BoundsInt(0, 0, 0, chunkSize, chunkSize, 1);
|
||||
chunk.Ground.SetTilesBlock(bounds, groundTiles);
|
||||
chunk.Walls.SetTilesBlock(bounds, wallTiles);
|
||||
chunk.Environment.SetTilesBlock(bounds, environmentTiles);
|
||||
runtime.State = ChunkState.Rendered;
|
||||
}
|
||||
|
||||
private void RefreshNeighborBorders(Vector2Int coord)
|
||||
{
|
||||
RenderChunk(coord + Vector2Int.up);
|
||||
RenderChunk(coord + Vector2Int.right);
|
||||
RenderChunk(coord + Vector2Int.down);
|
||||
RenderChunk(coord + Vector2Int.left);
|
||||
for (int i = 0; i < NeighborOffsets.Length; i++)
|
||||
{
|
||||
RenderChunk(coord + NeighborOffsets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private AutoTileShape ResolveWallShape(Vector2Int worldCell)
|
||||
@@ -521,7 +561,7 @@ namespace InfiniteWorld
|
||||
private bool HasWallAt(Vector2Int worldCell)
|
||||
{
|
||||
Vector2Int coord = new Vector2Int(Mathf.FloorToInt(worldCell.x / (float)chunkSize), Mathf.FloorToInt(worldCell.y / (float)chunkSize));
|
||||
if (!chunks.TryGetValue(coord, out GeneratedChunk chunk))
|
||||
if (!chunks.TryGetValue(coord, out ChunkRuntime runtime) || !runtime.HasData)
|
||||
{
|
||||
return SampleRock(worldCell);
|
||||
}
|
||||
@@ -533,7 +573,7 @@ namespace InfiniteWorld
|
||||
return SampleRock(worldCell);
|
||||
}
|
||||
|
||||
return chunk.WallMask[localX, localY];
|
||||
return runtime.WallMask[localX, localY];
|
||||
}
|
||||
|
||||
private TileBase PickEnvironmentTile(Vector2Int worldCell, WorldAutotileProfile activeProfile)
|
||||
@@ -606,24 +646,281 @@ namespace InfiniteWorld
|
||||
return (Hash(x, y, seed) & int.MaxValue) / (float)int.MaxValue;
|
||||
}
|
||||
|
||||
private async UniTaskVoid GenerateChunkDataAsync(Vector2Int coord, int version)
|
||||
{
|
||||
try
|
||||
{
|
||||
ChunkBuildResult result = await UniTask.RunOnThreadPool(() => GenerateChunkData(coord, version));
|
||||
lock (generationLock)
|
||||
{
|
||||
completedBuilds.Enqueue(result);
|
||||
activeGenerationJobs = Mathf.Max(0, activeGenerationJobs - 1);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
lock (generationLock)
|
||||
{
|
||||
activeGenerationJobs = Mathf.Max(0, activeGenerationJobs - 1);
|
||||
}
|
||||
|
||||
Debug.LogException(exception, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrainCompletedBuilds(int maxRenders)
|
||||
{
|
||||
int renders = 0;
|
||||
while (renders < maxRenders)
|
||||
{
|
||||
ChunkBuildResult result;
|
||||
lock (generationLock)
|
||||
{
|
||||
if (completedBuilds.Count == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
result = completedBuilds.Dequeue();
|
||||
}
|
||||
|
||||
if (!ApplyBuildResult(result))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RenderChunk(result.Coord);
|
||||
RefreshNeighborBorders(result.Coord);
|
||||
renders++;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ApplyBuildResult(ChunkBuildResult result)
|
||||
{
|
||||
if (!chunks.TryGetValue(result.Coord, out ChunkRuntime runtime))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result.Version != runtime.Version)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
runtime.WallMask = result.WallMask;
|
||||
runtime.EnvironmentMask = result.EnvironmentMask;
|
||||
if (!runtime.IsRendered)
|
||||
{
|
||||
runtime.State = ChunkState.ReadyToRender;
|
||||
}
|
||||
|
||||
return !runtime.IsRendered;
|
||||
}
|
||||
|
||||
private void EnsureBlockingChunksLoaded(Vector2Int centerChunk)
|
||||
{
|
||||
List<Vector2Int> requiredCoords = GetCoordsByPriority(centerChunk, blockingGenerationRadius);
|
||||
bool isMissingRequiredChunk = false;
|
||||
|
||||
for (int i = 0; i < requiredCoords.Count; i++)
|
||||
{
|
||||
Vector2Int coord = requiredCoords[i];
|
||||
ChunkRuntime runtime = GetOrCreateChunkRuntime(coord);
|
||||
if (runtime.IsRendered)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
isMissingRequiredChunk = true;
|
||||
if (runtime.State == ChunkState.Generating)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
runtime.Version++;
|
||||
runtime.State = ChunkState.SyncBuilding;
|
||||
ApplyBuildResult(GenerateChunkData(coord, runtime.Version));
|
||||
RenderChunk(coord);
|
||||
RefreshNeighborBorders(coord);
|
||||
}
|
||||
|
||||
DrainCompletedBuilds(isMissingRequiredChunk ? int.MaxValue : maxChunkRendersPerFrame);
|
||||
SetLoadingPaused(HasMissingRequiredChunks(requiredCoords));
|
||||
}
|
||||
|
||||
private bool HasMissingRequiredChunks(List<Vector2Int> requiredCoords)
|
||||
{
|
||||
for (int i = 0; i < requiredCoords.Count; i++)
|
||||
{
|
||||
if (!chunks.TryGetValue(requiredCoords[i], out ChunkRuntime runtime) || !runtime.IsRendered)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryReserveGenerationSlot()
|
||||
{
|
||||
lock (generationLock)
|
||||
{
|
||||
if (activeGenerationJobs >= maxAsyncChunkJobs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
activeGenerationJobs++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Vector2Int> GetCoordsByPriority(Vector2Int centerChunk, int radius)
|
||||
{
|
||||
List<Vector2Int> coords = new List<Vector2Int>((radius * 2 + 1) * (radius * 2 + 1));
|
||||
for (int y = -radius; y <= radius; y++)
|
||||
{
|
||||
for (int x = -radius; x <= radius; x++)
|
||||
{
|
||||
coords.Add(new Vector2Int(centerChunk.x + x, centerChunk.y + y));
|
||||
}
|
||||
}
|
||||
|
||||
coords.Sort((left, right) => CompareChunkPriority(centerChunk, left, right));
|
||||
return coords;
|
||||
}
|
||||
|
||||
private static int CompareChunkPriority(Vector2Int centerChunk, Vector2Int left, Vector2Int right)
|
||||
{
|
||||
int leftDx = Mathf.Abs(left.x - centerChunk.x);
|
||||
int leftDy = Mathf.Abs(left.y - centerChunk.y);
|
||||
int rightDx = Mathf.Abs(right.x - centerChunk.x);
|
||||
int rightDy = Mathf.Abs(right.y - centerChunk.y);
|
||||
|
||||
int leftChebyshev = Mathf.Max(leftDx, leftDy);
|
||||
int rightChebyshev = Mathf.Max(rightDx, rightDy);
|
||||
int chebyshevCompare = leftChebyshev.CompareTo(rightChebyshev);
|
||||
if (chebyshevCompare != 0)
|
||||
{
|
||||
return chebyshevCompare;
|
||||
}
|
||||
|
||||
int leftDistance = leftDx * leftDx + leftDy * leftDy;
|
||||
int rightDistance = rightDx * rightDx + rightDy * rightDy;
|
||||
int distanceCompare = leftDistance.CompareTo(rightDistance);
|
||||
if (distanceCompare != 0)
|
||||
{
|
||||
return distanceCompare;
|
||||
}
|
||||
|
||||
int yCompare = left.y.CompareTo(right.y);
|
||||
return yCompare != 0 ? yCompare : left.x.CompareTo(right.x);
|
||||
}
|
||||
|
||||
private ChunkRuntime GetOrCreateChunkRuntime(Vector2Int coord)
|
||||
{
|
||||
if (!chunks.TryGetValue(coord, out ChunkRuntime runtime))
|
||||
{
|
||||
runtime = new ChunkRuntime();
|
||||
chunks.Add(coord, runtime);
|
||||
}
|
||||
|
||||
return runtime;
|
||||
}
|
||||
|
||||
private bool IsWithinRadius(Vector2Int coord, Vector2Int centerChunk, int radius)
|
||||
{
|
||||
int dx = Mathf.Abs(coord.x - centerChunk.x);
|
||||
int dy = Mathf.Abs(coord.y - centerChunk.y);
|
||||
return dx <= radius && dy <= radius;
|
||||
}
|
||||
|
||||
private TileBase[] GetGroundTiles(TileBase groundTile, int tileCount)
|
||||
{
|
||||
if (cachedGroundTiles == null || cachedGroundTiles.Length != tileCount || cachedGroundTile != groundTile)
|
||||
{
|
||||
cachedGroundTiles = new TileBase[tileCount];
|
||||
cachedGroundTile = groundTile;
|
||||
if (groundTile != null)
|
||||
{
|
||||
Array.Fill(cachedGroundTiles, groundTile);
|
||||
}
|
||||
}
|
||||
|
||||
return cachedGroundTiles;
|
||||
}
|
||||
|
||||
private void SetLoadingPaused(bool pause)
|
||||
{
|
||||
if (pause == isLoadingPaused)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pause)
|
||||
{
|
||||
pausedTimeScale = Time.timeScale > 0f ? Time.timeScale : 1f;
|
||||
Time.timeScale = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
Time.timeScale = pausedTimeScale;
|
||||
}
|
||||
|
||||
isLoadingPaused = pause;
|
||||
}
|
||||
|
||||
private readonly struct GeneratedChunk
|
||||
{
|
||||
public GeneratedChunk(Transform root, Tilemap ground, Tilemap walls, Tilemap environment, bool[,] wallMask, bool[,] environmentMask)
|
||||
public GeneratedChunk(Transform root, Tilemap ground, Tilemap walls, Tilemap environment)
|
||||
{
|
||||
Root = root;
|
||||
Ground = ground;
|
||||
Walls = walls;
|
||||
Environment = environment;
|
||||
WallMask = wallMask;
|
||||
EnvironmentMask = environmentMask;
|
||||
}
|
||||
|
||||
public Transform Root { get; }
|
||||
public Tilemap Ground { get; }
|
||||
public Tilemap Walls { get; }
|
||||
public Tilemap Environment { get; }
|
||||
}
|
||||
|
||||
private sealed class ChunkRuntime
|
||||
{
|
||||
public GeneratedChunk Chunk;
|
||||
public bool[,] WallMask;
|
||||
public bool[,] EnvironmentMask;
|
||||
public ChunkState State;
|
||||
public int Version;
|
||||
|
||||
public bool HasData => WallMask != null && EnvironmentMask != null;
|
||||
public bool IsRendered => State == ChunkState.Rendered && Chunk.Root != null;
|
||||
}
|
||||
|
||||
private readonly struct ChunkBuildResult
|
||||
{
|
||||
public ChunkBuildResult(Vector2Int coord, bool[,] wallMask, bool[,] environmentMask, int version)
|
||||
{
|
||||
Coord = coord;
|
||||
WallMask = wallMask;
|
||||
EnvironmentMask = environmentMask;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public Vector2Int Coord { get; }
|
||||
public bool[,] WallMask { get; }
|
||||
public bool[,] EnvironmentMask { get; }
|
||||
public int Version { get; }
|
||||
}
|
||||
|
||||
private enum ChunkState
|
||||
{
|
||||
None,
|
||||
Generating,
|
||||
SyncBuilding,
|
||||
ReadyToRender,
|
||||
Rendered
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user