공부/유니티

유니티 핵심 개념 정리 - URP, 컴포넌트, 입력 시스템, 물리

Excidus 2023. 2. 20. 13:26

유니티(Unity)를 공부하며 렌더링 파이프라인, 컴포넌트 기반 구조, 입력 처리, 물리 시스템 등 핵심 개념을 정리했다. 이번 학습은 유니티의 다양한 기능을 실제 프로젝트에 적용하며 이해하는 데 초점을 맞췄다. 각 개념을 테스트하고 디버깅하며 얻은 경험을 기록했다.

1. 유니버설 렌더 파이프라인 (URP)

  • 정의: URP는 유니티의 스크립터블 렌더 파이프라인으로, 모바일부터 고사양 콘솔, PC까지 다양한 플랫폼에서 최적화된 그래픽스를 구현한다. 아티스트 친화적인 워크플로를 제공한다.
  • 구현 경험: 2D 게임 프로젝트에서 URP를 설정해 셰이더와 라이팅을 최적화했다. 초기에는 URP 설정이 낯설었으나, 공식 문서와 샘플 프로젝트를 참고해 렌더링 파이프라인을 구성했다.
  • 배운 점: URP는 성능과 시각적 품질의 균형을 맞추는 데 효과적이다. 특히 모바일 환경에서의 최적화가 뛰어나다.

2. 컴포넌트 기반 구조

유니티는 컴포넌트 기반 엔진으로, 게임 오브젝트는 다양한 컴포넌트로 구성된다. 주요 컴포넌트를 정리했다.

  • 트랜스폼 (Transform): 위치, 회전, 크기 속성을 관리한다. 모든 게임 오브젝트의 필수 컴포넌트다.
    • 구현 경험: 오브젝트 이동 로직에서 Transform.position을 조작하며 직관적인 위치 제어를 테스트했다.
    • 배운 점: Transform은 단순하지만 모든 변형의 핵심이다.
  • 스프라이트 렌더러 (SpriteRenderer): 2D 스프라이트를 화면에 렌더링한다.
    • 구현 경험: 캐릭터 스프라이트를 설정하며 SpriteRenderer의 정렬 순서를 조정해 겹침 문제를 해결했다.
    • 배운 점: 스프라이트의 시각적 품질은 렌더링 순서와 셰이더 설정에 크게 의존한다.
  • 리지드바디 (Rigidbody): 물리 영향을 받는 컴포넌트로, 힘과 충돌을 처리한다.
    • 구현 경험: 2D 게임에서 Rigidbody2D를 사용해 캐릭터 이동을 구현했다. 초기에는 물리 계산이 부정확했으나, FixedUpdate로 전환해 안정성을 높였다.
    • 배운 점: Rigidbody는 물리 기반 상호작용의 핵심이다.
  • 콜라이더 (Collider): 물리적 충돌 면을 정의한다.
    • 구현 경험: 캐릭터와 벽의 충돌을 처리하며 BoxCollider2D 크기를 조정해 정확한 충돌을 구현했다.
    • 배운 점: Collider의 정밀한 설정은 충돌 감지의 품질을 결정한다.
  • MonoBehaviour: 게임 로직을 구성하는 기본 클래스. 생명주기 함수와 유니티 API를 제공한다.
    • 구현 경험: MonoBehaviour를 상속받아 캐릭터 이동 스크립트를 작성하며 생명주기 함수의 활용법을 익혔다.
    • 배운 점: MonoBehaviour는 유니티 스크립트의 핵심이며, 생명주기 관리에 필수적이다.

3. 스프라이트와 아틀라스

  • 아틀라스: 여러 스프라이트를 하나의 텍스처로 묶은 형태로, 렌더링 성능을 최적화한다.
    • 구현 경험: UI 스프라이트를 아틀라스로 묶어 배치 호출(Draw Call)을 줄였다. 초기에는 아틀라스 설정이 복잡했으나, Sprite Atlas 도구를 활용해 간소화했다.
    • 배운 점: 아틀라스는 특히 모바일 게임에서 성능 최적화에 필수적이다.
  • 스프라이트: 2D 이미지를 게임에 표시한다. SpriteRenderer와 함께 사용된다.
    • 구현 경험: 스프라이트 애니메이션을 구현하며 프레임별 전환을 테스트했다.
    • 배운 점: 스프라이트의 효율적인 관리는 게임의 시각적 품질과 성능에 영향을 미친다.

4. 생명주기 함수

유니티의 생명주기 함수는 스크립트 실행의 핵심이다.

  • Awake(): 오브젝트 생성 시 한 번 호출되며, 초기화 작업에 사용된다.
    • 구현 경험: Awake()에서 Rigidbody2D를 초기화하며 GetComponent<Rigidbody2D>()를 사용했다. 초기에는 컴포넌트 참조가 누락되어 오류가 발생했으나, 디버깅으로 해결했다.
    • 배운 점: Awake()는 참조 설정에 최적이다.
  • Update(): 매 프레임 호출되며, 입력 처리나 로직 업데이트에 사용된다.
    • 구현 경험: 플레이어 입력을 Update()에서 처리하며 부드러운 움직임을 구현했다.
    • 배운 점: Update()는 빈번한 호출로 성능에 영향을 미칠 수 있으므로 최적화가 필요하다.
  • FixedUpdate(): 물리 프레임마다 호출되며, 물리 연산에 적합하다.
    • 구현 경험: Rigidbody2D 이동을 FixedUpdate()로 처리해 프레임 독립적인 움직임을 구현했다.
    • 배운 점: FixedUpdate()는 물리 계산의 안정성을 보장한다.

5. 입력 시스템

유니티의 입력 처리는 게임 상호작용의 핵심이다.

  • Input 클래스: 모든 입력(키보드, 마우스 등)을 관리한다.
    • 구현 경험: Input.GetAxisRaw("Horizontal")를 사용해 캐릭터 이동을 구현했다. GetAxis보다 명확한 입력을 위해 GetAxisRaw를 선택했다.
    • 배운 점: GetAxisRaw는 즉각적인 컨트롤이 필요한 경우 유리하다.
  • Input Manager: 물리적 입력을 지정된 버튼에 매핑한다. (구형 방식)
    • 구현 경험: Input Manager로 키 매핑을 설정했으나, 복잡한 입력 처리에는 한계가 있었다.
    • 배운 점: 최신 Input System이 더 유연하다.
  • Input System: 최신 입력 처리 방식으로, 다양한 디바이스를 지원한다.
    • 구현 경험: Input System으로 전환하며 입력 프로필을 설정했다. 초기 설정이 복잡했으나, 공식 튜토리얼로 해결했다.
    • 배운 점: Input System은 멀티플랫폼 게임에 적합하다.

6. 물리 이동 방식

Rigidbody2D를 사용한 이동 방식은 세 가지로 구현 가능하다.

  1. 힘 적용 (AddForce): rigid.AddForce(inputVec)로 힘을 가한다.
    • 구현 경험: 캐릭터 가속을 구현하며 부드러운 움직임을 테스트했다.
    • 배운 점: 물리적 느낌을 강조할 때 유용하다.
  2. 속도 제어 (velocity): rigid.velocity = inputVec로 속도를 직접 설정한다.
    • 구현 경험: 즉각적인 속도 변경이 필요할 때 사용했다.
    • 배운 점: 단순하지만 정밀한 제어가 필요하다.
  3. 위치 이동 (MovePosition): rigid.MovePosition(rigid.position + inputVec)로 위치를 이동한다.
    • 구현 경험: 정확한 위치 이동을 위해 사용했으나, 입력 벡터에 fixedDeltaTime을 곱해 부드럽게 조정했다.
    • 배운 점: MovePosition은 물리 기반 이동에 적합하다.
  • normalized: 벡터 크기를 1로 조정해 방향만 유지한다.
    • 구현 경험: 대각선 이동 속도를 균일하게 하기 위해 inputVec.normalized를 사용했다.
    • 배운 점: 입력 정규화는 일관된 움직임을 보장한다.
  • fixedDeltaTime: 물리 프레임의 시간 간격. FixedUpdate()에서 사용된다.
    • 구현 경험: 이동 속도에 fixedDeltaTime을 곱해 프레임 독립적인 이동을 구현했다.
    • 배운 점: fixedDeltaTimeFixedUpdate()의 핵심이다.

7. 기타 개념

  • Static: 정적 변수/메서드를 선언하며, 인스턴스 없이 즉시 호출 가능하다. Inspector에 표시되지 않는다.
    • 구현 경험: 싱글톤 패턴에서 static 변수를 사용해 전역 접근을 구현했다.
    • 배운 점: static은 편리하지만 메모리 관리에 주의해야 한다.
  • Rule Tile: 인접 타일에 따라 이미지를 자동 설정하는 타일.
    • 구현 경험: 2D 맵 제작에서 Rule Tile로 타일맵을 효율적으로 구성했다.
    • 배운 점: Rule Tile은 맵 디자인 시간을 단축한다.
  • Reference Resolution: UI와 카메라 크기 계산의 기준 해상도.
    • 구현 경험: UI 스케일링을 위해 Reference Resolution을 조정했다.
    • 배운 점: 다양한 화면 비율에 대응하려면 필수적이다.

8. 개발 중 주요 이슈와 해결

  • 컴포넌트 참조 오류: GetComponent<Rigidbody2D>()에서 참조가 누락되어 오류가 발생했다. Awake()에서 초기화를 보장해 해결했다.
  • 입력 처리 불균일: Input.GetAxis로 입력이 부드럽게 처리되었으나, 즉각적인 반응을 위해 GetAxisRaw로 전환했다.
  • 물리 이동 끊김: Update()에서 물리 이동을 처리했을 때 프레임 드롭이 발생했다. FixedUpdate()fixedDeltaTime으로 전환해 안정성을 높였다.

9. 마무리

이번 학습을 통해 유니티의 컴포넌트 기반 구조, 입력 시스템, 물리 이동 방식의 세부 동작을 깊이 이해할 수 있었다. 특히 URP와 Input System의 활용이 플랫폼 간 호환성과 성능 최적화에 큰 영향을 미친다는 점을 체감했다