Files
my-project/Assets/Blocks/Scripts/Chunk.cs
unknown 087db550f1 bloak
2026-04-16 18:19:23 +05:00

181 lines
7.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// MonoBehaviour-обёртка над ChunkData.
/// Хранит данные блоков и собирает процедурный меш с face culling.
/// </summary>
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshCollider))]
public class Chunk : MonoBehaviour
{
public ChunkData Data { get; private set; }
MeshFilter _filter;
MeshRenderer _renderer;
MeshCollider _collider;
Mesh _mesh;
// ── 6 направлений граней ──────────────────────────────────────────────────
// Порядок: Top, Bottom, Front(Z+), Back(Z-), Right(X+), Left(X-)
static readonly Vector3Int[] Directions =
{
Vector3Int.up, Vector3Int.down,
new(0,0,1), new(0,0,-1),
new(1,0,0), new(-1,0,0)
};
// Вершины каждой грани (относительно нижнего-левого угла блока)
// Порядок по часовой стрелке, смотрим снаружи
static readonly Vector3[][] FaceVerts =
{
// Top (Y+)
new[] { new Vector3(0,1,0), new Vector3(0,1,1), new Vector3(1,1,1), new Vector3(1,1,0) },
// Bottom (Y-)
new[] { new Vector3(0,0,0), new Vector3(1,0,0), new Vector3(1,0,1), new Vector3(0,0,1) },
// Front (Z+)
new[] { new Vector3(1,0,1), new Vector3(0,0,1), new Vector3(0,1,1), new Vector3(1,1,1) },
// Back (Z-)
new[] { new Vector3(0,0,0), new Vector3(1,0,0), new Vector3(1,1,0), new Vector3(0,1,0) },
// Right (X+)
new[] { new Vector3(1,0,0), new Vector3(1,0,1), new Vector3(1,1,1), new Vector3(1,1,0) },
// Left (X-)
new[] { new Vector3(0,0,1), new Vector3(0,0,0), new Vector3(0,1,0), new Vector3(0,1,1) }
};
// UV для каждой из 4 вершин грани
static readonly Vector2[] FaceUVs =
{
new(0,0), new(1,0), new(1,1), new(0,1)
};
// ── Инициализация ─────────────────────────────────────────────────────────
void Awake()
{
_filter = GetComponent<MeshFilter>();
_renderer = GetComponent<MeshRenderer>();
_collider = GetComponent<MeshCollider>();
_mesh = new Mesh { name = "ChunkMesh" };
_mesh.MarkDynamic(); // оптимизация для часто изменяемых мешей
_filter.sharedMesh = _mesh;
}
public void Initialise(ChunkData data, Material chunkMaterial)
{
Data = data;
_renderer.sharedMaterial = chunkMaterial;
RebuildMesh();
}
// ── Публичный API ─────────────────────────────────────────────────────────
public void SetBlock(Vector3Int localPos, string blockId)
{
Data.SetBlock(localPos, blockId);
RebuildMesh();
}
public void RemoveBlock(Vector3Int localPos)
{
Data.SetBlock(localPos, null);
RebuildMesh();
}
public string GetBlock(Vector3Int localPos) => Data.GetBlock(localPos);
// ── Построение меша ───────────────────────────────────────────────────────
public void RebuildMesh()
{
if (Data == null) return;
var verts = new List<Vector3>();
var tris = new List<int>();
var uvs = new List<Vector2>(); // UV0: координаты на грани 0-1
var texCoord1 = new List<Vector2>(); // UV1: x = индекс в Texture2DArray
for (int x = 0; x < ChunkData.SIZE; x++)
for (int y = 0; y < ChunkData.HEIGHT; y++)
for (int z = 0; z < ChunkData.SIZE; z++)
{
string blockId = Data.GetBlock(x, y, z);
if (blockId == null) continue;
var def = BlockRegistry.Instance?.GetById(blockId);
if (def == null) continue;
for (int f = 0; f < 6; f++)
{
Vector3Int neighbour = new Vector3Int(x, y, z) + Directions[f];
// Face culling: пропускаем грань если сосед — твёрдый блок
if (HasSolidNeighbour(neighbour)) continue;
// Индекс текстуры для этой грани
int texIndex = f == 0 ? def.topIndex :
f == 1 ? def.bottomIndex :
def.sideIndex;
int baseVert = verts.Count;
var origin = new Vector3(x, y, z);
for (int v = 0; v < 4; v++)
{
verts.Add(origin + FaceVerts[f][v]);
uvs.Add(FaceUVs[v]);
texCoord1.Add(new Vector2(texIndex, 0));
}
// Два треугольника на грань
tris.Add(baseVert + 0);
tris.Add(baseVert + 1);
tris.Add(baseVert + 2);
tris.Add(baseVert + 0);
tris.Add(baseVert + 2);
tris.Add(baseVert + 3);
}
}
_mesh.Clear();
_mesh.SetVertices(verts);
_mesh.SetTriangles(tris, 0);
_mesh.SetUVs(0, uvs);
_mesh.SetUVs(1, texCoord1);
_mesh.RecalculateNormals();
_mesh.RecalculateBounds();
// Обновляем коллайдер (нужен для рейкаста)
_collider.sharedMesh = null;
_collider.sharedMesh = _mesh;
Data.ClearDirty();
}
// ── Face culling ──────────────────────────────────────────────────────────
bool HasSolidNeighbour(Vector3Int localPos)
{
// Сосед внутри этого чанка
if (ChunkData.InBounds(localPos.x, localPos.y, localPos.z))
{
string id = Data.GetBlock(localPos);
if (id == null) return false;
var def = BlockRegistry.Instance?.GetById(id);
return def != null && def.isSolid;
}
// Сосед в соседнем чанке — спрашиваем WorldManager
if (WorldManager.Instance == null) return false;
Vector3Int worldOrigin = ChunkData.ChunkToWorld(Data.ChunkPos);
Vector3Int worldNeighbour = worldOrigin + localPos;
string neighbourId = WorldManager.Instance.GetBlock(worldNeighbour);
if (neighbourId == null) return false;
var neighbourDef = BlockRegistry.Instance?.GetById(neighbourId);
return neighbourDef != null && neighbourDef.isSolid;
}
}