⚓ 문제
![]() |
![]() |
유저가 영역 밖에서 영역 안으로 들어왔을 때 흔적과 영역의 경계가 만드는 닫힌 경계을 추출한다.
정점 정보를 가지고 평면을 생성한다.
⚓ 접근
1. 흔적과 영역의 경계가 만드는 닫힌 경계
- 영역 밖으로 나갔을 때/들어왔을 때는 bool flag를 통해 판별한다.
- Creature의 현재 밟고 있는 위치가 Area 오브젝트가 아닐 때 흔적 좌표를 List에 저장하기 시작한다.
- 다시 영역 안으로 들어왔을 때 흔적과 기존 영역의 경계가 이루는 폐구간을 새로운 영역의 경계로 정의한다.
- 이 때, 새로운 영역은 이루는 정점들은 시계방향으로 나열된 List여야 한다.(Mesh 정점, 삼각형 규칙 때문)
- Creature가 흔적을 남기는 방향은 2가지 경우가 있다. a) 시계방향 b) 반시계방향
- a) 시계방향의 경우 흔적 배열의 처음과 마지막 좌표에서 기존 영역 경계 좌표 리스트의 가장 가까운 좌표(start, end)를 기준으로 end ~ start까지 역으로 순회하며 흔적 배열에 추가한다.
- b) 반시계방향의 경우 흔적 배열을 역으로 정렬한 후 같은 방식으로 흔적 배열에 기존 영역 경계를 추가한다.
2. 평면을 생성
- 생성되는 영역은 흔적의 모양에 다라 오목/볼록 같이 여러 형태가 나올 수 있다.
- 이런 다각형 메시를 생성하기 위해선 정점으로 만들어진 삼각형 배열을 정교하게 만들어야 한다.
- 여러 방법을 시도해 봤는데 순서대로 기술하자면
Try 1) 폐영역의 중심점을 정점에 포함하고 이 중심을 기준으로 삼각형을 만든다.
문제점 : 중심으로부터 정점으로 뻗어가는 선에서 오목/볼록한 구간에 막혀버리면 삼각형이 만들어지지 않는 정점도 존재했다.
Try 2) 새 영역 메시의 두 끝 점을 직선으로 연결하고(기존 영역을 가로지르도록) bool operation 연산을 통해 subtract해서 기존 영역과 겹치는 영역을 뺀다.
문제점 : CSG 라이브러리를 시도해봤는데 정점과 삼각형을 만드는 로직이 달라서 그런지 적용이 되지 않았다...
Try 3) 유니티 Probuilder 패키지를 사용하면 정점 정보만 넣어주면 삼각분할(Triangulation)을 자동으로 해준다는 것을 알게 되어서 사용해봤다.
결과 : 시계방향으로 정렬한 정점 배열을 넣어주면 자동으로 삼각형을 만들고 mesh를 생성할 수 있었다.
![]() Probuilder에서 다각형을 만들면 생성되는 삼각형(삼각분할 알고리즘) |
![]() |
⚓ 구현
a) 영역 밖에서 안으로 들어왔을 때
private void FixedUpdate()
{
switch (CheckPixelType())
{
case EPixelType.None:
{
print("None!!");
isClosed = false;
TraceOnLand();
break;
}
case EPixelType.Area:
{
if (isClosed == false && Vertices.Count > 3)
{
isClosed = true;
Managers.Map.UpdateArea(this);
Vertices.Clear();
lineRenderer.positionCount = Vertices.Count;
}
print("Area!!");
break;
}
}
CreatureMove();
}
b) 새로운 영역 갱신(MapManager.cs)
//MapManager.cs
public GameObject UpdateArea(Creature creature)
{
if (creature == null) return null;
BoundaryCalcultate(creature);
GameObject bottom = new GameObject(creature.data.DataId.ToString());
GameObject top = new GameObject("TopArea");
bottom.CreateMesh(creature.Vertices);
bottom.GetorAddComponent<MeshRenderer>().material = creature.BottomMat;
top.CreateMesh(creature.Vertices);
top.GetorAddComponent<MeshRenderer>().material = creature.TopMat;
bottom.transform.position += Vector3.up * 0.01f;
top.transform.position += Vector3.up * 0.03f;
top.transform.localScale = 0.98f * Vector3.one;
GameObject root = new GameObject("Area");
root.transform.SetParent(GetRoot("@Areas").transform);
top.transform.SetParent(root.transform);
bottom.transform.SetParent(root.transform);
creature.Areas.Add(root);
bottom.AddComponent<MeshCollider>();
bottom.layer = AreaMask;
creature.Vertices.Clear();
return bottom;
}
c) 폐영역의 경계 좌표 계산 (MapManager.cs)
//MapManager.cs
void BoundaryCalcultate(Creature creature)
{
if (creature.Areas.Count == 0)
{
Debug.LogError("Creature Area is null");
return;
}
Vector3 center = Util.CalcultateAvg(creature.Vertices);
Vector3 a = creature.Vertices[0] - center;
Vector3 b = creature.Vertices[1] - center;
if (Vector3.Dot(Vector3.Cross(a, b), Vector3.up) < 0)
{
creature.Vertices.Reverse();
}
List<Vector3> boundary = new List<Vector3>();
boundary.AddRange(creature.Vertices);
int startIdx = 0;
int endIdx = 0;
float startDist = float.MaxValue;
float endDist = float.MaxValue;
for (int i = 0; i < creature.Boundary.Count; i++)
{
float dist = Vector3.Distance(creature.Vertices[0], creature.Boundary[i]);
if (dist < startDist)
{
startDist = dist;
startIdx = i;
}
dist = Vector3.Distance(creature.Vertices[creature.Vertices.Count - 1], creature.Boundary[i]);
if (dist < endDist)
{
endDist = dist;
endIdx = i;
}
}
int idx = endIdx;
while (idx != startIdx)
{
boundary.Add(creature.Boundary[idx]);
idx = (idx + 1) % creature.Boundary.Count;
}
boundary.Add(creature.Boundary[startIdx]);
idx = endIdx;
while (idx != startIdx)
{
creature.Vertices.Add(creature.Boundary[idx]);
idx = (creature.Boundary.Count + idx - 1) % creature.Boundary.Count;
}
creature.Vertices.Add(creature.Boundary[startIdx]);
creature.Boundary = boundary;
}
d) 메시 생성(Extension.cs)
//Extension.cs
public static void CreateMesh(this GameObject go, List<Vector3> vertices)
{
ProBuilderMesh mesh = go.GetorAddComponent<ProBuilderMesh>();
mesh.CreateShapeFromPolygon(vertices, 0, false);
}
⚓ 결과
'Unity > Paper.io 2' 카테고리의 다른 글
[유니티-Paper.io2] 이동흔적 가시화와 데이터 관리 (1) | 2024.12.15 |
---|---|
[유니티-paper.io2] 프레임 단위 이동과 흔적 남기기(폐기) (0) | 2024.12.15 |