[Fix] Flatten Terrain
This commit is contained in:
@@ -0,0 +1,232 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace InfiniteWorld.VoxelWorld
|
||||||
|
{
|
||||||
|
public sealed partial class VoxelWorldGenerator
|
||||||
|
{
|
||||||
|
private readonly Dictionary<Vector2Int, List<SpawnedPlacementRuntime>> spawnedPlacementsByChunk = new Dictionary<Vector2Int, List<SpawnedPlacementRuntime>>();
|
||||||
|
private readonly Dictionary<long, SpawnedPlacementRuntime> spawnedPlacementsById = new Dictionary<long, SpawnedPlacementRuntime>();
|
||||||
|
private readonly HashSet<long> disabledSpawnIds = new HashSet<long>();
|
||||||
|
|
||||||
|
private Transform placementRoot;
|
||||||
|
|
||||||
|
public IReadOnlyCollection<long> DisabledSpawnIds => disabledSpawnIds;
|
||||||
|
|
||||||
|
public bool IsSpawnDisabled(long spawnId)
|
||||||
|
{
|
||||||
|
return disabledSpawnIds.Contains(spawnId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DisableSpawn(long spawnId)
|
||||||
|
{
|
||||||
|
if (!disabledSpawnIds.Add(spawnId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spawnedPlacementsById.TryGetValue(spawnId, out SpawnedPlacementRuntime runtime))
|
||||||
|
{
|
||||||
|
RemoveSpawnedPlacement(runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool EnableSpawn(long spawnId)
|
||||||
|
{
|
||||||
|
return disabledSpawnIds.Remove(spawnId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearDisabledSpawns()
|
||||||
|
{
|
||||||
|
disabledSpawnIds.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsurePlacementRoot()
|
||||||
|
{
|
||||||
|
if (placementRoot != null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform existing = transform.Find("WorldPlacements");
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
placementRoot = existing;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject root = new GameObject("WorldPlacements");
|
||||||
|
root.transform.SetParent(transform, false);
|
||||||
|
placementRoot = root.transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TrySpawnChunkPlacements(Vector2Int chunkCoord)
|
||||||
|
{
|
||||||
|
EnsurePlacementRoot();
|
||||||
|
if (placementRoot == null || spawnedPlacementsByChunk.ContainsKey(chunkCoord))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldChunkPlacementPlan plan = GetChunkPlacementPlan(chunkCoord);
|
||||||
|
IReadOnlyList<WorldSpawnPoint> spawnPoints = plan.SpawnPoints;
|
||||||
|
if (spawnPoints == null || spawnPoints.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SpawnedPlacementRuntime> chunkPlacements = new List<SpawnedPlacementRuntime>(spawnPoints.Count);
|
||||||
|
for (int i = 0; i < spawnPoints.Count; i++)
|
||||||
|
{
|
||||||
|
WorldSpawnPoint spawnPoint = spawnPoints[i];
|
||||||
|
if (disabledSpawnIds.Contains(spawnPoint.SpawnId) || spawnedPlacementsById.ContainsKey(spawnPoint.SpawnId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryResolveSpawnPrefab(spawnPoint, out GameObject prefab) || prefab == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject instance = Object.Instantiate(prefab, spawnPoint.Position, spawnPoint.Rotation, placementRoot);
|
||||||
|
instance.name = $"{prefab.name}_{spawnPoint.ChunkCoord.x}_{spawnPoint.ChunkCoord.y}_{spawnPoint.SpawnOrdinalInChunk}";
|
||||||
|
SpawnedPlacementRuntime runtime = new SpawnedPlacementRuntime(spawnPoint.SpawnId, chunkCoord, spawnPoint.CollectionIndex, spawnPoint.EntryIndex, instance);
|
||||||
|
chunkPlacements.Add(runtime);
|
||||||
|
spawnedPlacementsById.Add(runtime.SpawnId, runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunkPlacements.Count > 0)
|
||||||
|
{
|
||||||
|
spawnedPlacementsByChunk[chunkCoord] = chunkPlacements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryResolveSpawnPrefab(WorldSpawnPoint spawnPoint, out GameObject prefab)
|
||||||
|
{
|
||||||
|
prefab = null;
|
||||||
|
if (config == null || config.placementCollections == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spawnPoint.CollectionIndex < 0 || spawnPoint.CollectionIndex >= config.placementCollections.Count)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldPrefabCollection collection = config.placementCollections[spawnPoint.CollectionIndex];
|
||||||
|
if (collection == null || collection.entries == null || spawnPoint.EntryIndex < 0 || spawnPoint.EntryIndex >= collection.entries.Count)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldPrefabEntry entry = collection.entries[spawnPoint.EntryIndex];
|
||||||
|
if (entry == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
prefab = entry.prefab;
|
||||||
|
return prefab != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DespawnChunkPlacements(Vector2Int chunkCoord)
|
||||||
|
{
|
||||||
|
if (!spawnedPlacementsByChunk.TryGetValue(chunkCoord, out List<SpawnedPlacementRuntime> chunkPlacements))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < chunkPlacements.Count; i++)
|
||||||
|
{
|
||||||
|
SpawnedPlacementRuntime runtime = chunkPlacements[i];
|
||||||
|
spawnedPlacementsById.Remove(runtime.SpawnId);
|
||||||
|
DestroyPlacementInstance(runtime.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnedPlacementsByChunk.Remove(chunkCoord);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveSpawnedPlacement(SpawnedPlacementRuntime runtime)
|
||||||
|
{
|
||||||
|
spawnedPlacementsById.Remove(runtime.SpawnId);
|
||||||
|
if (spawnedPlacementsByChunk.TryGetValue(runtime.ChunkCoord, out List<SpawnedPlacementRuntime> chunkPlacements))
|
||||||
|
{
|
||||||
|
chunkPlacements.RemoveAll(item => item.SpawnId == runtime.SpawnId);
|
||||||
|
if (chunkPlacements.Count == 0)
|
||||||
|
{
|
||||||
|
spawnedPlacementsByChunk.Remove(runtime.ChunkCoord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DestroyPlacementInstance(runtime.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CleanupSpawnedPlacements()
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<Vector2Int, List<SpawnedPlacementRuntime>> pair in spawnedPlacementsByChunk)
|
||||||
|
{
|
||||||
|
List<SpawnedPlacementRuntime> chunkPlacements = pair.Value;
|
||||||
|
for (int i = 0; i < chunkPlacements.Count; i++)
|
||||||
|
{
|
||||||
|
DestroyPlacementInstance(chunkPlacements[i].Instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnedPlacementsByChunk.Clear();
|
||||||
|
spawnedPlacementsById.Clear();
|
||||||
|
|
||||||
|
if (placementRoot != null)
|
||||||
|
{
|
||||||
|
if (Application.isPlaying)
|
||||||
|
{
|
||||||
|
Object.Destroy(placementRoot.gameObject);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(placementRoot.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
placementRoot = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DestroyPlacementInstance(GameObject instance)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Application.isPlaying)
|
||||||
|
{
|
||||||
|
Object.Destroy(instance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class SpawnedPlacementRuntime
|
||||||
|
{
|
||||||
|
public SpawnedPlacementRuntime(long spawnId, Vector2Int chunkCoord, int collectionIndex, int entryIndex, GameObject instance)
|
||||||
|
{
|
||||||
|
SpawnId = spawnId;
|
||||||
|
ChunkCoord = chunkCoord;
|
||||||
|
CollectionIndex = collectionIndex;
|
||||||
|
EntryIndex = entryIndex;
|
||||||
|
Instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SpawnId { get; }
|
||||||
|
public Vector2Int ChunkCoord { get; }
|
||||||
|
public int CollectionIndex { get; }
|
||||||
|
public int EntryIndex { get; }
|
||||||
|
public GameObject Instance { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f7d0d16ba43223c41bd21fbe9333d819
|
||||||
@@ -75,6 +75,7 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
EnsureRuntimeData();
|
EnsureRuntimeData();
|
||||||
EnsureChunkRoot();
|
EnsureChunkRoot();
|
||||||
EnsureRegionRoot();
|
EnsureRegionRoot();
|
||||||
|
EnsurePlacementRoot();
|
||||||
TryResolveStreamTarget();
|
TryResolveStreamTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +84,7 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
EnsureRuntimeData();
|
EnsureRuntimeData();
|
||||||
EnsureChunkRoot();
|
EnsureChunkRoot();
|
||||||
EnsureRegionRoot();
|
EnsureRegionRoot();
|
||||||
|
EnsurePlacementRoot();
|
||||||
if (!TryResolveStreamTarget())
|
if (!TryResolveStreamTarget())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -121,6 +123,7 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
CleanupChunks();
|
CleanupChunks();
|
||||||
CleanupRegions();
|
CleanupRegions();
|
||||||
CleanupPlacementPlans();
|
CleanupPlacementPlans();
|
||||||
|
CleanupSpawnedPlacements();
|
||||||
atlas?.Dispose();
|
atlas?.Dispose();
|
||||||
atlas = null;
|
atlas = null;
|
||||||
}
|
}
|
||||||
@@ -290,6 +293,7 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
{
|
{
|
||||||
chunkPlacementPlans.Remove(coord);
|
chunkPlacementPlans.Remove(coord);
|
||||||
}
|
}
|
||||||
|
DespawnChunkPlacements(coord);
|
||||||
runtime.Dispose();
|
runtime.Dispose();
|
||||||
TryDisposeRegionIfEmpty(regionCoord);
|
TryDisposeRegionIfEmpty(regionCoord);
|
||||||
QueueNeighborRefresh(coord);
|
QueueNeighborRefresh(coord);
|
||||||
@@ -804,6 +808,8 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
runtime.State = ChunkState.ReadyToRender;
|
runtime.State = ChunkState.ReadyToRender;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TrySpawnChunkPlacements(result.Coord);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -890,6 +896,7 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
}
|
}
|
||||||
|
|
||||||
chunks.Clear();
|
chunks.Clear();
|
||||||
|
CleanupSpawnedPlacements();
|
||||||
dirtyChunkMeshes.Clear();
|
dirtyChunkMeshes.Clear();
|
||||||
queuedChunkMeshes.Clear();
|
queuedChunkMeshes.Clear();
|
||||||
pendingNeighborRefreshes.Clear();
|
pendingNeighborRefreshes.Clear();
|
||||||
|
|||||||
Reference in New Issue
Block a user