Replicated

[Unity6 Tutorial] #2 애니메이션 & 액션 - 달리기, 점프, Grounded 체크 본문

유니티 엔진/Unity6 Tutorial

[Unity6 Tutorial] #2 애니메이션 & 액션 - 달리기, 점프, Grounded 체크

라구넹 2025. 4. 25. 13:16

달리기 애니메이션

일단 달리기부터 만들어보자

액션은 저번 시간에 만들었다! a랑 d를 누르면 움직인다

 

그런데 이제 애니메이션을 만들어보자!

일단 테스트용으로 깔아놨던 Terrain의 x 스케일을 30정도로 해서 움직여도 될만한 바닥을 만들자

 

그리고 Player에 Add Component해서 Animator 추가

Animator는 자신보다 하위 계층에 있는 게임 오브젝트가 컨트롤 가능하니, 최대한 상위인 Player 오브젝트에다 컴포넌트를 붙여주자

 

그리고 윈도우 → 애니메이터, 애니메이션 둘 다 열어주자

 

Assets에 애니메이션 폴더를 만들어주고

 

폴더 들어가서 우클릭 → 크리에이트 → 애니메이션 → 애니메이터 컨트롤러

이름 : PlayerAnimController

 

만든 컨트롤러를 Player에다 붙였던 Animator의 Controller에 마우스로 잡아 끌고 가서 붙인다

혹은 옆에 동그라미 누르면 컨트롤러 검색 가능

 

이제 Animation으로 가면 애니메이션 만들기 가능

Create 선택

 

이제 프레임 별로 애니메이션을 설정 가능한 창이 나온다

 

이 경로 따라가면

 

이렇게 있는데 이걸 쓸 거다

 

Add Property 누르면 Animator보다 하위에 있는 오브젝트들을 건드릴 수 있는데

그 중에서 PlayerSprite 선택

그럼 PlayerSprite 밑에 있는 컴포넌트인 SpriteRender에 접근 가능

반드시 PlayerSprite에서 작업해야 함

(Player에서 작업 시 스프라이트 여러 개 됨)

맨 밑에 Sprite 선택

 

애셋 폴더에서 스프라이트 잡아다 끌면 붙는다

 

순서대로 쭉 이어 붙인다

 

맨 뒤에 있던 건 삭제 (맨 끝이 애니메이션의 끝이 되는데 필요 없어서 삭제)

 

씬 우클릭해서 하나 추가하고

 

같이 볼 수 있게 적당한 곳에 창을 위치시키고 애니메이션 플레이 버튼 누르면 애니메이션 어떻게 실행되는지 알 수 있음

이대로 실행해보면??

아마 가만히 있는데도 뛰고 있을 거다

왜냐하면 가만히 있는 애니메이션을 안만들었기 때문

 

Idle 애니메이션

하나 새로 만들어보자

 

Idle도 다 있다

똑같이 해주자

 

그리고 애니메이터 들어가서

 

Idle 애니메이션 우클릭해서 디폴트 선택

 

근데 실행해보면 너무 빠르다

 

Idle 애니메이션에서 마우스 잡아 끌어서 전체 선택하고

 

30프레임 정도까지 잡아 끌자

이제 좀 자연스러울 거다

 

 

코드랑 애니메이션 연결하기

일단 애니메이터는 이 상태일 거다

 

우클릭하면 MakeTransition이 생길 텐데 해주자

 

양쪽으로

 

옆에 Layer랑 Parameter도 있는데 파라미터 가서

 

Bool 고르고

 

IsRun 파라미터 만들기

 

트랜지션 누르면 자세한 설정 가능한데

Condition이 일치 시 트랜잭션이 넘어감

 

각각 설정해주자

이제 문제는 IsRun을 어디서 설정해 주느냐 이다

이제 코드를 짜야 한다

저번 시간에 만들었던 PlayerController에 들어가자

 

public class PlayerController : MonoBehaviour
{
    private Rigidbody2D rb;
    private PlayerControlInput inputActions;
    private Animator animator;

    private Vector2 inputVector;

    private float moveSpeed = 5.0f;

    void Awake()
    {
        rb = GetComponentInChildren<Rigidbody2D>();
        animator = GetComponent<Animator>();
        inputActions = new PlayerControlInput();

        inputActions.Player.Move.performed += ctx => inputVector = ctx.ReadValue<Vector2>();
        inputActions.Player.Move.canceled += ctx => inputVector = Vector2.zero;
    
    }
    private void Update()
    {
        // sqrMagnitude은 벡터의 길이 제곱값. 연산이 magnitude보다 가벼움
        animator.SetBool("IsRun", inputVector.sqrMagnitude > 0.01f);
    }

애니메이터를 Awake에서 가져오고, Update에서 애니메이션 설정을 해준다

이대로 실행하면??

 

아마 애니메이션 전환이 제대로 안될 거다

 

이유는..

 

트랜지션에서 Settings를 열어보면 Interruption Source라는게 있다

저게 None이면 현재 애니메이션 실행 중에는 다른 애니메이션으로 넘어가지지 않는다

 

Current State로 바꿔주자

 

그리고 HasExitTime도 꺼줘야 한다

이것도 체크되어 있으면 저게 끝나기 전까지 다른 걸로 못넘어간다

 

이제 좌우 방향 전환만 되면 된다

 

void Awake()
{
    rb = GetComponentInChildren<Rigidbody2D>();
    animator = GetComponent<Animator>();
    inputActions = new PlayerControlInput();


    inputActions.Player.Move.performed += ctx =>
    {
        inputVector = ctx.ReadValue<Vector2>();

        if (inputVector.x > 0)
        {
            // transform.localScale.x = Mathf.Abs(transform.localScale.x); 이건 안됨
            transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
        }
        else
        {
            transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x) * -1.0f, transform.localScale.y, transform.localScale.z);
        }
    };
    inputActions.Player.Move.canceled += ctx => inputVector = Vector2.zero;

}

람다로 키 입력 발생했던 거에 같이 방향 전환을 넣어주면 된다

 

transform(소문자 시작)은 해당 컴포넌트가 붙어있는 게임 오브젝트의 Transform(위치, 회전, 스케일..)을 가져오는데, 거기서 localScale을 -1을 곱하면 방향이 반전됨

 

⇒ 이동 인풋 발생 시 스케일을 이벤트 발생으로 조정하기!

 

이제 방향 전환까지 된다

 

 

점프 액션 + Grounded 체크, 애니메이션

일단 액션부터 만들자

저번에 만들었던 인풋 열어서

 

Add Binding → 스페이스에 연결

Add Binding하면 그냥 함수랑 연결 가능

Save하고 닫으면 알아서 반영됨

 

private float jumpPower = 150.0f;

void Awake()
{
    ...
    inputActions.Player.Move.canceled += ctx => inputVector = Vector2.zero;

    inputActions.Player.Jump.performed += ctx => rb.AddForceY(jumpPower);

}

이렇게 해주면 이제 스페이스바를 누르면 점프가 된다!

그런데, 스페이스바를 여러번 눌러도 계속 점프가 된다

이러면 이제 맞닿은 땅이 있는지 체크해야 한다

 

1. 어디서 체크해야 하는가

groundCheck 선언하고

** [SerializeField] 선언 시 praivate 이어도 에디터에 보임

 

안에 빈 오브젝트 만들고

 

위치 적당히 설정한 다음

 

** 좌측 상단 디버깅용 아이콘 설정 가능

 

PlayerController에다 설정하기

저 위치를 기준으로 Grounded 체크를 할 것이다

 

 

2. 무엇을 체크해야 하는가

레이어 마스크라는 걸 추가

저걸로 부딫힐 오브젝트를 선택할 수 있음

 

아무대서나 Layer누르고 아래 AddLayer 선택

 

레이어에 그라운드 추가

 

만들었던 바닥의 Layer를 Ground로 설정

 

PlayerController에서 GroundLayer로 Ground 레이어 마스크 설정

 

적용

체크 범위도 만들어준 다음에

 

private void Update()
{
    // sqrMagnitude은 벡터의 길이 제곱값. 연산이 magnitude보다 가벼움
    animator.SetBool("IsRun", inputVector.sqrMagnitude > 0.01f);

    isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
}

Update에서 Physics2D.OverlapCircle을 이용해

groundCheck.position을 기준으로, groundCheckRadius 범위 내에 groundLayer인 오브젝트가 있는지 찾아낸다

 

 void Awake()
 {
     rb = GetComponentInChildren<Rigidbody2D>();
     animator = GetComponent<Animator>();
     inputActions = new PlayerControlInput();


     inputActions.Player.Move.performed += ctx =>
     {
         inputVector = ctx.ReadValue<Vector2>();

         if (inputVector.x > 0)
         {
             // transform.localScale.x = Mathf.Abs(transform.localScale.x); 이건 안됨
             transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
         }
         else
         {
             transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x) * -1.0f, transform.localScale.y, transform.localScale.z);
         }
     };
     inputActions.Player.Move.canceled += ctx => inputVector = Vector2.zero;

     inputActions.Player.Jump.performed += ctx => 
     {
         if (!isGrounded) return;
         rb.AddForceY(jumpPower);
     };

 }

그리고 점프 부분에서 isGrounded 체크를 해주면 된다

이제 점프가 한 번만 가능하다!

 

애니메이션 연결

한가지 주의할 사항이 있는데, 애니메이션 만들 때 Animation 폴더 선택하고 Create해야 한다.

왜냐하면 현재 선택된 폴더 기준으로 파일을 생성창이 열려서 이상한 곳에 파일을 만들 수도 있다

 

만들어주고

 

Loop Time 꺼준다

저거 켜져 있으면 애니메이션 계속 반복한다

Idle이나 Jump는 반복하는게 맞는데 점프같은 단발성 액션은 꺼 주자

 

점프는 10프레임 정도만 하나의 이미지로 해주자

(애초에 애셋에 이미지가 하나)

 

** 알트를 누르면 Animator 창에서 이동이 가능

일단 트랜지션은 이렇게 만들어주자

Any State는 말 그대로 어떤 스테이트도 가능하다는 뜻

 

이번엔 트리거로 점프를 만들어주고

 

AnyState → Jump는 HasExitTime 없이, 인터럽트 소스는 Current, 조건은 Jump이다

Can Transition To Self는 Any State→ 에서만 있는 설정인데, 스스로에서 스스로로 전이가 가능하냐는 설정이다. 해제해주자.

Jump → Exit는 알아서 잘 되있기에 안해도 된다(HasExitTime 등)

Exit 이후는 알아서 Entry로 돌아간다

 

void Awake()
{
    ...
    inputActions.Player.Jump.performed += ctx => 
    {
        if (!isGrounded) return;

        animator.SetTrigger("Jump");
        rb.AddForceY(jumpPower);
    };

}

그리고 트리거 Jump 설정만 해주면 이제 점프 애니메이션까지 잘 연동된다

 

 

2주차 부록 - 애니메이션

트랜지션 타임라인

이렇게 생긴 건 좌표나 회전 같은 값이 있을 때,

알아서 보간 처리해주는 구간 설정이라고 보면 된다.

근데 우리는 스프라이트를 바꿔 끼는 방식으로 애니메이션을 처리했기에 별 의미는 없다

 

스켈레톤 애니메이션?

각각의 파츠를 분리(팔 관절마다, 다리 관절마다, 몸통 등등등..)하고,

하나하나 포지션이랑 로테이션을 조절해서 끊기지 않는 애니메이션을 만들 수 있음

그 밖에도 Animator로 굉장히 많은 값들을 변경 가능함