if (_world.VoxelMap.TryGetValue(new(posX, y, posZ), out var value))
AddVoxelDataToChunk(new(posX, y, posZ), value);
private void AddVoxelDataToChunk(Vector3Int pos, BlockType block)
{
if (block is NormalBlockType normalBlock)
{
for (int i = 0; i < 6; i++)
{
if (_world.CheckVoxel(pos + _data.faceChecks[i]))
continue;
for (int j = 0; j < 4; j++)
_vertices.Add(pos + _data.voxelVerts[_data.voxelTris[i][j]]);
AddTextureUV(normalBlock.GetTextureID(i));
_triangles.Add(_vertexIdx);
_triangles.Add(_vertexIdx + 1);
_triangles.Add(_vertexIdx + 2);
_triangles.Add(_vertexIdx + 2);
_triangles.Add(_vertexIdx + 1);
_triangles.Add(_vertexIdx + 3);
_vertexIdx += 4;
}
}
else if (block is SlideBlockType slideBlock)
{
var obj = Managers.Resource.GetCache<GameObject>("Slide Block.prefab");
obj = UnityEngine.Object.Instantiate(obj, pos, Quaternion.identity);
var slide = obj.GetComponent<SlideBlock>();
slide.Forward = slideBlock.Forward;
slide.FrontMaterial = slideBlock.FrontMaterial;
slide.SideMaterial = slideBlock.SideMaterial;
slide.transform.SetParent(_chunkObject.transform);
}
}
미끄럼틀처럼 생긴 Slide 블럭을 추가하면서 BlockType을 NormalBlock과 SlideBlock 두 가지로 파생시키게 됐다.
때문에 블럭 데이터를 Chunk에 추가할 때, Map에서 불러온 BlockType이 어떤 타입인지 if문으로 걸러내서 각각 블럭에 맞는 로직으로 데이터를 추가하고 있었다.
이 구조는 전혀 객체지향적이지 않아서, 부모 클래스인 BlockType를 추상클래스로 바꾸고, AddVoxelDataToChunk를 Chunk가 아닌 BlockType에서 진행하도록 리팩토링해봤다.
if (_world.VoxelMap.TryGetValue(new(posX, y, posZ), out var value))
value.type.AddVoxelDataToChunk(this, new(posX, y, posZ), value.forward);
[System.Serializable]
public abstract class BlockType
{
...
public abstract void AddVoxelDataToChunk(Chunk chunk, Vector3Int pos, Vector3 dir);
}
[System.Serializable]
public class NormalBlockType : BlockType
{
...
public override void AddVoxelDataToChunk(Chunk chunk, Vector3Int pos, Vector3 dir)
{
for (int i = 0; i < 6; i++)
{
if (chunk.World.CheckVoxel(pos + chunk.Data.faceChecks[i]))
continue;
for (int j = 0; j < 4; j++)
chunk.Vertices.Add(pos + chunk.Data.voxelVerts[chunk.Data.voxelTris[i][j]]);
chunk.AddTextureUV(GetTextureID(i));
chunk.Triangles.Add(chunk.VertexIdx);
chunk.Triangles.Add(chunk.VertexIdx + 1);
chunk.Triangles.Add(chunk.VertexIdx + 2);
chunk.Triangles.Add(chunk.VertexIdx + 2);
chunk.Triangles.Add(chunk.VertexIdx + 1);
chunk.Triangles.Add(chunk.VertexIdx + 3);
chunk.VertexIdx += 4;
}
}
}
[System.Serializable]
public class SlideBlockType : BlockType
{
...
public override void AddVoxelDataToChunk(Chunk chunk, Vector3Int pos, Vector3 dir)
{
var obj = Managers.Resource.GetCache<GameObject>("Slide Block.prefab");
obj = UnityEngine.Object.Instantiate(obj, pos, Quaternion.identity);
var slide = obj.GetComponent<SlideBlock>();
slide.Forward = dir;
slide.FrontMaterial = FrontMaterial;
slide.SideMaterial = SideMaterial;
slide.transform.SetParent(chunk.InstanceBlocksParent);
slide.name = $"{obj.name} ({pos})";
}
}
Chunk의 AddVoxelDataToChunk 메서드는 아예 지웠다.
대신, BlockType의 추상메서드로 바꿔줬다.
이제, 새로운 블럭의 타입이 더 파생된다해도 Chunk.cs는 건드릴 필요가 없어졌다.
특히, 새로운 블럭 타입마다 else if를 더 늘리는 짓은 안해도 된다.
현재 진행중인 프로젝트는 복셀 맵을 사용중이다.
하지만 MagicaVoxel같은 완벽한 에디터라기보단, 유니티의 기본 3D 오브젝트 (프리미티브)를 이용해서 간단하게 맵 제작을 할 수 있도록 계획중이다.
맵 정보를 생성하게 되면, World 클래스와 WorldEditor(가명) 클래스의 기능도 분리해야한다.
using UnityEngine;
public class MapEditDataSource : MonoBehaviour
{
public enum Inherits
{
Normal,
Slide,
}
public enum Direction
{
Forward,
Back,
Up,
Down,
Left,
Right,
}
[SerializeField] private WorldData data;
[SerializeField] private Vector3Int position = Vector3Int.zero;
[SerializeField] private Vector3Int size = Vector3Int.one;
[SerializeField] private Direction forward = Direction.Forward;
[SerializeField] private Inherits type;
[SerializeField] private int index;
public BlockType BlockType;
private void OnValidate()
{
transform.position = position - new Vector3(0f, -0.5f, 0f);
transform.localScale = size;
switch (forward)
{
case Direction.Forward: transform.forward = Vector3Int.forward; break;
case Direction.Back: transform.forward = Vector3Int.down; break;
case Direction.Up: transform.forward = Vector3Int.up; break;
case Direction.Down: transform.forward = Vector3Int.down; break;
case Direction.Left: transform.forward = Vector3Int.left; break;
case Direction.Right: transform.forward = Vector3Int.right; break;
}
}
}
Edit Mode에서 인스펙터 수치 딸깍으로 Transform 정보가 반영되게 OnValidate 메서드를 사용했다.
또, 복셀 맵 정보를 생성할 것이기 때문에 위치, 사이즈 정보는 Vector3Int를 사용해서 소수점은 사용하지 않도록 했다.