Chapter 9-2. 건축 : 그리드 및 회전, 건축 제한
카테고리: Unity Lesson 3
태그: Unity Game Engine
인프런에 있는 케이디님의 [유니티 3D] 실전! 생존게임 만들기 - Advanced 강의를 듣고 정리한 필기입니다. 😀
🌜 강의 들으러 가기 Click
건축 시스템
🚀 그리드 및 회전
📜CraftManual.cs
private void PreviewPositionUpdate()
{
if (Physics.Raycast(tf_Player.position, tf_Player.forward, out hitInfo, range, layerMask))
{
if (hitInfo.transform != null)
{
Vector3 _location = hitInfo.point;
if (Input.GetKeyDown(KeyCode.Q))
go_Preview.transform.Rotate(0f, -90f, 0f);
else if (Input.GetKeyDown(KeyCode.E))
go_Preview.transform.Rotate(0f, +90f, 0f);
_location.Set(Mathf.Round(_location.x), Mathf.Round(_location.y / 0.1f) * 0.1f, Mathf.Round(_location.z));
go_Preview.transform.position = _location;
}
}
}
private void Build()
{
if(isPreviewActivated && go_Preview.GetComponent<PreviewObject>().isBuildable())
{
Instantiate(go_Prefab, go_Preview.transform.position, go_Preview.transform.rotation);
Destroy(go_Preview);
isActivated = false;
isPreviewActivated = false;
go_Preview = null;
go_Prefab = null;
}
}
✈ 프리뷰를 정수 단위로 딱딱 맞춰 그리드 방식으로 움직이게 하기
Vector3 _location = hitInfo.point;
_location.Set(Mathf.Round(_location.x), Mathf.Round(_location.y / 0.1f) * 0.1f, Mathf.Round(_location.z));
go_Preview.transform.position = _location;
_location
은 메인카메라로부터 쏜 Raycast가 충돌한 그 지점이다. (프리뷰가 크로스헤어를 따라다녀야 함)_location
의x
,z
값을 반올림하여 가까운 정수로 만든다._location
의y
값은 수직이니까 정수 단위가 아닌 미세하게 0.1 단위로만 움직이도록, 0.1f 로 나눈 값을 반올림 해주고 이에 다시 0.1 을 곱해주었다.- 위와 같이 반올림 조정한
_location
를 프리뷰의 위치로 설정한다.
✈ Q, E 누르면 프리뷰 회전하게 하고 그 회전한대로 건축
이렇게 Q
와 E
를 누르면 시계방향, 반시계방향으로 프리뷰를 회전 시킬 수 있고 원하는 상태로 건축할 수 있다.
if (Input.GetKeyDown(KeyCode.Q))
go_Preview.transform.Rotate(0f, -90f, 0f);
else if (Input.GetKeyDown(KeyCode.E))
go_Preview.transform.Rotate(0f, +90f, 0f);
Q
를 누르면 프리뷰를 Y축 중심으로 -90도로 회전시키기. E
를 누르면 프리뷰를 Y축 중심으로 90도로 회전시키기.
private void Build()
{
if(isPreviewActivated && go_Preview.GetComponent<PreviewObject>().isBuildable())
{
Instantiate(go_Prefab, go_Preview.transform.position, go_Preview.transform.rotation);
Destroy(go_Preview);
isActivated = false;
isPreviewActivated = false;
go_Preview = null;
go_Prefab = null;
}
}
좌클 입력으로 Build()가 호출되어 건축물을 지을 때, 위에서 Q
, E
눌렀을 때 회전시켰던 상태로, 그리고 그리드 단위로 움직였던 그대로 건설할 수 있어야 하므로 Instantiate(go_Prefab, go_Preview.transform.position
, go_Preview.transform.rotation
) 인 상태로 프리뷰를 실제 건축물로 생성하도록 한다.
🚀 건축 제한
✈ 건축물 프리팹 생성
Foundation 이라는 이름의 건축물로, Terrain처럼 이 건축물 위에 다른 건축물을 지을 수 있다.
- Box Collider 를 붙여주었다.
- 플레이어나 다른 오브젝트들과 충돌 처리가 가능해야 하므로.
- 또 플레이어나 동물들이 관통할 수 없도록
Is Trigger
는 체크하지 않는다. - Foundation의 전체적인 모습을 감싸는 콜라이더.
- Layer 를 Building 으로 설정한다.
- 마찬가지로 메인카메라의 쿨링마스크에 추가해주어야 렌더링 된다.
- 빈 오브젝트인 자식 오브젝트
Collider
- Box Collider 를 붙여주었다.
- Foundation 중앙에 작은 정사각형 콜라이더로 오로지 딱 이 부분에만 건축물을 지을 수 있도록 할 것이다.
Is Trigger
를 체크해주어야 건축 여부를 판별할 때 📜PreviewObject.cs에서 OnTriggerExit, OnTriggerEnter 를 호출할 수 있다.
- “Structure” 라는 태그를 붙여준다.
- 큰 Box Collider 말고 자식 오브젝트에 붙인 이 부분적인 Box Collider 를 가진 빈 오브젝트인 자식 오브젝트
Collider
에게만 붙어야 한다. - 이 태그를 가진 오브젝트와 Trigger 충돌을 했을 경우 이 위에다가 건축할 수 있게 할 것이다.
- 큰 Box Collider 말고 자식 오브젝트에 붙인 이 부분적인 Box Collider 를 가진 빈 오브젝트인 자식 오브젝트
- 📜Building.cs 를 붙여준다. (밑에 후술)
- Box Collider 를 붙여주었다.
Phillar 라는 이름의 건축물로, 이 건축물은 오로지 Foundation 위에서만 지을 수 있다는 설정을 붙였다.
- Box Collider 를 붙여주었다.
- 플레이어나 다른 오브젝트들과 충돌 처리가 가능해야 하므로.
- 또 플레이어나 동물들이 관통할 수 없도록
Is Trigger
는 체크하지 않는다. - Phillar 전체적인 모습을 감싸는 콜라이더.
- Layer 를 Building 으로 설정한다.
- 마찬가지로 메인카메라의 쿨링마스크에 추가해주어야 렌더링 된다.
- 빈 오브젝트인 자식 오브젝트
Collider
- Box Collider 를 붙여주었다.
- Phillar 상단에 있는 작은 정사각형 콜라이더로 딱 이 부분에만 건축물을 지을 수 있도록 할 것이다.
- Pillar 위에도 건축물 짓기 가능
Is Trigger
를 체크해주어야 건축 여부를 판별할 때 📜PreviewObject.cs에서 OnTriggerExit, OnTriggerEnter 를 호출할 수 있다.
- Phillar 상단에 있는 작은 정사각형 콜라이더로 딱 이 부분에만 건축물을 지을 수 있도록 할 것이다.
- “Structure” 라는 태그를 붙여준다.
- 큰 Box Collider 말고 자식 오브젝트에 붙인 이 부분적인 Box Collider 를 가진 빈 오브젝트인 자식 오브젝트
Collider
에게만 붙어야 한다. - 이 태그를 가진 오브젝트와 Trigger 충돌을 했을 경우 이 위에다가 건축할 수 있게 할 것이다.
- 큰 Box Collider 말고 자식 오브젝트에 붙인 이 부분적인 Box Collider 를 가진 빈 오브젝트인 자식 오브젝트
- 📜Building.cs 를 붙여준다. (밑에 후술)
- Box Collider 를 붙여주었다.
요런식으로 건축할 수 있다. Foundation 의 가운데에 있는 부분 콜라이더 위에만 Pillar 를 지을 수 있고 Pillar 의 꼭대기에 있는 부분에만 또 Foundation 같은 건축물을 지을 수 있다. 이런 부분 콜라이더들에만 “Structure” 태그를 붙였고 이에 대해서만 건축 가능하도록 밑에서 코딩할 예정.
탭과 슬롯도 이런식으로 만들 것이다. (자세한건 다음 포스트에서)
✈ 건축물 프리뷰 프리팹 생성
- Foundation 프리뷰 (빈오브젝트)
- Box Collider
- 프리뷰는 관통이 가능하도록(관통 되면 건축할 수 없도록 빨간색으로 변해야됨)
Is Trigger
를 체크
- 프리뷰는 관통이 가능하도록(관통 되면 건축할 수 없도록 빨간색으로 변해야됨)
- 📜PreviewObject.cs 붙이고 할당
- Rigidbody
- OnTriggerEnter, Exit 이 일어나는 조건으로 충돌 대상 둘 중 하나는 Rigidbody를 가지고 있어야 함
- Foundation 메시 (자식) 👉 기본 상태로 초록색 머테리얼 부여
- Box Collider
- Pillar 프리뷰도 마찬갖지.
- Foundation 과 Phillar 건축을 위해 원소를 추가한다. 각각 프리팹들을 할당해준다.
Terrain
,Building
레이어를 가진 오브젝트 위에만 건축 가능하도록 할 것이므로 layerMask 에서Building
을 추가로 선택했다.
📜Building.cs
public class Building : MonoBehaviour
{
public Type type;
public enum Type // 건축물 타입 지정
{
Normal, // 건축물이 아닌 것들
Wall, // 벽
Foundation, // 토대
Pillar // 기둥
}
}
Terrain을 제외하고, 그 위에 건축이 가능한 Foundation의 자식 오브젝트
Collider
와, Pillar의 자식 오브젝트Collider
에 붙인다.
건축 타입을 정의하기 위해 enum Type
을 정의한다.
- Foundation의 자식 오브젝트
Collider
는 에디터의 📜Building 에서type
을 “Foundation”으로 할당한다. - Foundation의 자식 오브젝트
Collider
는 에디터의 📜Building 에서type
을 “Pillar”으로 할당한다.
📜PreviewObject.cs
public Building.Type needType;
private bool needTypeFlag;
needType
은 이 스크립트가 붙은 프리뷰 오브젝트가 건축될 수 있는 곳의 타입.needTypeFlag
는 OnTriggerEnter, OnTriggerExit 에서 내가 건축될 수 있는 그 곳에 건축이 가능한 상태면 True 로 바꿔줄 것이다.
Foundation 프리뷰의 needType
은 Normal
로 할당 한다. Foundation 은 어디에든 지어질 수 있다.
Pillar 프리뷰의 needType
은 Foundation
로 할당한다. Pillar 는 오로지 Foundation 위에서만 지어질 수 있도록 할 것이다.
private void ChangeColor()
{
if (needType == Building.Type.Normal)
{
if (colliderList.Count > 0)
SetColor(red);
else
SetColor(green);
}
else
{
if (colliderList.Count > 0 || !needTypeFlag)
SetColor(red);
else
SetColor(green);
}
}
private void OnTriggerEnter(Collider other)
{
if (other.transform.tag == "Structure")
{
if (other.GetComponent<Building>().type == needType)
needTypeFlag = true;
else
colliderList.Add(other);
}
else
{
if (other.gameObject.layer != layerGround && other.gameObject.layer != IGNORE_RAYCAST_LAYER)
colliderList.Add(other);
}
}
private void OnTriggerExit(Collider other)
{
if (other.transform.tag == "Structure")
{
if (other.GetComponent<Building>().type == needType)
needTypeFlag = false;
else
colliderList.Remove(other);
}
else
{
if (other.gameObject.layer != layerGround && other.gameObject.layer != IGNORE_RAYCAST_LAYER)
colliderList.Remove(other);
}
}
public bool isBuildable()
{
if (needType == Building.Type.Normal)
return colliderList.Count == 0;
else
return colliderList.Count == 0 && needTypeFlag;
}
- ChangeColor
- 내가 건축 가능한 곳이
Normal
이 아니라면 나는 특정 건축물 위에서만 지어질 수 있는 건축물이다. Pillar 처럼.- 이 경우에는
needTypeFlag
값 또한 체크를 해주어야한다. 내가 건축될 수 있는 그 곳에 지금 건축이 가능한 상태인지.
- 이 경우에는
- 내가 건축 가능한 곳이
- OnTriggerEnter
- Trigger 충돌한 대상이 “Structure” 태그가 붙은 것이라면, 즉 건축물이라면
- 내가 지어질 수 있는 그
needType
과 일치하는 건축물인지를 검사해야 한다.- 일치한다면 건축물이 지어질 수 있도록
needTypeFlag
를 True 로 변경 - 일치하지 않는다면 내가 위에 지어질 수 없는 건축물이므로 콜라이더 리스트에 추가
- 일치한다면 건축물이 지어질 수 있도록
- OnTriggerExit
- OnTriggerEnter 와 반대로 해주면 됨
- isBuildable
- 내가 지어질 수 있는 곳이 특정 건축물이라면, 즉
Normal
이 아닌 건축물이라면- 실제 건축물을 지을 때
needTypeFlag
가 True인지도 함께 체크해주어야 한다.
- 실제 건축물을 지을 때
- 내가 지어질 수 있는 곳이 특정 건축물이라면, 즉
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기