This commit is contained in:
unknown
2026-04-16 18:19:23 +05:00
parent 6c3f72385e
commit 087db550f1
43 changed files with 1676 additions and 58 deletions

View 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));
}
}