본문 바로가기

Unity/3D

Unity C# Tutorials - Procedural Grid (Programming Form) 번역본

반응형

#이 블로그에 기재되는 번역본은 번역기에 의존하여 최대한 스스로 이해할 수 있도록 작성된 것 입니다. 만약 어색한 부분이 있어 의견을 제기하신다면 수정하도록 하겠습니다.

 

출처 : https://catlikecoding.com/unity/tutorials/procedural-grid/

Procedural Grid

 Programming Form 

  • 점들의 그리드를 생성합니다. (점과 점을 이어 격자를 그린다는 의미 같습니다.)
  • 코루틴을 이용하여 위치를 분석할 것 입니다.(순차적으로 나오도록 한다.)
  • 삼각형으로 표면을 정의합니다.
  • 법선을 자동으로 생성합니다.
  • 텍스처 좌표와 접선을 추가합니다.

이 튜토리얼에서는 정점과 삼각형의 간단한 그리드를 작성합니다.

 

이 튜토리얼에서는 Unity 스크립팅의 기본 사항에 익숙하다고 가정합니다. 이러한 기본 사항은 Clock를 참조하십시오. Constructing a Fractal은 코루틴의 사용법을 소개합니다. 이 튜토리얼은 Unity 5.0.1 이상에서 작성되었습니다.

 

복잡한 외형 아래에는 단순한 형상이 있습니다.

 

 

1. 사물을 렌더링합니다.

 Unity에서 무언가를 시각화하려면 메쉬를 사용하세요. 다른 프로그램에서 내보낸 3D 모델일 수도있고 절차적으로 생성 된 메쉬일 수도 있습니다. Unity가 메쉬를 사용하는 스프라이트, UI 요소 또는 파티클 시스템이 될 수도 있습니다. 화면 효과조차도 메쉬로 렌더링됩니다.

 

결국 메쉬란? 개념적으로 메쉬는 복잡한 물건을 그리기 위해 그래픽 하드웨어에서 사용하는 구조입니다. 여기에는 3D 공간에서 점을 정의하는 꼭짓점 모음과 이 점들을 연결하는 가장 기본적인 2D 모양 인 삼각형 집합이 포함됩니다. 삼각형은 메시가 나타내는 것이 무엇이든간에 표면을 형성합니다.

 

삼각형은 평평하고 직선 모서리를 가지므로 큐브면과 같이 평평하고 직선인 물건을 완벽하게 시각화 하는 데 사용할 수 있습니다. 곡선 또는 원형 표면은 많은 소형 삼각형을 사용하여 근사하게 구현할 수 있습니다. 삼각형이 단일 픽셀보다 크지 않은 정도로 작게 나타나면 근사치를 알 수 없습니다. 일반적으로 실시간 성능에는 적합하지 않으므로 표면이 항상 어느 정도 늘쭉날쭉하게 나타납니다.

 

Unity의 기본 캡슐, 큐브, 구(음영 및 와이어 프레임)

 

 

...더보기

와이어 프레임을 표시하는 방법?

도구 모음의 왼쪽에서 Scene 뷰의 표시 모드를 선택할 수 있습니다. 처음 세 옵션은 음영 처리, 와이어 프레임 및 음영 처리된 와이어 프레임입니다.

게임 개체에 3D 모델을 표시하려면 두 가지 구성 요소가 있어야합니다. 첫 번째는 메쉬 필터입니다. 이 구성 요소는 보여주고자 하는 메쉬에 대한 참조를 보유합니다. 두 번째는 메쉬 렌더러입니다. 이를 사용하여 메쉬 렌더링 방법을 구성합니다. 어떤 재질을 사용해야하는지, 그림자를 생성하거나 다른 물체의 그림자를 받아야 하는지 등.

 

Unity의 기본 큐브 게임 개체.

...더보기

왜 머티리얼의 배열이 있습니까?

메쉬 렌더러에는 여러 머티리얼이 있을 수 있습니다. 이것은 주로 서브 메쉬로 알려진 여러 개의 별도 삼각형 세트가 있는 메쉬 렌더링에 사용됩니다. 주로 가져온 3D 모델과 함께 사용되며 이 자습서에서는 다루지 않습니다.

머티리얼을 조정하여 메쉬의 모양을 완전히 바꿀 수 있습니다. Unity의 기본 재질은 흰색으로 단색이빈다. Assets/Create/Material을 통해 새로운 머티리얼 에셋을 생성하고 이를 게임 객체로 드래그 하여 자신의 것으로 대체할 수 있습니다. 새로운 재질은 기본적으로 유니티의 표준 쉐이더를 사용합니다. 이 쉐이더는 표면이 시각적으로 어떻게 동작하는지 조정할 수 있는 컨트롤 세트를 제공합니다.

 

메쉬에 많은 디테일을 추가하는 빠른 방법은 알베도 맵을을 제공하는 것 입니다. 머티리얼의 기본 색상을 나타내는 텍스처를 뜻 합니다. 물론 우리는 이 텍스쳐를 메쉬의 삼각형에 투영하는 방법을 알아봐야합니다. 이 작업은 정점에 2D 텍스처 좌표를 추가하여 수행됩니다. 텍스처 공간의 두 가지 차원을 U와 V라고 하며, 이것이 UV좌표로 알려져  있습니다. 이러한 좌표는 일반적으로 (0,0)과 (1,1)사이에 있으며 전체 텍스처를 포함합니다. 텍스처 설정에 따라 해당 범위 밖의 좌표가 고정되거나 타일링됩니다.

Unity 메쉬에 적용된 UV테스트 텍스처.

파일 : 유니티 패키지

불러오는 중입니다...

2. 정점의 그리드 생성하기

그렇다면 메쉬는 어떻게 만들까요? 간단한 직사각형 격자를 만들어서 알아 보겟습니다. 그리드는 단위 길이의 정사각형 타일 - 쿼드로 구성됩니다. 새 C# 스크립트를 만들고 가로 및 세로 크기의 그리드 구성 요소로 변환합니다.

using UnityEngine;
using System.Collections;

public class Grid : MonoBehaviour {

	public int xSize, ySize;
}
...더보기

using System.Collections가 필요한가요?

지금은 필요가 없습니다. 나중에 코루틴을 사용할 것이기 때문에 포함시켰습니다.

이 구성 요소를 게임 객체에 추가 할 때 메쉬 필터와 메쉬 렌더러도 제공해야합니다. 클래스에 속성을 추가하여 Unity에서 자동으로 속성을 추가하도록 할 수 있습니다.

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class Grid : MonoBehaviour {

	public int xSize, ySize;
}

이제 빈 게임 개체를 새로 만들고 그리드 구성 요소를 추가하면 다른 두 구성 요소도 생깁니다. 렌더러의 재질을 설정하고 필터의 메쉬를 정의되지 않은 상태로 둡니다. 그리드 크기를 10 x 5로 설정했습니다.

 

그리드 오브젝트

우리는 재생(Play) 모드의 시작때 메쉬를 생성 할것입니다. 

	private void Awake () {
		Generate();
	}

먼저 정점 위치에 초점을 맞추고 나중에 삼각형을 다룰 것 입니다. 점들을 저장하기 위해 일련의 3D 벡터를 유지해야 합니다. 정점의 양은 그리드의 크기에 따라 다릅니다. 모든 쿼드의 모서리에 꼬지점이 필요하지만 인접한 쿼드는 동일한 꼭지점을 공유할 수 있습니다. 따라서 우리는 각 차원에서 타일을 가지고있는 것 보다 하나의 꼭 지점이 더 필요합니다.

( # x + 1 ) ( # y + 1 )

4x2 그리드에 대한 정점 및 쿼드 인덱스

	private Vector3[] vertices;

	private void Generate () {
		vertices = new Vector3[(xSize + 1) * (ySize + 1)];
	}

이 꼭지점을 시각화하여 올바르게 배치했는지 확인할 수 있도록합시다. 우리는 OnDrawGizmos방버을 추가하고 모든 정점에 대한 씬 뷰에 작은 검은 색 구를 그립니다.

	private void OnDrawGizmos () {
		Gizmos.color = Color.black;
		for (int i = 0; i < vertices.Length; i++) {
			Gizmos.DrawSphere(vertices[i], 0.1f);
		}
	}
...더보기

Gizmos란 무엇인가요?

Gizmos는 편집기에서 사용할 수 있는 시각적 자료입니다. 기본적으로 Game View가 아닌 Scene View에서 볼 수 있지만 툴바를 통해 조정할 수 있습니다. Gizmos Utility클래스는 아이콘, 선, 다른 것들을 그릴 수 있습니다.

 

Gizmos는 Unity 편집기에서 자동으로 호출되는 OnDrawGizmos 메서드 내부에서 사용할 수 있습니다. 다른 방법은 선택된 객체에 대해서만 OnDrawGizmosSelected를 호출하는 방법입니다.

플레이 모드가 아닐 때 에러가 발생합니다. 왜냐하면 Unity가 편집 모드에 있을 때에도 호출을 하는데 OnDrawGizmos가 어떤 정점도 없는 상태에서 vertices에 접근하려 해서 그렇습니다. 이 오류를 방지하려면 배열이 있는지 여부를 확인하고 그렇지 않을 경우 return 되도록 하면됩니다.

 

	private void OnDrawGizmos () {
		if (vertices == null) {
			return;
		}
		…
	}

Gizmo

재생 모드에 있는 동안 원점에있는 구가 하나만 보입니다. 아직은 정점을 배치하지 않았기 때문에 정점이 모두 한 점에 겹치기 때문입니다. (Vector는 구조체로 아무런 값 없이 선언되면 (0,0,0) 으로 초기화 되어 있습니다.) 이중 루프를 이용하여 모든 위치를 배치합니다.

 

	private  void Generate () { 
		vertices = new  Vector3 [(xSize + 1 ) * (ySize + 1 )];
		for ( int i = 0 , y = 0 ; y <= ySize; y ++) {
			for ( int x = 0 ; x <= xSize; x ++, i ++) {
				vertices [i] = new  Vector3 (x, y);
			}
		}
	}

정점 생성을 보여줍니다.

https://thumbs.gfycat.com/IgnorantEminentDragon-mobile.mp4

불러오는 중입니다...

파일 : 유니티 패키지

3. 메쉬 만들기

이제 정점이 올바르게 배취되었음을 알았으므로 실제 메쉬를 처리 할 수 있습니다. 우리 자신의 구성 요소에 대한 참조를 보유하는 것 외에도 이를 메쉬 필터에 지정해야합니다. 그런 다음 꼭지점을 처리하고 나서면 그 꼭지점을 메쉬에 제공 할 수 있습니다.

	private Mesh mesh;

	private IEnumerator Generate () {
		WaitForSeconds wait = new WaitForSeconds(0.05f);
		
		GetComponent<MeshFilter>().mesh = mesh = new Mesh();
		mesh.name = "Procedural Grid";

		vertices = new Vector3[(xSize + 1) * (ySize + 1)];
		…
		mesh.vertices = vertices;
	}
...더보기

구성 요소가 메쉬에 고정되어 있어야합니까?

Generate 메소드 내부의 메쉬에 대한 참조만 필요합니다. 메쉬 필터도 메쉬에 대한 참조가 있으므로 어쨌든 적용되어 있습니다. 이 튜토리얼의 다음 논리적 단계는 메쉬를 애니메이션으로 만드는 것이므로 전역 변수로 만들었습니다. 여러분이 시도해 보길 권장합니다.

재생 모드시 메쉬가 나타납니다.

이제 우리는 재생 모드에서 메쉬를 가졌지만 삼각형을 주지 않았기 때문에 아직 나타나지 않습니다. 삼각형은 정점 인덱스 배열을 통해 정의됩니다. 각 삼각형은 세 점을 가지므로 세 개의 연속된 색인은 한 삼각형을 나타냅니다. 하나의 삼각형으로 시작해 보겠습니다.

 

	private IEnumerator Generate () {
		…

		int[] triangles = new int[3];
		triangles[0] = 0;
		triangles[1] = 1;
		triangles[2] = 2;
		mesh.triangles = triangles;
	}

이제 하나의 삼각형을 가지고 있지만, 우리가 사용하고있는 세 점은 모두 직성으로 놓여 있습니다. 따라서 보이지 않는 Degenerate 삼각형을 생성합니다. 청므 두 정점은 괜찮지 만 당므 행의 첫 번째 정점으로 점프해야합니다.

	triangles[0] = 0;
	triangles[1] = 1;
	triangles[2] = xSize + 1;

이제 삼각형을 형성하지만 한 방향에서만 볼 수 있습니다. 이 경우 Z축의 반대방향을 볼 때만 보입니다. 따라서 보이는 방향을 회전시킬 수 있습니다.

 

삼각형이 보이는 쪽은 정점의 인덱스의 방향에 의해 결정되빈다. 기본적으로 시계 방향으로 배치 된 경우 삼각형은 앞으로 향하고 보이는 것으로 간주됩니다. 반 시계 방향의 삼각형은 버려지므로 일반적으로 볼 수 없지만, 객체 내부 렌더링에 시간을 낭비할 필요가 없어집니다.

 

삼각형의 양면

그래서 우리가 Z축을 내려다 보앗을 때 삼각형을 나타내려면 꼭지점이 지나가는 순서를 변경해야 합니다. 마지막 두개의 인덱스를 교체하여 그렇게 할 수 있습니다.

		triangles[0] = 0;
		triangles[1] = xSize + 1;
		triangles[2] = 1;

첫번째 삼각형

이제 격자의 첫 번째 타일의 절반을 덮는 하나의 삼각형이 생겼습니다. 전체 타일을 덮으려면 두 번째 삼각형만 있으면 됩니다.

 

		int[] triangles = new int[6];
		triangles[0] = 0;
		triangles[1] = xSize + 1;
		triangles[2] = 1;
		triangles[3] = 1;
		triangles[4] = xSize + 1;
		triangles[5] = xSize + 2;

두 개의 삼각형으로 만든 쿼드

이 삼각형은 두 갱의 꼭지점을 공유 하기 때문에 각 꼭지점 인덱스를 명시적으로 한 번만 언급하는 4줄의 코드로 줄일 수 있습니다.

 

		triangles[0] = 0;
		triangles[3] = triangles[2] = 1;
		triangles[4] = triangles[1] = xSize + 1;
		triangles[5] = xSize + 2;

 

첫 번째 쿼드

타일을 루프로 바꿈으로써 타일을 전체를 만들 수 있습니다. 정점과 삼각형 인덱스를 반복하면서 우리는 두 가지를 모두 추적해야합니다. yield 문을 이 루프로 이동시켜 더이상 정점이 나타날 때 까지 기다릴 필요가 없습니다.

		//여기선 최하단 한줄만 처리합니다.
        int[] triangles = new int[xSize * 6];
        //ti는 삼각형 배열의 인덱스
        //vi는 정점 배열의 인덱스
        //x는 만들 패널의 갯수로 보시면됩니다.
		for (int ti = 0, vi = 0, x = 0; x < xSize; x++, ti += 6, vi++) {
			triangles[ti] = vi;
			triangles[ti + 3] = triangles[ti + 2] = vi + 1;
			triangles[ti + 4] = triangles[ti + 1] = vi + xSize + 1;
			triangles[ti + 5] = vi + xSize + 2;
			yield return wait;
		}

버텍스 기즈모가 즉시 나타나고 잠시 기다린 후에 삼각형이 모두 나타납니다. 타일이 하나씩 나타나는지 확인하려면 루프 이후가 아닌 매 반복마다 메쉬를 업데이트해야합니다.

		mesh.triangles = triangles;
			yield return wait;

이제 단일 루프를 이중 루프로 전환하여 전체를 채웁니다. 다음 행으로 이동하려면 꼭지점 인덱스를 1씩 증가시켜야합니다. 왜냐하면 행당 타일보다 꼭지점이 하나 더 많기 때문입니다.

		int[] triangles = new int[xSize * ySize * 6];
		for (int ti = 0, vi = 0, y = 0; y < ySize; y++, vi++) {
			for (int x = 0; x < xSize; x++, ti += 6, vi++) {
				…
			}
		}

전체를 채웁니다.

https://thumbs.gfycat.com/InsignificantBlackandwhiteChihuahua-mobile.mp4

불러오는 중입니다...

 

보시다시피, 전체 그리드는 한 번에 한 행씩 삼각형으로 채워집니다. 일단 당신이 만족한다면, 모든 코루틴 코드를 제거할 수 있으므로 메쉬가 지체없이 생성됩니다.

 

	private void Awake () {
		Generate();
	}

	private void Generate () {
		GetComponent<MeshFilter>().mesh = mesh = new Mesh();
		mesh.name = "Procedural Grid";

		vertices = new Vector3[(xSize + 1) * (ySize + 1)];
		for (int i = 0, y = 0; y <= ySize; y++) {
			for (int x = 0; x <= xSize; x++, i++) {
				vertices[i] = new Vector3(x, y);
			}
		}
		mesh.vertices = vertices;

		int[] triangles = new int[xSize * ySize * 6];
		for (int ti = 0, vi = 0, y = 0; y < ySize; y++, vi++) {
			for (int x = 0; x < xSize; x++, ti += 6, vi++) {
				triangles[ti] = vi;
				triangles[ti + 3] = triangles[ti + 2] = vi + 1;
				triangles[ti + 4] = triangles[ti + 1] = vi + xSize + 1;
				triangles[ti + 5] = vi + xSize + 2;
			}
		}
		mesh.triangles = triangles;
	}
...더보기

단일 쿼드를 사용하지 않는 이유는 무엇일까요?

평평한 직사각형 표면을 만들 때 두 개의 삼각형으로 충분합니다. 이는 의심할 필요 없는 사실이며 복잡한 구조가 필요하단 것은 보다 많은 제어와 표현을 요구하는 것과 같습니다.

 

4. 추가 정점 데이터 생성

우리의 그리드는 현재 특이한 방식으로 켜져 있는데, 아직 메쉬에 법선을 부여하지 않았기 때문입니다. 기본 수직 방향은 (0,0,1)이며 이는 우리가 필요로 하는 것의 정반대 방향입니다.

...더보기

법선은 어떻게 작동하나요?

법선은 서페이스에 수직인 벡터입니다. 우리는 항상 단위 길이의 법선을 사용합니다. 그리고 그것들은 내부가 아닌 표면의 바깥을 가리킵니다.

 

법선은 광선이 표면에 닿는 각도를 결정하는 데 사용할 수 있습니다. 사용 방법에 대한 세부 사항은 쉐이더에 따라 다릅니다.

 

삼각형은 항상 평평하므로 법선에 대한 별도의 정보를 제공 할 필요는 없습니다. 하지만 그렇게 함으로써 우리는 속일 수 있습니다. 사실 정점에는 법선이 없고 삼각형이 있습니다. 커스텀 법선을 정점에 붙이고 삼각형을 가로 지르는 보간법을 사용함으로써 평평한 삼각형 대신 부들버게 곡면을 취하는 척 할 수 있습니다. 이 속임수는 당신이 메쉬의 날카로운 실루엣에 주의를 기울이지 않는 한 잘 보이지 않습니다.

법선은 정점별로 정의되므로 다른 벡터 배열을 채워야합니다. 또는 메쉬의 삼각형을 기준으로 법선 자체를 파악하도록 요청할 수 있습니다.

	private void Generate () {
		…
		mesh.triangles = triangles;
		mesh.RecalculateNormals();
	}
...더보기

법선은 어떻게 다시 계산됩니까?

Mesh.RecalculateNormals 방법은 각 정점의 법선을 계산하며, 그 정점과 연결된 삼각형을 알아 내고, 평평한 삼각형의 법선을 결정하고, 평균화하고, 결과를 정규화합니다.

법선의 유무에 따른 빛에 대한 반응

 

다음은 UV좌표입니다. 알베도 텍스처가 있는 머티리얼을 사용하더라도 격자가 현재 균일한 색상을 가지고 있음을 눈치 챘을 것 입니다. 이것은 우리가 UV 좌표를 제공하지 않으면 모두 0이 되기 때문에 의미가 있습니다.

 

텍스처를 우리의 전체 그리드에 맞게 만들려면 단순히 그리드 치수로 정점의 위치를 나눕니다.

		vertices = new Vector3[(xSize + 1) * (ySize + 1)];
		Vector2[] uv = new Vector2[vertices.Length];
		for (int i = 0, y = 0; y <= ySize; y++) {
			for (int x = 0; x <= xSize; x++, i++) {
				vertices[i] = new Vector3(x, y);
				uv[i] = new Vector2(x / xSize, y / ySize);
			}
		}
		mesh.vertices = vertices;
		mesh.uv = uv;

잘못된 UV좌표, 클래핑 대 랩핑 텍스처

이제 텍스처가 나타나지만 전체 그리드를 덮지는 않습니다. 정확한 모양은 텍스처의 랩 모드가 클램프 또는 반복으로 설정되어 있는지 여부에 달려 있습니다. 이는 현재 정수를 정수로 나눠서 결과가 다른 정수가 되기 때문에 발생합니다. 전체 그리드에서 0과 1사이의 올바른 좌표를 얻으려면 올바른 데이터 타입을 사용하도록 해야합니다.

	uv[i] = new Vector2((float)x / xSize, (float)y / ySize);

이제 텍스처가 전체 표에 투영됩니다. 그리드의 크기를 10으로 설정했으므로 텍스처는 수평으로 늘어나게됩니다. 이는 재질의 텍스처의 타일링 설정을 조정하여 해결할 수 있습니다. 설정에 따라 (2,1) U 좌표가 두배가 됩니다. 텍스처 반복되도록 설정되면 두 개의 정사각형 타일이 표시됩니다.

UV 좌표 정정 후 타일링 1,1과 2,1

 

표면에 더 많은 디테일을 추가하는 또 달느 방법은 노멀 맵을 사용하는 것 입니다. 이 맵에는 색상으로 인코딩 된 법선 벡터가 퐇마됩니다. 표면에 적용하면 정점 법선을 사용하여 만들 수 있는 것 보다 훨씬 자세한 광 효과가 발생합니다.

울퉁불퉁한 표면, 극적 효과를 위해 금속 효과도 줬습니다.

이 자료를 그리드에 적용해도 당장엔 큰 어려움이 없습니다. 먼저 메쉬에 접선 벡터를 추가해야합니다.

...더보기

탄젠트가 어떻게 작동합니까?

노말 맵은 접선 공간에 정의됩니다. 물체의 표면을 따라 흐르는 3D 공간이라 생각하면 됩니다. 이 접근법은 다른 장소와 방향에서 동일한 법선 맵을 적용 할 수 있게합니다.

 

표면 법선은 이 공간에서 상향을 나타냅니다. 하지만 어떤면이 맞는지 알수없습니다. 따라서 이것을 탄젠트에 의해 정의됩니다. 이상적으로 두 벡터 사이의 각도는 90도입니다. 이들의 교차 곱은 3D공간을 정의하는 데 필요한 세 번째 방향을 산출합니다. 실제로 각도는 종종 90도가 아니지만 결과는 여전히 충분합니다.

 

접선은 3D벡터이지만 Unity는 실제로 4D 벡터를 사용합니다. 네 번째 구성 요소는 항상 -1 또는 1로, 앞으로 또는 뒤로 세 번째 탄젠트 공간 차원의 방향을 제어하는데 사용됩니다. 이것은 일반지도의 미러링을 용이하게 합니다. 보통지도는 사람과 같은 양방향 대칭을 사용하는 3D 모델에서 자주 사용됩니다. Unity의 쉐이더가 이 계산을 수행하는 방식은 -1을 사용해야합니다.

이미 평평한 표면을 가지고 있기 때문에, 모든 접선은 단순히 같은 방향을 향하고 있습니다. 오른쪽 방향으로 말입니다.

		vertices = new Vector3[(xSize + 1) * (ySize + 1)];
		Vector2[] uv = new Vector2[vertices.Length];
		Vector4[] tangents = new Vector4[vertices.Length];
		Vector4 tangent = new Vector4(1f, 0f, 0f, -1f);
		for (int i = 0, y = 0; y <= ySize; y++) {
			for (int x = 0; x <= xSize; x++, i++) {
				vertices[i] = new Vector3(x, y);
				uv[i] = new Vector2((float)x / xSize, (float)y / ySize);
				tangents[i] = tangent;
			}
		}
		mesh.vertices = vertices;
		mesh.uv = uv;
		mesh.tangents = tangents;

울퉁불퉁해 보일 뿐인 평평한 표면

이제 간단한 메쉬를 작성하고 메쉬를 사용하여 복잡한 메쉬를 만드는 방법을 알았습니다. 메쉬에는 정점 위치와 삼각형, 노말 UV 좌표(최대 4세트)가 필요하며 종종 접선도 필요합니다. 유니티의 표준 쉐이더에는 그것들을 사용하지 않지만 정점 색상을 추가할 수도 있습니다. 이러한 색상을 사용하는 쉐이더를 직접 만들 수도 있지만 다른 튜토리얼에서는 이 방법을 사용합니다.

 

이 문서를 다 익혔다면 Ronded Cub 튜토리얼로 넘어갈 수 있습니다.

 

파일 : 유니티 패키지

불러오는 중입니다...

문서 : PDF

반응형