bloak
This commit is contained in:
180
Assets/Blocks/Scripts/Chunk.cs
Normal file
180
Assets/Blocks/Scripts/Chunk.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user