XR 플밍 - 8. UnityEngine3D 입문 - 프로젝트 GwisinRun 3일차 (4/30)

이형원·2025년 4월 30일
0

XR플밍

목록 보기
59/215

1. 에셋의 선정

GwisinRun의 기능 개발도 계속되어가고 있으며, 내가 담당하고 있는 인게임 UI, 트리거 이벤트 외에도 다른 팀원들의 기능도 계속 업데이트되어 가고 있었다. 이제는 슬슬 각자 개발한 기능을 합쳐 게임 테스트를 진행해야 할 준비가 되어가고 있었다.

이제는 계속 테스트 씬에서만 진행하던 것을 에셋을 입혀서 진행할 때가 되었다.
우리는 주요 맵 에셋을 Haunted Mansion Set으로 학원에서 제공해주는 에셋을 활용하기로 결정했다.

공포 탈출/러너 게임 테마로 어울리는 테마로 게임 에셋을 선정하고 이제까지 만들었던 작업물에서 실제 에셋을 입혀 작동 여부를 확인하기 시작했다.
에셋 적용 전 기본 도형으로 구현한 기능이었으나, 해당 애니메이션은 물체 이미지의 형태가 변하더라도 그대로 반영된다는 사실을 알게 되었다. (다만 애니메이션이 잘 작동하는지 한 번 씩 체크할 필요성이 있음.)

아래는 프리팹을 반영한 문과 철창 문 이벤트이다.


  • 철창 문과 스위치

이와 같이 애니메이션에는 해당 물체의 Position 변경 및 Rotation 변경만 기록되는 사안이다 보니, 애니메이션에 사용되는 모델이 변해도 적용된다는 사실을 알게 되었다.

2. 앵커와 TextMeshPro에 대한 공부

전날 작업을 마무리하면서 신경 쓰이던 부분에 대해 다시 손 보기로 했다. 그건 내가 만들었던 기능 중 ItemInfoUIController에 대한 것이었다.

지금껏 기능은 정상 작동하였고 심지어 플레이어간 거리를 판정해 아이템의 UI 출력 여부를 결정하는 기능까지 추가했다. 다만 아직 해결하지 못한 문제가 있었다.
그건 화면 변경에 따른 UI 출력에 대한 문제였다.

팀장님이 맡은 게임매니저 쪽에는 화면 해상도를 변경할 수 있는 기능이 있기 때문에 내가 만든 UI 또한 해상도의 영향을 받을 수 있다. 아직 UI와 관련된 세팅 및 에셋이 결정된 상황은 아니지만, 그래도 미리 세팅을 해 두는 편이 좋아 보였다.

2.1 앵커에 관하여

앵커는 UI의 기준점이 되는 부분이라고 공부해본 바가 있었다. 하지만 앵커가 단순히 한 점이 아니라 공간을 기준점으로 둘 수도 있고 어떻게 작용하는지 잘 알아봐야 할 것 같았다.

해당 내용은 유니티 메뉴얼을 참고하여 비교해보았다.
https://docs.unity3d.com/kr/560/Manual/UIBasicLayout.html

  • 점을 기준으로 하는 경우

  • 코너를 기준으로 하는 경우

이와 같이 앵커를 설정했을 경우, 가로로 늘렸을 때 버튼의 길이 또한 가로로 길어지고, 세로로 늘렸을 때 버튼은 길이 변화 없이 일정한 높이에 있게 된다.

  • 그러면 내가 원하는 출력을 위한 앵커 설정은?

나는 마우스를 갖다댄 위치의 우상단의 공간에 UI가 표시되기를 원하므로, 최종적으로 조정한 앵커의 위치는 다음과 같이 되었다.

이와 같은 부분을 반영하여 UI가 어느정도 일정하게 나오도록 출력할 수 있었다.

2.2 TextMeshPro에 대하여

해당 문제를 해결하는 동안 계속 이상한 현상이 벌어졌다. 자꾸만 글자가 이상하게 출력되는 문제가 발생했던 것이다.
글자가 넘치면 Overflow가 되도록 설정을 해놓았는데, 자꾸만 글자 공간이 넘어가면 엔터를 친 것처럼 글자가 출력되는 문제가 발생한 것이다. 이건 아무래도 TextMeshPro에 대한 기능 이해가 부족한 것으로 판단하여 영상을 참고하여 어떻게 기능을 작성해야 하는지에 대해서 고민했다.

참고영상 (TextMeshPro Overflow Controls)
https://www.youtube.com/watch?v=_mvbntvQjts

  • Overflow

텍스트 공간 밖으로 텍스트가 튀어나가도 텍스트가 그대로 유지되도록 하는 것

  • Truncate

텍스트 공간 밖으로 나간 텍스트만 잘라서 출력

  • Elipsis

텍스트 공간 밖으로 나간 텍스트를 ...으로 출력

  • Masking

텍스트 공간 밖으로 나간 텍스트만 잘라서 출력, 다만 Truncate은 글자를 통째로 자르나 Masking은 튀어나온 글자까지 출력한다.

  • 중요한 점 - Wrapping

내가 지금까지 겪고 있던 문제는 결국 Wrapping이 활성화되어 있었기 때문이었다.
Wrapping은 글자가 공간보다 좁은 상황일 때 자동으로 줄바꿈을 해 주는 기능이다.

Wrapping과 Overflow가 동시에 활성화되어 있을 때 Wrapping이 먼저 진행되어 줄바꿈이 자동으로 진행되고, 그 다음으로 줄바꿈을 하고도 공간이 부족하면 그제서야 글자가 공간 밖으로 넘치도록 설정되어 있다.

즉 내가 원하는 상황을 만들기 위해선 Wrapping을 해제하고 Overflow를 적용시켜야지 구현할 수 있는 기능이란 것이다. 이로서 UI크기가 변해도 아이템 글자가 정상 출력되는 시스템을 적용할 수 잇었다.

3. 책상 프리팹의 발견, 그리고 책상의 기능 개발

사용할 에셋을 결정하고, 여기에 있는 소품들을 찾아보다가, 책상 프리팹이 있는 것을 발견했다.

책상 부분도 있고 책상의 서랍 부분도 따로 모델이 만들어져 있던 터라 새로운 기능 개발에 제격이라고 판단했다. 책상의 서랍 안에 아이템을 넣고, 플레이어가 서랍과 상호작용하여 열쇠를 찾는 기능을 위해 나는 서랍을 여는 기능을 개발해보기로 했다.
덤으로 책상 아래의 공간도 있으니, 숨을 수 있는 공간으로도 제격이었다. 숨을 수 있는 공간에 대한 이벤트 제작도 진행하기로 했다.

3.1 서랍 열기 애니메이션 설정, 그리고 스크립팅

우선은 애니메이션부터 만들었다. 애니메이션은 이때까지 만들었던 것처럼 물체의 Position을 옮기는 방식으로 진행했고, 이 애니메이션 하나로 다른 선반도 정상작동하는 것을 확인했다. 또한 IsOpen bool 변수로 열리면 서랍이 열리고, 닫히는 것을 구현했다.
이제 다음으로 해당 부분을 코드로 구현하려고 했는데, 처음에는 어제 기능 개편을 했던 SwitchController를 재활용 해 보려고 했다. 하지만 이걸 그대로 적용시켜 보려고 하니 문제가 생겼다.

  • 가만 생각해 보면 두 서랍의 Trigger 영역이 겹친다. 여기에 내가 이전에 만들어 둔 SwitchController를 그대로 쓰면 계속 한 쪽 서랍이 번갈아가면서 열리는 이상한 움직이 될 것이다.
    우리가 원하는 의도는 특정 서랍과 상호작용할 때 해당 서랍이 작동하도록 하는 의도이므로, 새로운 서랍 스크립트가 필요하다.

이에 따라 새로운 서랍 스크립트를 만들면서 아래와 같은 방법들을 생각했다.

  1. 서랍의 트리거 영역을 두 개로 두어 제작한다면?

문을 만들었을 때의 방법에 착안하여 이와 같은 방법을 생각했지만, 또 코드를 여러 개로 나누는 방법은 비효율적이라고 생각했다. 문 부분도 리팩토링해야 하는 요소인데, 다른 방법은 없을까?

  1. 해당 트리거 영역에 들어왔을 때 서랍의 종류를 판정하는 방법

이 방법이 지금 상황에서 더 알맞은 방법이라고 생각했다. 하지만 해당 방법을 어떻게 실행해야 할까? 고민을 해 보다 Raycast를 쏘아 해당 물체가 무엇인지 판정하는 방식으로 처리해보기로 했다.

  • Raycast를 어떻게 쏠 것인가? ScreenPointToRay()

우리가 지금 만드는 게임은 1인칭 공포게임이란 것에 착안했다. 이전에 강사님이 진행하셨던 특강에서, RTS 게임을 만들기 위한 방법으로 시야 기준으로 Raycast를 쏴서 물체를 판정하는 방법에서 착안하였다.
우선 선반에 Collider를 만들어 준 후 해당 선반에 Ray를 받아와서 애니메이션의 작동을 유도한다.
(Collider는 선반의 모양을 정확히 판정하기 위해 Mesh Collider를 사용했다.)

또한 여기서 기존에는 bool 변수를 따로 만들고 그냥 해당 변수를 참 거짓 왔다갔다 하는 방식으로 만들었는데, 이렇게 하면 문제가 생긴다는 사실 또한 인지했다.

만약 첫 번째 선반을 열었으면 변수는 true이고, 이 상태에서 아래 선반을 누르면 false가 되어 아래 선반이 열리지 않는다. 이와 같은 번갈아 가면서 여는 상황에서 서랍의 움직임을 제대로 반영하기 위해선 해당 변수 자체를 참조해야 한다.

이와 같은 문제를 해결하기 위해서 애니메이터의 bool 변수 값을 알아낼 수 있는 방법이 없나 찾아봤는데 방법이 있었다.

Animator animator

animator.SetBool("IsOpen", true) // 해당 변수값을 변경하는 것
bool IsDrawerOpen = animator.GetBool("IsOpen") // 해당 변수의 bool 값을 가져오는 것

이와 같은 점을 이용하여 코드를 아래와 같이 작성했다.

using UnityEngine;

public class DeskDrawerController : MonoBehaviour, IInteractable
{
    private bool m_interactable = false;

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Player")
            m_interactable = true;
    }
    private void OnTriggerExit(Collider other)
    {
        if (other.gameObject.tag == "Player")
            m_interactable = false;
    }

    public void Interact()
    {
        if (m_interactable)
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            Debug.DrawRay(ray.origin, ray.direction * 10, Color.red, 0.1f);

            if (Physics.Raycast(ray, out RaycastHit hitInfo))
            {
                Debug.Log(hitInfo.collider.gameObject.name);
                DrawerAnimator(hitInfo.collider.gameObject.GetComponent<Animator>());
            }
        }
    }

    private void DrawerAnimator(Animator animator)
    {
        bool isOpen = animator.GetBool("IsOpen");
        isOpen = !isOpen;
        animator.SetBool("IsOpen", isOpen);
    }
}

게임 오브젝트의 애니메이션을 직접 찾아와서 재생시키는, 컴퓨터에는 사실 부담이 꽤 가는 방식이다. 하지만 해당 코드에 대한 리팩토링은 추후에 진행하기로 하고, 우선 애니메이션이 제대로 작동하는지 확인했다.

원하는 대로 선반의 열기와 닫기 기능을 구현했다.

3.2 플레이어의 숨기 기능 구현

플레이어의 숨기 기능의 경우 처음에는 내가 전부 구현해야 하는 것으로 알았으나, 이미 플레이어를 담당한 팀원이 플레이어의 숨기 기능을 구현해 놓았다고 한다.
그래서 플레이어 담당자에게 어떤 걸 해 놓으면 되는지 물어봤는데, 플레이어의 숨기 기능이 HideObject라는 tag를 통해서 작동한다는 사실을 알려주었다.

따라서 여기서 내가 해야 할 작업은 책상에서의 Trigger 영역을 설정하고, 해당 영역을 HideObject로 설정만 해 놓으면 되는 것이었다.

이제 슬슬 이와 같이 다른 팀원의 작업물과 기능을 합쳐가기 시작했다.

4. 충돌 발생 두 번째와 교훈, 애니메이션은 함부로 수정하지 말자

내가 지금까지 만든 작업물에서, 프리팹 이미지를 적용해도 제대로 반영되어 애니메이션이 원활하게 재생되고 작동하는 것을 확인했다. 이제 프리팹을 적용하면서 애니메이션을 약간 조정할 필요가 있어서 세부 조정만 하고 PR을 시도했다.

또 충돌이 발생했다. 심지어 내가 직접 만든 작업물인데 내 작업물이랑 충돌이 난 것이다. 처음엔 왜 애니메이션에서 충돌이 난 건지 고민을 했었는데, 가만 생각해보니 철창이 너무 높게까지 올라가서 애니메이션으로 높이 조정을 한 것이 원인이었다.

이런 충돌이 발생한 경우를 검색해 보았지만 이런 특수한(?) 케이스는 거의 찾아볼 수도 없었고 방법도 찾기 어려웠다. 다만 그래서 문제가 생긴 부분의 코드를 쭉 살펴 보니, 딱 봐도 내용이 변한 부분이, 단순히 내가 높이를 낮춘 것만으로 단순한 수치 변화가 일어난 게 아니라는 걸 알게 되었다.

애니메이션에서 Position의 위치를 조금 변경하는 것만으로도 많은 요소가 변경된다. 이로 인해 값이 변동하는 것은 Git에서 해당 파일 내용 변경이 아니라 충돌로 인식한다는 사실을 알게 되었다.

결국 어떻게 했냐, 그냥 삭제하고 다시 만들었다...

지금 단계에서는 이게 최선이라고 생각하여 이와 같이 반영하고 팀장님께 PR을 받았다. 하지만 대체 왜 이런 충돌이 발생했는지 차후에 다시 조사해 볼 생각이 있으며, 애니메이션 편집에 있어서는 상당히 신중해질 필요가 있다는 것을 느꼈다.

profile
게임 만들러 코딩 공부중

0개의 댓글