본문 바로가기
게임 프로그래밍/유니티 활용

[유니티 활용] BlendTree 를 이용한 2D 점프 애니메이션

by 레오란다 2022. 12. 23.
반응형

이번 포스팅에서는 Animator 의 BlendTree 기능을 이용한 2D 캐릭터의 점프 애니메이션 구현 방법에 대해 알아보도록 하겠습니다. 본 포스팅은 아래의 포스팅에 이어서 작성하는 글입니다. 

 

[유니티 활용] Platform Effector 2D 를 이용한 관통 플랫폼 만들기

이번 글은 이전에 작성한 아래의 포스팅에서 이어집니다. 처음부터 차근차근 해보시려면 아래의 링크부터 시작하시는 게 좋습니다. 단방향 충돌처리에 대한 정보만 얻으시려면 이 글로 충분 하

ugames.tistory.com

 

▶ 준비사항

에셋: SunnyLand 무료 에셋 (여기를 클릭해서 받으시면 됩니다.)

 

 

▶ 애니메이션

애니메이션과 관련된 내용은 간단하게 다른 포스팅에서 다룬 적이 있습니다. 이전 포스팅에서는 Animation 을 이용해 점프 구현을 위해 anim 파일 생성, 타임라인별 sprite 설정 및 position 값 변경을 다루었습니다. 여기서는 Asset Store 에서 다운로드 받은 에셋에 이미 포함되어 있는 animation 파일을 활용하는 법과 필요한 animation 을 만들어서 추가해 사용하는 법 모두 다루도록 하겠습니다.

 

▷ Player 객체에 Animation Controller 생성

  • Player 객체를 선택하고 Inspector 창에서 [Add Component > Miscellaneous] 에 있는 Animator 를 추가합니다.
  • Project 창에 있는 Assets 폴더에 AnimController 폴더를 생성합니다.
  • AnimController 폴더 안에서 마우스 우클릭 [Create > Animator Controller] 를 선택하고 이름을 Player 로 변경합니다.
  • 생성한 Player animator controller 를 Player 객체에 있는 Animator 의 Controller 항목에 드래그 해줍니다.

Animator Controller 연결
Animator Controller 연결

  • 위 그림에서 밑에 있는 animator controller 인 Player 를 더블 클릭하면 아래와 같은 Animator 창이 나타납니다.

Animator 창
빈 Animator 창

 

▷ Idle 애니메이션 만들기

Idle 애니메이션은 SunnyLand 에셋에 이미 포함되어 있기 때문에 여기서는 연결해서 사용하는 방법에 대해 알아보도록 하겠습니다.

  • 우선 Project 창에서 경로 [Assets > SunnyLand Artwork > Sprites > player > idle] 로 이동합니다.
  • Animator 창에서 마우스 우클릭 [Create State > Empty] 를 선택해 New State 를 생성하고 선택합니다.
  • 아래의 그림과 같이 Project 창에 있는 idle-Animation 을 Inspector 창에 있는 Motion 에 연결하고 이름을 Idle 로 변경합니다.

Animation 연결

여기까지 진행하시고 Play 버튼을 누르면 캐릭터와 꼬리가 살짝살짝 움직이는 애니메이션을 볼 수 있습니다.

 

 

▷ Run 애니메이션 만들기

Run animation 은 위에서 진행한 idle 과 똑같기 때문에 간략하게 설명하겠습니다.

  • Project 창에서 경로 [Assets > SunnyLand Artwork > Sprites > player > run] 으로 이동합니다.
  • Animator 창에서 마우스 우클릭 [Create State > Empty] 를 선택해 New State 를 생성하고 선택합니다.
  • Project 창에 있는 run-Animation 을 Inspector 창에 있는 Motion 에 연결하고 이름을 Run 으로 변경합니다.

 

여기까지 진행하면 Animator 창에 다음과 같이 두 개의 State 가 생성되어 있을 것입니다.

Animator 창

캐릭터의 이동을 Rigidbody2D 를 이용해 하고 있기 때문에 캐릭터를 이동하면 rigidbody2d 의 속성인 velocity 의 x 값이 0 이 아닌 값이 되고 가만히 있으면 x 값이 0 이 됩니다. 이 특성을 이용해 가만히 있으면 Idle 애니메이션이 플레이되고 움직이면 Run 애니메이션이 플레이 되도록 만들어 보겠습니다.

 

 

▷ Run <-> Idle 애니메이션 사이의 Transition 만들기

  • 우선 위 이미지와 같이 Parameters 탭을 클릭 하고 아래 테두리 + 부분을 클릭하면 나오는 팝업창에서 Float 을 선택합니다. 생성된 파라미터의 이름을 New Float 에서 MoveSpeed 로 변경합니다.
  • Idle 을 선택하고 마우스 우클릭 [Make Transition] 을 선택한 다음 Run 을 선택합니다.
  • Run 을 선택하고 마우스 우클릭 [Make Transition] 을 선택한 다음 Idle 을 선택합니다.
  • 우선 Idle -> Run Transition 을 다음과 같이 설정해 줍니다.

Idle -&gt; Run Transition 설정

  • 위 그림의 왼쪽 테두리에 있는 Idle -> Run 방향의 화살표를 클릭합니다.
  • Inspector 창에 나타나는 속성을 위의 그림과 같이 설정해 줍니다. 
    • Has Exit Time 체크 해제
    • Transition Duration 0 으로 변경
    • Conditions 에서 + 클릭 후 위의 그림과 같이 설정
  • 위 설정의 의미는 MoveSpeed 의 값이 0 보다 크면 Run 애니메이션으로 transition 한다는 의미입니다.
  • Run -> Idle Transition 을 다음과 같이 설정합니다.

  • 차이점은 MoveSpeed 의 조건 값이 Less 0.5 부분입니다. 이 의미는 MoveSpeed 의 값이 0.5 미만이면 Idle 애니메이션으로 transition 하라는 의미입니다.

 

▷ BlendTree 를 이용한 Jump 애니메이션 만들기

  • 우선 새 파라미터 2개를 생성합니다.
    • Bool 타입의 IsGround
    • Float 타입의 JumpBlend
  • 이제 Player 객체를 선택하고 Animation 창에서 아래의 그림과 같이 붉은 테두리 부분을 클릭하고 [Create New Clip...] 을 선택합니다.

Create New Clip

  • 이름을 JumpUp.anim 으로 변경하고 저장합니다.
  • Project 창에서 [Assets > SunnyLand Artwork > Sprites > player > jump] 에 있는 "player-jump-1" 를 Animation 창의 타임라인 0 에 드래그 합니다.

JumpUp anim

 

  • 다시 위와 같은 방법으로 [Create New Clip...] 을 선택하고 이름을 JumpDown.anim 으로 저장합니다.
  • Project 창에서 [Assets > SunnyLand Artwork > Sprites > player > jump] 에 있는 "player-fall" 를 Animation 창의 타임라인 0 에 드래그 합니다.

JumpDown anim

  • Jump 를 선택하고 마우스 우클릭 [Create new BlendTree in State] 를 선택하고 Jump state 를 더블 클릭합니다.
  • Blend Tree 를 선택하고 마우스 우클릭 [Add Motion] 을 2회 합니다.
  • 다시 Animator 창으로 돌아와 마우스 우클릭 [Create State > Empty] 로 New State 를 만들고 이름을 Inspector 창에서 Jump 로 변경합니다.
  • Jump 를 선택하고 마우스 우클릭 [Create new BlendTree in State] 를 선택하고 더블클릭합니다.
  • Blend Tree 를 선택하고 마우스 우클릭 [Add Motion] 을 2회 합니다.

BlendTree 설정

  • 먼저 Inspector 창에서 Parameter 를 위의 그림과 같이 JumpBlend 로 변경합니다.
  • 그림과 같이 animation 파일을 Motion 에 각각 드래그하여 설정해 줍니다.
  • Automate Thresholds 를 체크 해제하고 Motion 에 있는 Threshold 를 -1, 1 로 설정합니다.

 

▷ Idle ↔ Jump 와 Run → Jump Transition 만들기

  • 우선 Idle Jump 의 설정을 다음과 같이 해줍니다.

Idle &gt; Jump Transition 설정

  • Jump  Idle transition 의 설정을 다음과 같이 해줍니다.

Jump &gt; Idle Transition 설정

  • Run Jump transition 의 설정을 다음과 같이 해줍니다.

Run &gt; Jump Transition 설정

 

▶ Player 스크립트 수정

Player 스크립트를 다음과 같이 수정합니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
   Rigidbody2D rig;
   Animator anim;
   BoxCollider2D collider;

   public float moveSpeed;
   public float maxSpeed;
   public float jumpPower;


   // Start is called before the first frame update
   void Start()
   {
      rig = GetComponent<Rigidbody2D>();
      anim = GetComponent<Animator>();
      collider = GetComponent<BoxCollider2D>();
   }

   // Update is called once per frame
   void Update()
   {
      // 왼쪽 화살표를 누르고 있는 경우
      if (Input.GetKey(KeyCode.LeftArrow))
      {         
         // 물체에 왼쪽 방향으로 물리적 힘을 가해줍니다. 즉, 왼쪽으로 이동 시킵니다.
         rig.AddForce(Vector2.left * moveSpeed, ForceMode2D.Impulse);

         // velocity 는 물체의 속도입니다. 일정 속도에 도달하면 더 이상 빨라지지 않게합니다.
         rig.velocity = new Vector2(Mathf.Max(rig.velocity.x, -maxSpeed), rig.velocity.y);

         // scale 값을 이용해 캐릭터가 이동 방향을 바라보게 합니다.
         transform.localScale = new Vector3(-1f, 1f, 1f);
      }
      else if (Input.GetKey(KeyCode.RightArrow)) // 오른쪽 화살표를 누르고 있는 경우
      {         
         rig.AddForce(Vector2.right * moveSpeed, ForceMode2D.Impulse);
         rig.velocity = new Vector2(Mathf.Min(rig.velocity.x, maxSpeed), rig.velocity.y);
         transform.localScale = new Vector3(1f, 1f, 1f);
      }
      else if (Input.GetKeyUp(KeyCode.LeftArrow) || Input.GetKeyUp(KeyCode.RightArrow)) // 이동 키를 뗀 경우
      {
         // x 속도를 줄여 이동 방향으로 아주 살짝만 움직이고 거의 바로 멈추게 합니다.
         rig.velocity = new Vector3(rig.velocity.normalized.x, rig.velocity.y);
      }

      // 스페이스바를 누르면 점프합니다.
      if (Input.GetKeyDown(KeyCode.Space))
      {
         if (IsGrounded())
         {
            rig.AddForce(Vector2.up * jumpPower, ForceMode2D.Impulse);            
         }
      }

      var vel_norm = rig.velocity.normalized;      
      anim.SetBool("IsGround", vel_norm.y == 0);
      // vel_norm.x 의 값은 왼쪽으로 가면 -1 로 오른쪽으로 가면 1 로 변화합니다.
      // Animator 설정에서 Idle 상태에서 MoveSpeed 값이 0 보다 크면 Run
      // Run 상태에서 MoveSpeed 값이 0.5 보다 작으면 Idle 로 Transtion 됩니다.
      // 따라서 방향성을 없애기 위해 절대값을 취하는 것입니다.
      anim.SetFloat("MoveSpeed", Mathf.Abs(vel_norm.x));
      if (vel_norm.y != 0) anim.SetFloat("JumpBlend", rig.velocity.normalized.y);
   }

   // Ground 체크
   bool IsGrounded()
   {
      // Collider 의 가로크기를 이용해 캐릭터의 발쪽에 Box 모양으로 충돌 체크를 합니다.
      var ray = Physics2D.BoxCast(
         collider.bounds.center, 
         new Vector2(collider.bounds.size.x, 0.2f), 
         0f, 
         Vector2.down, 
         0.35f, 
         1 << LayerMask.NameToLayer("Ground"));
      Debug.Log(ray);
      return ray.collider != null;
   }
}

 

완성되었습니다. Play 하고 캐릭터를 움직이고 점프를 해보면 지금까지 설정한대로 애니메이션이 상태에 따라 나타나는 것을 볼 수 있습니다.

 

완성
완성후 플레이

반응형

댓글