[Add] TextMeshPro & Chunk maker

This commit is contained in:
2026-03-30 06:16:25 +07:00
parent e2dab2208a
commit 0b06ff5a06
103 changed files with 70513 additions and 67 deletions
@@ -35,6 +35,10 @@ namespace InfiniteWorld
[SerializeField] private float environmentNoiseScale = 0.19f;
[SerializeField] private float environmentThreshold = 0.7f;
[Header("Random Objects")]
[SerializeField, Range(0f, 1f)] private float randomPrefabChance = 0.06f;
[SerializeField] private float randomPrefabZOffset = -0.1f;
[Header("Streaming")]
[SerializeField, Min(1)] private int maxAsyncChunkJobs = 2;
[SerializeField, Min(1)] private int maxChunkRendersPerFrame = 1;
@@ -92,6 +96,36 @@ namespace InfiniteWorld
SetLoadingPaused(false);
}
public bool UsesProfile(WorldAutotileProfile candidateProfile)
{
return profile == candidateProfile;
}
public void EditorRefreshFromProfile()
{
EnsureSceneInfrastructure();
EnsureRuntimeData();
cachedGroundTile = null;
cachedGroundTiles = null;
List<Vector2Int> coords = new List<Vector2Int>(chunks.Keys);
for (int i = 0; i < coords.Count; i++)
{
if (!chunks.TryGetValue(coords[i], out ChunkRuntime runtime) || !runtime.HasData)
{
continue;
}
runtime.State = ChunkState.ReadyToRender;
RenderChunk(coords[i]);
}
for (int i = 0; i < coords.Count; i++)
{
RefreshNeighborBorders(coords[i]);
}
}
private void EnsureSceneInfrastructure()
{
grid = GetComponentInChildren<Grid>();
@@ -120,7 +154,10 @@ namespace InfiniteWorld
if (profile == null || !profile.HasAnyAssignedTiles())
{
runtimeFallbackProfile = RuntimeWorldProfileFactory.CreateFallbackProfile();
return;
}
runtimeFallbackProfile = null;
}
private bool TryFindPlayer()
@@ -215,8 +252,10 @@ namespace InfiniteWorld
Tilemap ground = CreateTilemap("Ground", chunkObject.transform, 0, false);
Tilemap walls = CreateTilemap("Walls", chunkObject.transform, 1, true);
Tilemap environment = CreateTilemap("Environment", chunkObject.transform, 2, false);
Transform objectsRoot = new GameObject("Objects").transform;
objectsRoot.SetParent(chunkObject.transform, false);
return new GeneratedChunk(chunkObject.transform, ground, walls, environment);
return new GeneratedChunk(chunkObject.transform, ground, walls, environment, objectsRoot);
}
private Tilemap CreateTilemap(string name, Transform parent, int sortingOrder, bool addCollision)
@@ -473,6 +512,7 @@ namespace InfiniteWorld
chunk.Ground.SetTilesBlock(bounds, groundTiles);
chunk.Walls.SetTilesBlock(bounds, wallTiles);
chunk.Environment.SetTilesBlock(bounds, environmentTiles);
RenderRandomPrefabs(coord, runtime, activeProfile);
runtime.State = ChunkState.Rendered;
}
@@ -620,6 +660,102 @@ namespace InfiniteWorld
return activeProfile.environmentTiles[0].tile;
}
private void RenderRandomPrefabs(Vector2Int coord, ChunkRuntime runtime, WorldAutotileProfile activeProfile)
{
if (runtime.Chunk.ObjectsRoot == null)
{
return;
}
ClearSpawnedObjects(runtime.Chunk.ObjectsRoot);
if (randomPrefabChance <= 0f || activeProfile.randomPrefabs == null || activeProfile.randomPrefabs.Count == 0)
{
return;
}
for (int y = 0; y < chunkSize; y++)
{
for (int x = 0; x < chunkSize; x++)
{
if (runtime.WallMask[x, y] || runtime.EnvironmentMask[x, y])
{
continue;
}
Vector2Int worldCell = ChunkToWorldCell(coord, x, y);
if (Hash01(worldCell.x, worldCell.y, seed + 1103) > randomPrefabChance)
{
continue;
}
GameObject prefab = PickRandomPrefab(worldCell, activeProfile);
if (prefab == null)
{
continue;
}
GameObject instance = Instantiate(prefab, runtime.Chunk.ObjectsRoot);
instance.transform.localPosition = new Vector3(x + 0.5f, y + 0.5f, randomPrefabZOffset);
instance.transform.localRotation = Quaternion.identity;
instance.name = prefab.name;
}
}
}
private GameObject PickRandomPrefab(Vector2Int worldCell, WorldAutotileProfile activeProfile)
{
float total = 0f;
for (int i = 0; i < activeProfile.randomPrefabs.Count; i++)
{
RandomPrefabEntry entry = activeProfile.randomPrefabs[i];
if (entry != null && entry.prefab != null)
{
total += Mathf.Max(0.01f, entry.weight);
}
}
if (total <= 0f)
{
return null;
}
float selector = Hash01(worldCell.x, worldCell.y, seed + 1409) * total;
float cumulative = 0f;
for (int i = 0; i < activeProfile.randomPrefabs.Count; i++)
{
RandomPrefabEntry entry = activeProfile.randomPrefabs[i];
if (entry == null || entry.prefab == null)
{
continue;
}
cumulative += Mathf.Max(0.01f, entry.weight);
if (selector <= cumulative)
{
return entry.prefab;
}
}
for (int i = 0; i < activeProfile.randomPrefabs.Count; i++)
{
RandomPrefabEntry entry = activeProfile.randomPrefabs[i];
if (entry != null && entry.prefab != null)
{
return entry.prefab;
}
}
return null;
}
private static void ClearSpawnedObjects(Transform root)
{
for (int i = root.childCount - 1; i >= 0; i--)
{
UnityEngine.Object.Destroy(root.GetChild(i).gameObject);
}
}
private Vector2Int WorldToChunk(Vector3 position)
{
return new Vector2Int(
@@ -872,18 +1008,20 @@ namespace InfiniteWorld
private readonly struct GeneratedChunk
{
public GeneratedChunk(Transform root, Tilemap ground, Tilemap walls, Tilemap environment)
public GeneratedChunk(Transform root, Tilemap ground, Tilemap walls, Tilemap environment, Transform objectsRoot)
{
Root = root;
Ground = ground;
Walls = walls;
Environment = environment;
ObjectsRoot = objectsRoot;
}
public Transform Root { get; }
public Tilemap Ground { get; }
public Tilemap Walls { get; }
public Tilemap Environment { get; }
public Transform ObjectsRoot { get; }
}
private sealed class ChunkRuntime