bloak
This commit is contained in:
140
Assets/Blocks/Scripts/WorldManager.cs
Normal file
140
Assets/Blocks/Scripts/WorldManager.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Синглтон. Управляет всеми чанками.
|
||||
/// Точка входа для постановки/разрушения блоков.
|
||||
/// </summary>
|
||||
public class WorldManager : MonoBehaviour
|
||||
{
|
||||
public static WorldManager Instance { get; private set; }
|
||||
|
||||
[Header("References")]
|
||||
public BlockRegistry registry; // назначается автоматически если null
|
||||
|
||||
[Header("Chunk Prefab")]
|
||||
[Tooltip("Можно оставить пустым — создаётся из кода")]
|
||||
public GameObject chunkPrefab;
|
||||
|
||||
// chunkPos (в чанковых координатах) → Chunk
|
||||
private readonly Dictionary<Vector3Int, Chunk> _chunks = new();
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (Instance != null) { Destroy(gameObject); return; }
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
if (registry == null)
|
||||
registry = FindObjectOfType<BlockRegistry>();
|
||||
}
|
||||
|
||||
// ── Публичный API ─────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Получить blockId по мировым координатам блока. null = пусто.</summary>
|
||||
public string GetBlock(Vector3Int worldPos)
|
||||
{
|
||||
Vector3Int cp = ChunkData.WorldToChunkPos(worldPos);
|
||||
if (!_chunks.TryGetValue(cp, out var chunk)) return null;
|
||||
return chunk.GetBlock(ChunkData.WorldToLocal(worldPos));
|
||||
}
|
||||
|
||||
/// <summary>Поставить блок. Создаёт чанк если нужно.</summary>
|
||||
public void PlaceBlock(Vector3Int worldPos, string blockId)
|
||||
{
|
||||
var chunk = GetOrCreateChunk(ChunkData.WorldToChunkPos(worldPos));
|
||||
chunk.SetBlock(ChunkData.WorldToLocal(worldPos), blockId);
|
||||
|
||||
// Соседние чанки на границе тоже надо пересобрать (face culling)
|
||||
RebuildNeighboursIfBorder(worldPos);
|
||||
}
|
||||
|
||||
/// <summary>Сломать блок. Возвращает blockId удалённого блока (или null).</summary>
|
||||
public string BreakBlock(Vector3Int worldPos)
|
||||
{
|
||||
Vector3Int cp = ChunkData.WorldToChunkPos(worldPos);
|
||||
if (!_chunks.TryGetValue(cp, out var chunk)) return null;
|
||||
|
||||
string blockId = chunk.GetBlock(ChunkData.WorldToLocal(worldPos));
|
||||
if (blockId == null) return null;
|
||||
|
||||
chunk.RemoveBlock(ChunkData.WorldToLocal(worldPos));
|
||||
RebuildNeighboursIfBorder(worldPos);
|
||||
return blockId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить или создать чанк по чанковым координатам.
|
||||
/// Используется генераторами мира и PlaceBlock.
|
||||
/// </summary>
|
||||
public Chunk GetOrCreateChunk(Vector3Int chunkPos)
|
||||
{
|
||||
if (_chunks.TryGetValue(chunkPos, out var existing))
|
||||
return existing;
|
||||
|
||||
return CreateChunk(chunkPos, new ChunkData(chunkPos));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создать чанк с готовыми данными (используется генераторами мира).
|
||||
/// </summary>
|
||||
public Chunk CreateChunk(Vector3Int chunkPos, ChunkData data)
|
||||
{
|
||||
if (_chunks.ContainsKey(chunkPos))
|
||||
{
|
||||
Debug.LogWarning($"[WorldManager] Chunk {chunkPos} already exists.");
|
||||
return _chunks[chunkPos];
|
||||
}
|
||||
|
||||
GameObject go;
|
||||
if (chunkPrefab != null)
|
||||
go = Instantiate(chunkPrefab, transform);
|
||||
else
|
||||
{
|
||||
go = new GameObject($"Chunk_{chunkPos}");
|
||||
go.transform.SetParent(transform);
|
||||
}
|
||||
|
||||
go.transform.position = ChunkData.ChunkToWorld(chunkPos);
|
||||
|
||||
var chunk = go.GetComponent<Chunk>() ?? go.AddComponent<Chunk>();
|
||||
chunk.Initialise(data, registry.ChunkMaterial);
|
||||
|
||||
_chunks[chunkPos] = chunk;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
// ── Вспомогательные ───────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Если изменённый блок находится на границе чанка —
|
||||
/// пересобираем соседний чанк, чтобы обновить face culling.
|
||||
/// </summary>
|
||||
void RebuildNeighboursIfBorder(Vector3Int worldPos)
|
||||
{
|
||||
Vector3Int local = ChunkData.WorldToLocal(worldPos);
|
||||
|
||||
if (local.x == 0) RebuildChunkAt(worldPos + Vector3Int.left);
|
||||
if (local.x == ChunkData.SIZE - 1) RebuildChunkAt(worldPos + Vector3Int.right);
|
||||
if (local.y == 0) RebuildChunkAt(worldPos + Vector3Int.down);
|
||||
if (local.y == ChunkData.HEIGHT - 1) RebuildChunkAt(worldPos + Vector3Int.up);
|
||||
if (local.z == 0) RebuildChunkAt(worldPos + new Vector3Int(0,0,-1));
|
||||
if (local.z == ChunkData.SIZE - 1) RebuildChunkAt(worldPos + new Vector3Int(0,0,1));
|
||||
}
|
||||
|
||||
void RebuildChunkAt(Vector3Int worldPos)
|
||||
{
|
||||
Vector3Int cp = ChunkData.WorldToChunkPos(worldPos);
|
||||
if (_chunks.TryGetValue(cp, out var chunk))
|
||||
chunk.RebuildMesh();
|
||||
}
|
||||
|
||||
/// <summary>Перевод мировой позиции игрока → мировые координаты блока.</summary>
|
||||
public static Vector3Int WorldToBlock(Vector3 worldPosition)
|
||||
{
|
||||
return new Vector3Int(
|
||||
Mathf.FloorToInt(worldPosition.x),
|
||||
Mathf.FloorToInt(worldPosition.y),
|
||||
Mathf.FloorToInt(worldPosition.z));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user