[압량(Amnyang)] #6. 2D 주인공 캐릭터 달리기(Run) 애니메이션 추가하기

2022. 2. 5. 17:33Projects/Amnyang

 

 

 

학교에서 일주일간 진행했던 게임 교육 특강을 듣고, 설 연휴도 겹쳐서 작업 스케쥴이 다소 밀렸다. 설 연휴가 끝난 후에 회의를 진행했고, 프로젝트 종료일인 2월 말까지 얼마 남지 않은 탓에 세부적인 디테일은 포기하기로 했다.

 

캐릭터가 숨는 모션, 적이 찾는 시늉을 하는 모션, 문 열리거나 상호작용하는 모션 등이 있으면 게임 완성도는 올라가지만 시간이 부족하다. 디자이너 님도 한 분밖에 없다보니 UI와 배경작업도 하셔야 하는데 그거까지 할 시간이 없으실 것 같다.

 

그래서, 우선 게임 플레이에 필수적인 요소들만 먼저 빠르게 구현하기로 했다. 저번 글에서 주인공 캐릭터의 Idle, Walk 애니메이션을 작업했는데, 다음 애니메이션은 달리기(Run)다.

 

 

 

1. 달리기(Run) 애니메이션 제작하기

 

걷기(Walk)를 한 번 만들어봤던 탓인지, 익숙해져서 달리기(Run) 모션은 그리 오래 걸리지 않았다. 마찬가지로 달리기 모션을 프레임별로 나타낸 그림을 참고하여 만들어봤다. 2D Bone Animation의 한계로 왼손 모양은 어쩔수가 없었다.

 

0 프레임

 

5 프레임

 

10 프레임

 

15 프레임

 

20 프레임

 

25 프레임

 

30 프레임

 

35 프레임

 

40 프레임

 

 

달리기 모션이다보니, 조금 빠르게 움직여야 하므로 5 프레임 단위로 구성했다. 달리기를 할 때 상체는 자연스럽게 숙여지므로 숙였고, 다리가 교차하며 바닥에 닿일 때마다 상체 뼈를 움직여 반동을 줬다. 오른손은 휴대폰을 쥐고 있기 때문에, 뛰면서 오는 몸의 반동으로 인한 약간의 움직임만 줬다. 만드는데 30분도 안 걸렸던 것 같다.

 

달리기 애니메이션

 

 

애니메이터 구조 변경 (Blend Tree 사용)

 

Idle, Walk, Run 애니메이션이 생겼다보니, 기존 방법대로는 복잡하고 다루기 힘들 것 같았다. 그래서 블렌드 트리(Blend Tree)를 하나 만들어서 "Move"라고 이름을 지어줬다.

 

기존 애니메이션들 구조는 삭제하고, 블렌드 트리를 생성

 

그리고, Float 타입의 매개변수를 생성하고, "Move"라고 이름을 지었다. 이 Move 매개변수 값이 0일 땐 "Idle", 0.5일 땐 "Walk", 1일 땐 "Run"으로 애니메이션 전환이 일어나도록 구성했다.

 

Move 블렌드 트리 내부 구성 모습

 

Move 블렌드 트리 Inspector

 

Automate Thresholds 옵션을 체크 해제하고, 각 Threshold 값에 계획했던 값들을 넣었다. 이제 스크립트를 수정하는 일만 남았다.

 

 

 

2. 스크립트(Script) 수정하기

 

  • 왼쪽 쉬프트(Left Shift)와 방향키 입력이 있다면, 달리기(Run)
  • 왼쪽 쉬프트(Left Shift) 입력은 없지만 방향키 입력이 있다면 걷기(Walk)
  • 두 입력 모두 없다면 가만히 있기(Idle)

 

현재 달리는 상태인지를 체크하기 위해 boolean 변수를 하나 추가했다.

private bool isRunning;

 

Update() 함수에서 왼쪽 쉬프트에 대한 입력을 받았다.

void Update()
{
   walkDirection = Input.GetAxisRaw("Horizontal");
   isRunning = Input.GetButton("Run");    // 추가
}

 

물론, Input Manager에서 Run 버튼에 대한 필드를 추가해줬다. 그 부분은 밑에 "더보기" 클릭

더보기

Unity Editor의 왼쪽 상단 Edit → Project Settings → Input Manager → Run 필드 추가

 

 

 

기존에 있었던 Walk() 함수를 삭제하고, Move() 함수로 통합했다. 그리고, 약간의 리팩토링 과정을 거쳤다.

   void FixedUpdate()
    {
        bool hasControl = !Mathf.Approximately(walkDirection, 0f);
        TurnOtherSide(hasControl);
        Move(hasControl);
    }

    private void Move(bool hasControl)   // 0f : Idle, 0.5f : Walk, 1f : Run
    {
        if (hasControl && isRunning)
        {
            animator.SetFloat("Move", 1f);
            _rigidBody.velocity = new Vector2(walkDirection * walkSpeed * 2f,
                                              _rigidBody.velocity.y);
            return;
        }

        float walkValue = (hasControl && !isRunning) ? 0.5f : 0f;
        animator.SetFloat("Move", walkValue);
        _rigidBody.velocity = new Vector2(walkDirection * walkSpeed, _rigidBody.velocity.y);
    }

 

이렇게 함으로써, 달리기 애니메이션 제작 및 적용이 완료되었다. 빨리 점프랑 상호작용 하러 가야겠다...

 

달리기 애니메이션 적용 완료

 

 

✏️전체 스크립트

더보기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SujiMoveController : MonoBehaviour
{

    public float walkSpeed;
    public Transform sujiTransform;

    private Animator animator;
    private Rigidbody2D _rigidBody;
    private float walkDirection;
    private float initScaleX;
    private bool isRunning;


    void Start()
    {
        _rigidBody = GetComponent<Rigidbody2D>();
        animator = GetComponentInChildren<Animator>();          
        initScaleX = sujiTransform.localScale.x;
    }

    void Update()
    {
        walkDirection = Input.GetAxisRaw("Horizontal");
        isRunning = Input.GetButton("Run");
    }

    void FixedUpdate()
    {
        bool hasControl = !Mathf.Approximately(walkDirection, 0f);
        TurnOtherSide(hasControl);
        Move(hasControl);
    }

    private void Move(bool hasControl)   // 0f : Idle, 0.5f : Walk, 1f : Run
    {
        if (hasControl && isRunning)
        {
            animator.SetFloat("Move", 1f);
            _rigidBody.velocity = new Vector2(walkDirection * walkSpeed * 2f,
                                              _rigidBody.velocity.y);  
            return;
        }

        float walkValue = (hasControl && !isRunning) ? 0.5f : 0f;
        animator.SetFloat("Move", walkValue);
        _rigidBody.velocity = new Vector2(walkDirection * walkSpeed, _rigidBody.velocity.y);
    }

    private void TurnOtherSide(bool hasControl)
    {
        if (!hasControl)
            return;

        var scaleX = sujiTransform.localScale.x;
        if (Mathf.Approximately(walkDirection * scaleX, initScaleX))
            return;

        var scaleY = sujiTransform.localScale.y;
        var scaleZ = sujiTransform.localScale.z;

        sujiTransform.localScale = new Vector3(-scaleX, scaleY, scaleZ);
    }
}

 

 

728x90
반응형