From 8db3d0a27c1d620f0f7ebf549dcd660b5f4ab9d6 Mon Sep 17 00:00:00 2001 From: Konstantin Dyachenko Date: Tue, 7 Apr 2026 04:34:42 +0700 Subject: [PATCH] [Fix] Chunk Unload --- .../VoxelWorld/Runtime/VoxelWorldGenerator.cs | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs b/Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs index 467ed262..d3c37bae 100644 --- a/Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs +++ b/Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs @@ -281,9 +281,11 @@ namespace InfiniteWorld.VoxelWorld continue; } + Vector2Int regionCoord = ChunkToRegion(coord); MarkRegionDirty(coord); chunks.Remove(coord); runtime.Dispose(); + TryDisposeRegionIfEmpty(regionCoord); QueueNeighborRefresh(coord); } } @@ -510,7 +512,16 @@ namespace InfiniteWorld.VoxelWorld continue; } - RegionBuildResult result = await UniTask.RunOnThreadPool(() => BuildRegionBuffers(request)); + RegionBuildResult result = request.Chunks == null || request.Chunks.Length == 0 + ? RegionBuildResult.CreateEmpty(request.RegionCoord, request.Version, request.Session) + : await UniTask.RunOnThreadPool(() => BuildRegionBuffers(request)); + + if (!IsRegionBuildStillCurrent(result.RegionCoord, result.Version, result.Session)) + { + buildsThisFrame++; + continue; + } + lock (regionBuildLock) { completedRegionBuilds.Enqueue(result); @@ -568,6 +579,53 @@ namespace InfiniteWorld.VoxelWorld return true; } + private bool IsRegionBuildStillCurrent(Vector2Int regionCoord, int version, int session) + { + if (session != generationSession) + { + return false; + } + + regionVersions.TryGetValue(regionCoord, out int currentVersion); + return version == currentVersion; + } + + private void TryDisposeRegionIfEmpty(Vector2Int regionCoord) + { + if (RegionHasVisibleChunks(regionCoord)) + { + return; + } + + if (regions.TryGetValue(regionCoord, out RegionRuntime region)) + { + region.Dispose(); + regions.Remove(regionCoord); + } + + regionVersions.Remove(regionCoord); + } + + private bool RegionHasVisibleChunks(Vector2Int regionCoord) + { + Vector2Int baseChunk = new Vector2Int(regionCoord.x * renderRegionSizeInChunks, regionCoord.y * renderRegionSizeInChunks); + for (int z = 0; z < renderRegionSizeInChunks; z++) + { + for (int x = 0; x < renderRegionSizeInChunks; x++) + { + Vector2Int chunkCoord = new Vector2Int(baseChunk.x + x, baseChunk.y + z); + if (!chunks.TryGetValue(chunkCoord, out ChunkRuntime runtime) || runtime.RenderSnapshot == null || runtime.RenderSnapshot.IsEmpty) + { + continue; + } + + return true; + } + } + + return false; + } + private Vector2Int ChunkToRegion(Vector2Int chunkCoord) { return new Vector2Int(