Chapter 9-2. 건축 : 그리드 및 회전, 건축 제한

Date:     Updated:

카테고리:

태그:

인프런에 있는 케이디님의 [유니티 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;
        }
    }

✈ 프리뷰를 정수 단위로 딱딱 맞춰 그리드 방식으로 움직이게 하기

image

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가 충돌한 그 지점이다. (프리뷰가 크로스헤어를 따라다녀야 함)
  • _locationx, z 값을 반올림하여 가까운 정수로 만든다.
  • _locationy 값은 수직이니까 정수 단위가 아닌 미세하게 0.1 단위로만 움직이도록, 0.1f 로 나눈 값을 반올림 해주고 이에 다시 0.1 을 곱해주었다.
  • 위와 같이 반올림 조정한 _location를 프리뷰의 위치로 설정한다.


✈ Q, E 누르면 프리뷰 회전하게 하고 그 회전한대로 건축

image

이렇게 QE를 누르면 시계방향, 반시계방향으로 프리뷰를 회전 시킬 수 있고 원하는 상태로 건축할 수 있다.

    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) 인 상태로 프리뷰를 실제 건축물로 생성하도록 한다.


🚀 건축 제한

✈ 건축물 프리팹 생성

image

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 충돌을 했을 경우 이 위에다가 건축할 수 있게 할 것이다.
    • 📜Building.cs 를 붙여준다. (밑에 후술)

image

Phillar 라는 이름의 건축물로, 이 건축물은 오로지 Foundation 위에서만 지을 수 있다는 설정을 붙였다.

  • Box Collider 를 붙여주었다.
    • 플레이어나 다른 오브젝트들과 충돌 처리가 가능해야 하므로.
    • 또 플레이어나 동물들이 관통할 수 없도록 Is Trigger는 체크하지 않는다.
    • Phillar 전체적인 모습을 감싸는 콜라이더.
  • Layer 를 Building 으로 설정한다.
    • 마찬가지로 메인카메라의 쿨링마스크에 추가해주어야 렌더링 된다.
  • 빈 오브젝트인 자식 오브젝트 Collider
    • Box Collider 를 붙여주었다.
      • Phillar 상단에 있는 작은 정사각형 콜라이더로 딱 이 부분에만 건축물을 지을 수 있도록 할 것이다.
        • Pillar 위에도 건축물 짓기 가능
      • Is Trigger를 체크해주어야 건축 여부를 판별할 때 📜PreviewObject.cs에서 OnTriggerExit, OnTriggerEnter 를 호출할 수 있다.
    • “Structure” 라는 태그를 붙여준다.
      • 큰 Box Collider 말고 자식 오브젝트에 붙인 이 부분적인 Box Collider 를 가진 빈 오브젝트인 자식 오브젝트 Collider에게만 붙어야 한다.
      • 이 태그를 가진 오브젝트와 Trigger 충돌을 했을 경우 이 위에다가 건축할 수 있게 할 것이다.
    • 📜Building.cs 를 붙여준다. (밑에 후술)

image

요런식으로 건축할 수 있다. Foundation 의 가운데에 있는 부분 콜라이더 위에만 Pillar 를 지을 수 있고 Pillar 의 꼭대기에 있는 부분에만 또 Foundation 같은 건축물을 지을 수 있다. 이런 부분 콜라이더들에만 “Structure” 태그를 붙였고 이에 대해서만 건축 가능하도록 밑에서 코딩할 예정.

image

탭과 슬롯도 이런식으로 만들 것이다. (자세한건 다음 포스트에서)


✈ 건축물 프리뷰 프리팹 생성

image

  • Foundation 프리뷰 (빈오브젝트)
    • Box Collider
      • 프리뷰는 관통이 가능하도록(관통 되면 건축할 수 없도록 빨간색으로 변해야됨) Is Trigger를 체크
    • 📜PreviewObject.cs 붙이고 할당
    • Rigidbody
      • OnTriggerEnter, Exit 이 일어나는 조건으로 충돌 대상 둘 중 하나는 Rigidbody를 가지고 있어야 함
    • Foundation 메시 (자식) 👉 기본 상태로 초록색 머테리얼 부여
  • Pillar 프리뷰도 마찬갖지.

image

  • 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 로 바꿔줄 것이다.

image

Foundation 프리뷰의 needTypeNormal로 할당 한다. Foundation 은 어디에든 지어질 수 있다.

image

Pillar 프리뷰의 needTypeFoundation로 할당한다. 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인지도 함께 체크해주어야 한다.


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Unity Lesson 3 카테고리 내 다른 글 보러가기

댓글 남기기