본문 바로가기
프로그래밍/C#

[C#]🎮 유니티 애니메이션 마스터하기: 게임 오브젝트에 생명을 불어넣는 여정 ✨

by 다다면체 2025. 4. 18.
728x90
반응형

안녕하세요, 게임 개발자 여러분! 👋 게임 속 캐릭터나 오브젝트가 살아 움직이는 듯한 느낌, 바로 '애니메이션' 덕분이죠. 단순히 움직임을 넘어, 플레이어의 몰입도를 높이고 게임 세계에 활력을 불어넣는 핵심 요소입니다.

이번 시간에는 유니티의 강력한 애니메이션 시스템을 깊이 있게 파헤쳐 보고, 여러분의 게임 오브젝트에 생동감을 불어넣는 방법을 함께 알아보겠습니다. 기초부터 실무 팁까지, 꼼꼼하게 준비했으니 잘 따라와 주세요! 😊


🎬 7.1 애니메이션 클립 생성 및 임포트: 첫걸음 떼기

모든 애니메이션은 '애니메이션 클립'이라는 기본 단위에서 시작합니다. 클립은 특정 동작(걷기, 점프, 공격 등)을 정의하는 키프레임의 시퀀스입니다.

1. 유니티 내장 애니메이션 에디터 활용:

  • 언제 사용할까? 🤔 간단한 움직임(UI 요소의 페이드 인/아웃, 문 열림/닫힘 등)이나 프로토타이핑 단계에서 빠르게 애니메이션을 만들고 싶을 때 유용합니다.
  • 사용법:
    1. 애니메이션을 적용할 게임 오브젝트를 선택합니다.
    2. Window > Animation > Animation (단축키: Ctrl+6 또는 Cmd+6) 메뉴를 통해 애니메이션 창을 엽니다.
    3. 'Create' 버튼을 눌러 새로운 애니메이션 클립(.anim 파일)을 저장합니다.
    4. 타임라인에서 'Add Property' 버튼을 눌러 변경하고 싶은 컴포넌트의 속성(예: Transform의 Position, Rotation, Scale 또는 Sprite Renderer의 Color)을 추가합니다.
    5. 타임라인의 특정 시간 지점(프레임)을 선택하고, 인스펙터 창에서 속성 값을 변경하면 자동으로 '키프레임'이 생성됩니다. 💎
    6. 다른 시간 지점에 또 다른 키프레임을 만들면, 유니티가 그 사이를 부드럽게 보간(interpolation)하여 움직임을 만들어줍니다.
  • 심층 분석: 애니메이션 커브(Curves) 뷰를 활용하면 키프레임 간의 보간 방식을 세밀하게 조정할 수 있습니다. 선형(Linear) 보간 외에도 부드러운 시작/종료(Ease-in/out) 등을 설정하여 더욱 자연스러운 움직임을 표현할 수 있습니다. 📈

2. 외부 3D 모델 및 2D 스프라이트 애니메이션 임포트:

  • 3D 모델: Maya, Blender, 3ds Max 등 외부 툴에서 제작된 3D 모델과 애니메이션 데이터(주로 .fbx 또는 .dae 형식)를 유니티로 가져올 수 있습니다.
    • 임포트 설정: 모델 파일을 프로젝트 뷰로 드래그 앤 드롭하면 임포트 설정(Import Settings)이 나타납니다.
      • Rig 탭: 모델의 뼈대(Bone) 구조를 어떻게 인식할지 설정합니다. 사람 형태 모델은 'Humanoid'로 설정하면 리타겟팅 등 강력한 기능을 활용할 수 있습니다. 동물이나 기계 등은 'Generic'을 사용합니다.
      • Animation 탭: 모델 파일에 포함된 애니메이션 클립들을 확인하고, 루프(Loop Time), 시작/종료 프레임 등을 설정할 수 있습니다. 필요하다면 여기서 클립을 분할할 수도 있습니다. ✂️
    • 실무 팁: 💡 모델링 툴에서 애니메이션 클립 이름을 명확하게 지정(예: Walk, Run, Attack01)해두면 유니티에서 관리하기 훨씬 수월합니다.
  • 2D 스프라이트: 여러 프레임의 이미지를 하나로 합친 '스프라이트 시트(Sprite Sheet)'를 주로 사용합니다.
    • 임포트 설정: 스프라이트 시트 이미지 파일을 임포트한 후, 인스펙터 창에서 Texture Type을 Sprite (2D and UI)로 설정합니다.
    • Sprite Mode를 Multiple로 변경하고 Sprite Editor 버튼을 클릭합니다.
    • Sprite Editor 창에서 'Slice' 기능을 이용하여 각 프레임을 자동으로 또는 수동으로 잘라냅니다. 🔪 (Grid, Automatic, Manual 방식 지원)
    • 잘라낸 스프라이트들을 프로젝트 뷰에서 모두 선택한 후, 씬(Scene) 뷰로 드래그 앤 드롭하면 자동으로 애니메이션 클립과 애니메이터 컨트롤러 생성을 제안합니다.

⚙️ 7.2 애니메이터 (Animator) 컴포넌트: 애니메이션의 지휘자

애니메이터 컴포넌트는 어떤 애니메이션 클립을 언제, 어떻게 재생할지 결정하는 '지휘자' 역할을 합니다. 이는 '애니메이터 컨트롤러(Animator Controller)'라는 에셋을 통해 관리됩니다.

1. 애니메이션 컨트롤러 생성 및 상태 머신 구성:

  • 생성: 프로젝트 뷰에서 Create > Animator Controller 메뉴를 통해 새 컨트롤러를 만듭니다.
  • 애니메이터 창: 컨트롤러 파일을 더블 클릭하면 애니메이터 창(Window > Animation > Animator)이 열립니다. 이곳이 바로 애니메이션의 흐름을 설계하는 '상태 머신(State Machine)'의 편집 공간입니다. 🧠
  • 상태(State): 각 상태는 특정 애니메이션 클립(예: Idle, Walk, Jump)의 재생을 나타냅니다. 빈 공간에 우클릭하여 Create State > Empty로 새 상태를 만들고, 인스펙터 창의 Motion 필드에 원하는 애니메이션 클립(.anim)을 연결합니다.
  • 기본 상태: 주황색의 'Entry' 상태는 게임 시작 시 가장 먼저 실행될 상태(보통 'Idle')로 연결됩니다. Set as Layer Default State 메뉴로 지정할 수 있습니다.

2. 트랜지션(Transition) 설정 및 조건 추가:

  • 트랜지션: 상태 간의 전환을 의미합니다. 예를 들어, 'Idle' 상태에서 'Walk' 상태로 전환되는 화살표를 만드는 것입니다. 상태 위에서 우클릭 > Make Transition을 선택하고 다른 상태를 클릭하여 연결합니다.
  • 조건(Conditions): 트랜지션이 언제 발동될지를 결정하는 규칙입니다. 이것이 없으면 애니메이션이 끝나자마자 바로 다음 상태로 넘어가 버릴 수 있습니다.
    • 애니메이터 창 좌측 상단의 Parameters 탭에서 조건을 위한 변수(파라미터)를 생성합니다. (아래에서 자세히 설명)
    • 트랜지션을 선택하고 인스펙터 창의 Conditions 목록에서 '+' 버튼을 눌러 조건을 추가합니다. (예: Speed 파라미터가 0.1보다 클 때 'Walk' 상태로 전환)
  • 트랜지션 설정 상세:
    • Has Exit Time: 체크하면 현재 애니메이션 클립이 완전히 끝나야 전환됩니다. (예: 공격 애니메이션) 체크 해제하면 조건 만족 즉시 전환됩니다. (예: 걷다가 바로 멈추기)
    • Settings > Transition Duration: 상태 전환 시 얼마나 부드럽게 블렌딩될지 시간(초)을 설정합니다. 값이 크면 부드럽지만 느리고, 작으면 빠르지만 딱딱하게 전환될 수 있습니다.
    • Settings > Interruption Source: 전환 도중 다른 전환 조건이 만족되었을 때 어떻게 반응할지 설정합니다. 복잡한 상호작용에 중요합니다.

3. 파라미터(Parameter)를 이용한 애니메이션 제어:

  • 애니메이션 상태를 외부(주로 스크립트)에서 제어하기 위한 변수입니다. 네 가지 타입이 있습니다.
    • Float: 실수 값 (예: 이동 속도, 방향)
    • Int: 정수 값 (예: 무기 종류, 특정 상태 코드)
    • Bool: 참/거짓 값 (예: 점프 중인가? 땅에 닿았는가?)
    • Trigger: 한번 발동하면 자동으로 초기화되는 신호 (예: 공격, 스킬 사용) - Bool과 달리 SetTrigger() 호출 후 자동으로 false가 됩니다.
  • 활용: 스크립트에서 플레이어의 입력이나 게임 상황에 따라 이 파라미터 값을 변경해주면, 애니메이터 컨트롤러는 설정된 조건에 따라 자동으로 상태를 전환합니다. 이것이 스크립트와 애니메이션 시스템을 연결하는 핵심 고리입니다! 🔗

📜 7.3 스크립트를 이용한 애니메이션 제어: 생동감 불어넣기

애니메이터 컨트롤러와 파라미터를 설정했다면, 이제 스크립트를 통해 이를 제어하여 실시간으로 애니메이션을 변화시킬 차례입니다.

1. Animator 컴포넌트 접근 및 함수 활용:

  • 컴포넌트 가져오기: 스크립트에서 애니메이터 컴포넌트에 접근해야 합니다.
    • 실무 팁: 🚀 Awake()나 Start()에서 미리 컴포넌트를 찾아 변수에 저장해두는 것이 Update() 등에서 매번 GetComponent를 호출하는 것보다 성능상 훨씬 효율적입니다.
  • Animator animator;
    
    void Awake() // 또는 Start()
    {
        // GetComponent<Animator>()는 현재 게임 오브젝트에 붙어있는 Animator 컴포넌트를 찾아줍니다.
        animator = GetComponent<Animator>();
        if (animator == null)
        {
            Debug.LogError("Animator 컴포넌트를 찾을 수 없습니다!");
        }
    }
    
  • 파라미터 제어 함수:
    • animator.SetFloat("파라미터 이름", 값); // 예: animator.SetFloat("Speed", moveSpeed);
    • animator.SetInteger("파라미터 이름", 값); // 예: animator.SetInteger("WeaponType", currentWeaponIndex);
    • animator.SetBool("파라미터 이름", true/false); // 예: animator.SetBool("IsGrounded", isGrounded);
    • animator.SetTrigger("파라미터 이름"); // 예: animator.SetTrigger("Attack");
    • 값을 가져올 때는 GetFloat, GetInteger, GetBool 함수를 사용합니다. (Trigger는 가져오는 함수 없음)
  • 예시: 이동 애니메이션 제어
  • public float moveSpeedThreshold = 0.1f; // 걷기 애니메이션을 시작할 최소 속도
    
    void Update()
    {
        // 수평, 수직 입력 값을 받아옵니다. (Input System 또는 Input Manager 사용)
        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");
    
        // 이동 벡터의 크기를 계산하여 현재 속도를 구합니다.
        Vector3 moveDirection = new Vector3(horizontalInput, 0f, verticalInput);
        float currentSpeed = moveDirection.magnitude;
    
        // Animator의 "Speed" 파라미터 값을 현재 속도로 업데이트합니다.
        if (animator != null)
        {
            animator.SetFloat("Speed", currentSpeed);
    
            // 예시: 점프 입력 처리
            if (Input.GetButtonDown("Jump")) // "Jump"는 Input Manager에 정의된 버튼 이름
            {
                 animator.SetTrigger("Jump");
            }
        }
    }
    

2. Play 함수를 이용한 특정 애니메이션 재생:

  • animator.Play("상태 이름"); // 예: animator.Play("DamageTaken");
  • 이 함수는 상태 머신의 로직(트랜지션 조건 등)을 무시하고 지정된 상태(애니메이션)를 즉시 재생합니다.
  • 주의: ⚠️ 상태 머신 기반 제어가 더 유연하고 관리하기 편하기 때문에, Play 함수는 상태 머신으로 처리하기 애매한 특수한 경우(예: 특정 컷신 재생, 즉각적인 반응이 필요한 상태 등)에 제한적으로 사용하는 것이 좋습니다. 남용하면 상태 머신이 꼬이기 쉽습니다.

3. 애니메이션 이벤트 활용:

  • 애니메이션 클립의 특정 프레임에서 스크립트 함수를 호출하는 강력한 기능입니다. ✨
  • 사용법:
    1. 애니메이션 창에서 이벤트를 추가하고 싶은 클립과 프레임을 선택합니다.
    2. 타임라인 상단의 이벤트 마커 영역에서 우클릭 > Add Animation Event를 선택하거나, 해당 위치에 있는 'Add Event' 버튼을 클릭합니다.
    3. 인스펙터 창에 나타난 이벤트 설정에서 호출할 함수 이름을 Function 필드에 입력합니다. (매개변수도 전달 가능: Float, Int, String, Object)
    4. 중요: 호출될 함수는 Animator 컴포넌트가 붙어있는 동일한 게임 오브젝트에 추가된 스크립트 내에 public으로 선언되어 있어야 합니다.
  • 활용 예시:
    • 걷기/달리기 애니메이션의 발이 땅에 닿는 프레임에 PlayFootstepSound() 함수 호출
    • 공격 애니메이션의 칼 휘두르는 정점 프레임에 ApplyDamage() 함수 호출
    • 총 발사 애니메이션의 특정 프레임에 SpawnMuzzleFlash() 함수 호출
  • 문제 해결: 🛠️ 이벤트가 호출되지 않는다면? 함수 이름 오타 확인, 함수가 public인지 확인, 함수가 있는 스크립트가 Animator와 같은 게임 오브젝트에 붙어있는지 확인하세요.

✨ 7.4 2D 애니메이션: 픽셀에 생명을!

2D 게임에서의 애니메이션은 주로 스프라이트 시트를 기반으로 하지만, 더 발전된 기법들도 존재합니다.

1. Sprite Sheet를 이용한 2D 애니메이션 생성:

  • 기본 원리: 7.1에서 설명했듯이, 스프라이트 시트를 슬라이스하고, 슬라이스된 스프라이트들을 순서대로 나열하여 애니메이션 클립을 만드는 방식입니다.
  • 생성: 슬라이스된 스프라이트들을 프로젝트 뷰에서 선택 -> 씬 뷰로 드래그 앤 드롭 -> Unity가 .anim 파일과 Animator Controller 생성을 도와줍니다.
  • 장점: 직관적이고, 전통적인 방식이며, 픽셀 아트 스타일에 잘 어울립니다.
  • 단점: 프레임 수가 많아지면 용량이 커지고, 부드러운 움직임을 표현하려면 많은 프레임 이미지가 필요합니다.

2. 2D Animation 패키지 활용 (Skinning Editor 등):

  • 설치: Window > Package Manager에서 2D Animation 패키지를 찾아 설치합니다.
  • 핵심 기능: 스키닝(Skinning) & 본(Bone):
    • 3D 애니메이션처럼 2D 스프라이트에도 '뼈대(Bone)'를 심고, 각 뼈대에 스프라이트 메시(Mesh)의 정점(Vertex)들이 얼마나 영향을 받을지 '가중치(Weight)'를 칠하는 방식입니다. (이 과정을 '스키닝'이라고 합니다.)
    • Sprite Editor 창에 새로 추가된 Skinning Editor를 사용합니다. 여기서 뼈대를 만들고(Create Bone), 스프라이트 메시를 자동으로 생성하고(Auto Geometry), 뼈대와 메시를 연결(Auto Weights)할 수 있습니다. 세밀한 조정도 가능합니다. 💪
    • 뼈대를 움직이면 해당 뼈대에 연결된 스프라이트 메시가 자연스럽게 변형됩니다.
  • 장점:
    • 적은 수의 스프라이트 이미지로도 매우 부드럽고 유연한 애니메이션 제작 가능 (메모리 효율성 👍)
    • 캐릭터 파츠(머리, 몸통, 팔, 다리 등)를 분리하여 제작하면, 옷이나 무기 등을 쉽게 교체 가능 (커스터마이징 용이)
    • IK (Inverse Kinematics)를 적용하여 발을 땅에 고정시키는 등 고급 제어 가능.
  • 단점: 초기 설정 과정이 스프라이트 시트 방식보다 복잡하고, 픽셀 아트보다는 벡터나 고해상도 아트 스타일에 더 적합할 수 있습니다.

💡 실무 경험 기반의 팁 & 문제 해결 가이드

  • 애니메이터 컨트롤러 정리: 상태가 많아지면 복잡해집니다. 관련된 상태들을 묶어 'Sub-State Machine'으로 관리하고, 특정 애니메이션(예: 상체 공격, 하체 이동)을 분리하기 위해 'Layers' 기능을 활용하세요. 🗂️
  • 루트 모션(Root Motion): 애니메이션 데이터 자체에 이동/회전 정보가 포함되어 캐릭터를 움직이는 방식입니다. 물리 기반 이동과 잘 통합하면 자연스러운 움직임을 만들 수 있지만, 제어가 까다로울 수 있습니다. Animator 컴포넌트의 Apply Root Motion 체크박스로 활성화/비활성화합니다.
  • 애니메이션 최적화: 화면에 보이지 않는 오브젝트의 애니메이션 계산을 멈추는 'Culling Mode' 설정을 활용하세요 (Always Animate, Cull Update Transforms, Cull Completely). 복잡한 상태 머신보다는 단순하게 유지하는 것이 성능에 유리합니다. 💨
  • 협업 시: 애니메이터 컨트롤러, 애니메이션 클립, 파라미터 이름 등에 일관된 규칙(Naming Convention)을 정하는 것이 매우 중요합니다.
  • 흔한 문제 & 해결:
    • 애니메이션이 안 나와요! 😭 -> Animator 컴포넌트가 비활성화되진 않았는지? Controller가 제대로 할당되었는지? Entry 상태가 연결되었는지? 파라미터 조건이 스크립트에서 올바르게 설정되고 있는지? 확인해보세요.
    • 전환이 너무 뚝 끊겨요! -> Transition의 Transition Duration 값을 늘려보세요. Has Exit Time 설정을 확인하세요.
    • 이벤트가 실행 안 돼요! -> 함수 이름이 정확한지? 함수가 public인지? 함수가 있는 스크립트가 Animator와 같은 게임 오브젝트에 있는지? 재확인하세요.

맺음말

애니메이션은 단순히 움직임을 만드는 것을 넘어, 게임 세계에 영혼을 불어넣는 예술입니다. 🎨 유니티의 애니메이션 시스템은 처음에는 다소 복잡해 보일 수 있지만, 각 요소(클립, 애니메이터, 파라미터, 스크립트 제어)의 역할과 상호작용을 이해하면 상상하는 거의 모든 움직임을 구현할 수 있습니다.

오늘 배운 내용을 바탕으로 직접 이것저것 만져보고 실험해보는 것이 중요합니다. 작은 것부터 시작해서 점차 복잡한 애니메이션에 도전해보세요! 여러분의 게임이 더욱 생동감 넘치게 될 그날까지, 응원하겠습니다! 파이팅! 🔥

728x90
반응형