게임 개발 엔진으로는 Unity와 Unreal Engine이 주로 사용됩니다. 다양한 플랫폼 지원과 에셋 스토어를 통해 필요한 리소스를 쉽게 구할 수 있는 Unity 엔진에 대해 공부하고자 합니다. 이번 포스트에서는 Unity에서 주로 사용되는 MonoBehavior 클래스, 머티리얼, 벡터 연산, 게임 오브젝트의 움직임 방법, 코루틴, UI 시스템, 애니메이션, 그리고 FBX 파일 임포트에 대해 정리하려고 합니다.



MonoBehavior 클래스


Unity에서 ‘MonoBehavior’ 클래스를 상속받는 클래스들은 다양한 생명 주기 함수를 통해 게임 오브젝트의 행동을 제어합니다. 이러한 생명 주기 함수들은 특정 시점에 자동으로 호출됩니다.

  • 초기화 관련 함수
    Awake() : 스크립트 인스턴스가 로드될 때 호출됩니다.(모든 오브젝트 초기화 된 후 호출됨) 일반적으로 데이터 초기화나 싱글톤 패턴 구현에 사용됩니다.
    OnEnable() : 게임 오브젝트가 활성화될 때 호출됩니다. 게임 오브젝트가 비활성화된 후 다시 활성화될 때마다 호출됩니다. 오브젝트 풀을 사용한 게임 오브젝트 관리에서 사용됩니다.
    Start() : Awake와 OnEnable 함수가 호출된 후에 한 번만 호출됩니다. 주로 게임 오브젝트가 첫 번째 프레임 업데이트를 준비할 때 사용됩니다.

  • 업데이트 관련 함수
    Update() : 매 프레임마다 호출됩니다. 주로 게임 오브젝트의 상태를 지속적으로 체크하고 업데이트하는 데 사용됩니다.
    FixedUpdate() : 물리 연산과 관련된 업데이트를 수행하는 함수로, 고정된 시간 간격마다 호출됩니다. 물리 연산이나 Rigidbody와 관련된 작업을 수행할 때 사용됩니다.
    LateUpdate() : 모든 Update 함수가 호출된 후에 호출됩니다. 다른 게임 오브젝트의 업데이트가 끝난 후 후처리가 필요할 때 사용됩니다.

  • 충돌 관련 함수
    OnTriggerEnter(Collider other) : 트리거 콜라이더에 다른 콜라이더가 들어올 때 호출됩니다.
    OnCollisionEnter(Collision collision) : 물리적 충돌이 시작될 때 호출됩니다. 실제 충돌이 발생할 때 충돌 처리 로직을 작성할 때 사용됩니다.

  • 종료 관련 함수
    OnDisable() : 게임 오브젝트가 비활성화될 때 호출됩니다. 게임 오브젝트가 파괴되기 전에도 호출될 수 있습니다.
    OnDestroy() : 게임 오브젝트가 파괴될 때 호출됩니다. 메모리 해제나 정리 작업을 수행할 때 사용됩니다.



머티리얼


머티리얼(Material)은 셰이더(Shader)를 포함하며, 셰이더는 오브젝트가 어떻게 보일지를 결정하는 함수와 같은 역할을 합니다. 과거에는 나무, 금속, 종이 등 다양한 재질을 표현하기 위해 각기 다른 셰이더를 사용해야 했지만, 현재 유니티 및 3D 소프트웨어에서는 PBR(Physically Based Rendering) 시스템이 도입되었습니다.

PBR은 물리 기반 렌더링으로, 빛이 물체 표면에 반사되는 경로를 물리적 공식을 사용해 계산하여 그 결과를 머티리얼로 표현합니다. 이전에도 물리적 계산이 가능했지만, 최근 컴퓨터 성능의 향상으로 복잡한 물리 계산을 추가할 수 있게 되었습니다. PBR을 사용하면 사실적인 표현이 가능해지고, 하나의 셰이더로 다양한 재질을 표현할 수 있습니다. PBR의 주요 특징은 다음과 같습니다.

  • Albedo: 알베도는 물체의 고유한 색상을 결정합니다. PBR 이전에는 난반사(Diffuse)와 정반사(Specular)라는 개념을 사용했습니다. 거친 재질의 물체에는 난반사가 강한 셰이더를, 메탈이나 유리같이 매끄러운 재질에는 정반사가 강한 셰이더를 사용했습니다.
  • Metallic: 메탈릭은 금속과 비금속을 구분하는 파라미터로, 0은 비금속, 1은 금속을 나타냅니다. 금속과 비금속의 가장 큰 차이는 빛을 받을 때의 반사 특성입니다. 금속은 빛을 받으면 반사시킵니다.
  • Smoothness: 스무드니스는 표면의 매끄러움을 나타냅니다. 스무드니스가 0인 경우 표면이 거칠고, 1인 경우 유리나 거울처럼 매끄럽습니다. 매끄러운 표면은 반사 효과를 더 많이 가집니다.



벡터


오브젝트를 이동시키기 위해서는 벡터에 대한 이해가 필요합니다. 크기만을 가지는 스칼라와 달리, 벡터는 크기와 방향을 가집니다. 벡터 간 연산에는 더하기, 빼기, 곱하기가 있으며, 곱하기는 내적과 외적으로 나눌 수 있습니다.

내적(Dot Product)은 두 벡터 사이의 각도와 두 벡터의 크기를 반영하는 스칼라 값을 반환합니다. 이는 두 벡터가 이루는 각도를 계산하거나, 조명과 면의 방향을 계산할 때 사용됩니다. 예를 들어, 조명이 표면에 얼마나 직접적으로 비치는지를 계산할 때 내적을 사용합니다.

외적(Cross Product)은 두 벡터에 수직인 벡터를 반환합니다. 주로 두 벡터가 이루는 평면의 법선 벡터(Normal Vector)를 계산하거나, 회전 축을 계산할 때 사용됩니다. 이는 3D 그래픽스에서 회전이나 충돌 감지에 사용됩니다.

벡터의 정규화(Normalization)는 벡터의 크기를 1로 만들어, 방향만을 나타내는 벡터로 변환하는 과정입니다. 정규화된 벡터는 방향 정보만을 가지므로, 다양한 연산에서 유용하게 사용됩니다.

유니티 SDK의 Vector3 클래스는 기본적인 벡터 연산을 지원하며, 여러 기본 벡터들을 제공하고 있습니다. 예를 들어, Vector3.up은 (0, 1, 0) 벡터를, Vector3.forward는 (0, 0, 1) 벡터를 나타냅니다. 이러한 기본 벡터들은 오브젝트의 이동, 회전, 스케일링 등 다양한 작업에서 유용하게 사용됩니다.



움직임


유니티 엔진에서 게임 오브젝트를 이동시키는 방법에는 여러 가지가 있습니다. 주요 방법으로는 RigidBody를 사용하는 방법, Character Controller를 사용하는 방법, Transform을 사용하는 방법, 네비게이션 AI(Nav Mesh Agent)를 사용하는 방법이 있습니다.

RigidBody를 사용하는 방법은 물리 엔진을 통해 게임 오브젝트를 이동시키는 방법입니다. 이 방법은 물리 법칙을 따르므로 중력, 충돌 등의 물리 효과를 자연스럽게 구현할 수 있습니다. RigidBody를 사용하여 이동시키려면 AddForce, MovePosition, velocity 등의 속성이나 메서드를 사용합니다.

Transform을 사용하는 방법은 게임 오브젝트의 위치, 회전, 크기를 직접 조작하는 방법입니다. 이 방법은 가장 직관적이고 간단하지만, 물리적 상호작용이 필요하지 않은 경우에 적합합니다. Transform을 사용하여 이동시키려면 transform.position이나 transform.Translate 등을 사용합니다.

네비게이션 AI(Nav Mesh Agent)를 사용하는 방법은 AI를 활용하여 게임 오브젝트를 이동시키는 방법입니다. 이 방법은 주로 경로 찾기와 자동 이동을 구현할 때 사용됩니다. Nav Mesh를 생성하고 Nav Mesh Agent를 게임 오브젝트에 추가하여 이동 경로를 설정하고, SetDestination 메서드를 사용하여 목적지로 이동시킵니다.

Character Controller를 사용하는 방법은 캐릭터의 이동과 충돌을 간단하게 처리하는 데 사용됩니다. 이 방법은 주로 플레이어 물리 작용이 잘 쓰이지 않는 캐릭터의 이동에 적합합니다. Character Controller를 사용하여 이동시키려면 Move나 SimpleMove 메서드를 사용합니다. 이 방법은 물리 효과를 사용하지 않으며, 충돌 처리는 Character Controller가 자체적으로 처리합니다. 하지만, 중력 처리는 직접 구현해야합니다.



코루틴


유니티는 기본적으로 단일 스레드를 사용하기 때문에 반복적인 작업이나 특정 조건에서 대기 상태를 만들어야 하는 경우 어려움이 있습니다. 이러한 문제를 해결하기 위해 코루틴(Coroutine)을 사용합니다. 코루틴은 메인 스레드를 차단하지 않고 비동기 작업을 수행할 수 있으며, 이를 통해 복잡한 타이밍 제어나 반복 작업을 보다 쉽게 관리할 수 있습니다.

코루틴은 IEnumerator를 반환하는 함수로 정의되며, StartCoroutine 메서드를 통해 호출됩니다. 코루틴 내에서는 yield return을 사용하여 실행을 일시 중지할 수 있습니다. yield return null은 다음 프레임까지 대기하고, yield return new WaitForSeconds(1)는 지정된 시간(여기서는 1초) 동안 대기합니다. 이러한 방식으로 여러 이벤트를 순차적으로 실행하거나, 특정 조건을 만족할 때까지 반복 실행하는 등의 작업을 처리할 때 유용합니다. 또한, 코루틴을 사용하면 함수 실행 중간에 대기 상태를 만들 수 있어, 애니메이션 동작 효과와 같은 특정 타이밍에 맞춰 함수를 실행하는 데 유리합니다.



유니티 UI 시스템


UI 오브젝트를 생성하면 자동으로 ‘캔버스’와 ‘이벤트 시스템’이라는 두 개의 오브젝트가 생성됩니다. 이 두 오브젝트는 UI 오브젝트 생성 시 반드시 있어야 하고, UI를 렌더링하고 기능을 제공하는 역할을 합니다.

캔버스는 모든 UI 엘리먼트(버튼, 이미지, 텍스트 등)의 상위 오브젝트로, 이 엘리먼트들은 반드시 캔버스의 하위에 있어야 정상적으로 그려집니다. 이를 ‘컨테이너 방식’이라고 합니다. UI 엘리먼트가 하이라키 뷰에 추가되는 순간, 게임 오브젝트가 되며, 각 엘리먼트의 속성은 컴포넌트를 통해 정의됩니다. 예를 들어, 텍스트 엘리먼트는 텍스트 컴포넌트가 있어야 텍스트 속성을 지니며, 텍스트 컴포넌트가 없다면 단순히 이름만 텍스트인 빈 오브젝트가 됩니다.

이벤트 시스템은 캔버스 외부에 하나만 존재합니다. 이벤트 시스템은 키보드 입력, 스크린 터치 등의 입력 정보를 감지하여 UI 엘리먼트들에게 전달합니다. 이를 통해 사용자가 UI를 터치하거나 클릭하는 등의 상호작용을 할 수 있도록 합니다.

추가적으로, UI 엘리먼트가 출력되는 순서는 최하단부터 생성되어 사용자에게 가장 먼저 보입니다.



애니메이션


유니티 엔진에서 애니메이션 시스템은 게임 오브젝트의 동작과 상태 변화를 시각적으로 표현하는 중요한 요소입니다. 유니티의 애니메이션 시스템은 Mecanim이라는 고급 애니메이션 시스템을 통해 다양한 애니메이션 기능을 제공합니다. 이 시스템의 대표적인 기능으로는 리타깃팅과 비주얼 스크립팅이 있습니다.

리타깃팅 기능은 다양한 모델의 본(bone) 정보를 자동으로 매칭하여 동일한 애니메이션을 여러 모델에 적용할 수 있도록 합니다. 리그(Rig) 설정에서 애니메이션 타입을 휴머노이드로 설정하고, 아바타(Avatar)가 존재하면 다른 휴머노이드 모델의 본 정보를 재사용할 수 있습니다. 이를 통해 동일한 애니메이션을 다양한 캐릭터 모델에 쉽게 적용할 수 있습니다.

애니메이션 비주얼 스크립팅 기능은 애니메이션 클립 간의 전환과 블렌딩을 시각적으로 관리할 수 있도록 해줍니다. 애니메이션 전환과 블렌딩 과정을 시각화하여 개발자가 보다 직관적으로 작업할 수 있게 도와줍니다. 이를 통해 복잡한 애니메이션 로직을 쉽게 구성하고, 디버깅할 수 있습니다.

유니티 애니메이션 시스템의 기본 구조는 다음과 같습니다. Animator 컴포넌트는 모든 애니메이션 동작을 제어하는 중심 역할을 합니다. Animator는 애니메이션을 재생하고, 애니메이션 상태를 관리하며, 상태 간 전환을 처리합니다. Animator Controller는 애니메이션 상태와 상태 간 전환 규칙을 정의하는 속성입니다. 각 상태는 특정 애니메이션 클립을 재생하며, 상태 간 전환은 트랜지션을 통해 이루어집니다. Animation Clips는 오브젝트의 동작을 정의하는 기본 단위입니다. 각 클립은 특정한 시간 동안의 위치, 회전, 스케일 변화를 포함합니다.

블렌딩 트리는 여러 애니메이션 클립을 혼합하여 부드러운 애니메이션 전환을 가능하게 하는 메커니즘입니다. 이를 통해 다양한 애니메이션을 자연스럽게 연결할 수 있습니다. 블렌딩 트리는 주로 캐릭터의 이동, 공격, 방어와 같은 복잡한 동작을 구현하는 데 사용됩니다.

애니메이션 이벤트는 애니메이션 클립 내 특정 시점에 메서드를 호출할 수 있게 해주는 기능입니다. 이를 통해 애니메이션 도중에 특정 동작을 수행할 수 있습니다. 예를 들어, 캐릭터의 공격 애니메이션 중 타격 판정을 하거나, 소리를 재생하는 등의 작업을 할 수 있습니다. 애니메이션 이벤트 메서드는 애니메이션 클립이 연결된 스크립트 내에 정의되어야 하며, public이어야 하고, void 반환형을 가져야 합니다.



FBX 파일


보통 모델링 데이터는 3DS 맥스나 마야와 같은 외부 모델링 툴에서 작업되기 때문에 유니티와는 유닛 크기 단위나 피벗 등에서 차이가 발생할 수 있습니다. 아트 팀에서 3D 모델링 툴로 작업한 파일을 외부 파일로 익스포트할 때, 보편적으로 사용되는 파일 형식은 FBX입니다. 따라서, 유니티에서 FBX 파일을 임포트한 후에는 모델링 데이터를 게임 환경에 맞도록 조정하는 작업이 필요합니다.

가장 먼저 확인해야 할 요소는 모델의 크기입니다. 유니티의 Model Importer에서는 ‘Convert Units’ 옵션이 제공됩니다. 이 옵션은 외부 모델링 툴에서 제작된 스케일을 유니티의 기본 스케일(1미터)에 맞춰주는 역할을 합니다. 또한, 척도인자(Scale Factor)를 사용하여 모델링 툴에서 제작된 크기 배율을 설정할 수 있습니다. 이는 모델의 크기를 조정하는 데 도움이 됩니다.

FBX 파일을 임포트한 후에는 모델의 텍스처, 색상, 머티리얼 설정 등도 유니티의 환경에 맞게 조정해야 합니다. 특히, 피벗(pivot) 위치가 모델링 툴과 다를 경우, 유니티에서 피벗 조정을 통해 정확한 위치 설정이 필요할 수 있습니다. 이러한 작업을 통해 외부에서 제작된 모델이 게임 환경에서 올바르게 표현될 수 있도록 보장할 수 있습니다.