애니메이션 2

애니메이터 컨트롤러의 스테이트 머신

 

 

 

애니메이터 컨트롤러 = 스테이트 머신 + 애니메이션 클립

애니메이터는 컴포넌트가 아닌 '애셋'이다. 따라서 게임 오브젝트에 직접 적용할 수 없고 애니메이터의 프로퍼티에 있는 애니메이터 컨트롤러로 링크를 설정해 사용한다. 

애니메이터를 만들고 애니메이터 컴포넌트에 연결하는 방법

 

 

◎ 스테이트 머신

스테이트 머신은 동작 표현 방법의 하나이다. FSM(유한 상태 머신)이라 불리는 모델을 바탕으로 기호를 사용해 동작을 표현할 수 있는 것이 큰 특징이다. 유니티에선 애니메이션 동작의 표현을 위해 스테이트 머신을 사용한다.

 

Animator 창에서 오른쪽 클릭으로 생성한 empty state

 

Animator 창에서는 표현된 상태를 잇는 화살표 기호로 상태간의 트랜지션, 즉 전이를 표현한다.

컨트롤러를 만들면 처음 시작은 Entry이며 이 상태에서 빈 Empty State를 추가하면 Entry -> New State 사이의 화살표가 생긴다

 

 

Animator 창에서 state를 선택하면 보이는 해당 state의 inspector창

 

스테이트 인스펙터 창 기능

Motion : 애니메이션 클립

Speed : 재생 속도 (1.0이 기본 속도)

Multiplier : 재생 속도에 애니메이션 파라미터의 값을 곱한다.

Mirror : 애니메이션의 좌우 반전

Cycle Offset : 반복 재생 시키는 경우의 시작 위치 (재생 시간의 비율은 0과 1사이)

Foot IK : 스테이트 안에 IK 계산에서 발이 땅에 붙도록 자동 보정

Write Defaults : 스테이트를 시작할 때 키 없는 프로퍼티에 기본값을 설정

 

 

 

◎ 애니메이션 클립

애니메이션 클립은 컴포넌트가 아닌 애셋이다. 따라서 단독으로는 다룰 수 없다.

State의 인스펙터에 애니메이션 클립 애셋으로 링크를 연결해 그 스테이트의 상태일 때 애니메이션 클립이 재생되는 구조이다.

 

 

애니메이션 클립을 해당 State에 적용하는 방법은 간단하다. hierarchy 창에서 해당 animation 클립을 드래그앤 드랍으로 Motion에 넣어주면 된다.

 

아니면 아직 스테이트를 생성하지 않은 경우에는 Animator 창에서 오른쪽 버튼을 클릭해서 [From Selected Clip]을 선택하면 애니메이션 클립이 연결된 State를 생성할 수 있다. 

 

애니메이션 재생을 현재 Idle 상태에서 걷기로 바꾸고 싶다면 Idle 스테이트에서 Walk 스테이트로 화살표(트랜지션)를 이어준다. 

 

 

 

 

참고 자료;

유니티 게임 프로그래밍 바이블 (김은철, 유세라 옮김 ; 위키북스) 

 

요즘 Direct를 공부해서 거의 반년만에 유니티를 처음 켜봤더니 어떻게 쓰는지 까먹을 것 같아서 뭐라도 하기로 했다.

내 입맛에 맞는 공부 시간 측정 타이머가 필요하기도 했고 쉽게 만들고 싶으면서도 유니티 내용을 기본부터 포스팅하고 싶어서 타이머로 결정하게 되었다.  


 

타이머의 크기는 300x500 정도가 적당할 것 같아서 300x500으로 결정했다.

 

기본 화면에 300x500 사이즈에 맞는 Image(흰색)를 깔아놓은 상태

 

더보기

Text UI 추가 방법

Hierarchy 창에서 오른쪽 클릭 후 UI->Text를 추가하면 Canvas안에 text가 생성 된다. 

 

우선 기본적으로 Text를 추가해서 다음과 같이 설정해두었다. 

시, 분, 초로 Text를 나누고 다운받아놨던 글씨체도 추가해두었다. 

 

 

이제 기본 세팅을 끝냈으니 버튼을 누르면 시간을 계산하는 기능을 추가할 것이다.

버튼은 Text를 추가하는 방법과 동일하게 Hierarchy 창을 오른쪽 클릭해서 UI->Button을 눌러 생성하거나 상단바의 GameObject->UI->Button으로 추가해도 된다.

 

 

 

 

버튼을 추가하면 위와 같은 기본 버튼이 하나 생성된다. 개인적으로 유니티는 UI 중 버튼이 너무 유용하다는 생각을 했었다. 사용방법이 무궁무진하기 때문이다. 

 

 

그 이유는 위 사진을 보면 알 수 있는데 가장 기본적인 사용방법으로는 마우스가 버튼 위에 있는지 아닌지에 따라서 Color도 쉽게 바꿀 수 있고 Color Tint를 Sprite Swap으로 바꾸면 버튼의 이미지를 바꿀 수도 있고 Animation으로 설정하면 Animation 효과도 넣을 수 있기 때문이다. 참 좋은 기능이다.

 

 

이제 Button 기능과 Script를 연결해야 한다.

우선 Script를 하나 만들어서 함수를 만들어준다. 나중에 추가할 기능들을 위해서 시간을 Manager로 관리해주긴 할 거지만 버튼이 눌렸는지 확인하는 '버튼'용 스크립트를 만든다. 들어가야 하는 기능은 Button을 누르면 Start가 되고 한 번 더 누르면 Stop(멈춤) 상태가 되어야 한다. 

그 스크립트를 끌어다 Button에 넣어주고 아래와 같은 함수를 추가해준다.

 

bool ButtonActive; // 버튼이 눌렸는지 안눌렸는지 확인하는 상태

public void ClickButton()
{
	if(!ButtonActive)
    {
    	ButtonActive = true;
    }
    else
    {
    	ButtonActive = false;
    }
}

 

 

Button의 OnClick() 은 처음에는 List is Empty 상태, 즉 비어있지만 + 를 누르게 되면 아래와 같이 변한다.

 

 

 

이렇게 바뀐 부분의 None(Object)에 hierarchy창의 해당 버튼 오브젝트를 드래그해서 넣어준다. 그러면 Function 부분을 선택할 수 있게 되고 눌러보면 아래와 같이 내가 추가한 스크립트가 나온다. 그 안에 함수의 목록들도 나오게 되는데 그 중 ClickButton()을 눌러주면 버튼의 클릭과 해당 함수가 연결 된다. 

 

 

 

버튼을 연결했으니 이제 시간초만 계산해주면 된다. 타이머 매니저를 만들기 위해 빈 오브젝트를 하나 추가해 주고 매니저용 스크립트도 하나 만들어 준다. 

 

public class TimerCountManager : MonoBehaviour
{

    bool TimerOn; // Timer가 돌아가고 있는지 

    void Start()
    {
        TimerOn = false;
    }

    void Update()
    {

    }

    public void SetTimerOn()
    {
        TimerOn = true;
    }

    public void SetTimerStop()
    {
        TimerOn = false;
    }


}

 

 

위의 스크립트는 추가한 매니저에 넣고 버튼이 눌렸을 때 Manager에 전달해 주기 위해서 Button 스크립트도 아래와 같이 수정한다.

 

 

public class TimerCtrlButton : MonoBehaviour
{
    bool ButtonActive; // 지금 숫자 세고 있는지
    public GameObject TimerManager;
    TimerCountManager Tm;

    void Start()
    {
        ButtonActive = false;
        Tm = TimerManager.GetComponent<TimerCountManager>();
    }

    void Update()
    {
        
    }

    public void ClickButton()
    {
        if(!ButtonActive)
        {
            // 시작해야 되는 부분
            Tm.SetTimerOn();
            ButtonActive = true;
        }
        else
        {
            // STop
            Tm.SetTimerStop();
            ButtonActive = false;
        }
    }
}
더보기

 혹시몰라서 설명

TimerManager를 직접 Button의 해당 스크립트로 드래그해서 가져다 놓으면 해당 오브젝트의 스크립트(위 코드의 Tm부분)를 찾아서 쓰기 위해 Start에서 처음 시작할 때 스크립트를 찾아 놓는 것이다. 

 

 

보통 Find를 많이 쓰는데 성능 저하가 심한 대표적 기능이라고 해서 안쓰게 된 이후로 간단한 것들은 Find를 안하고 직접 메타를 연결해 주고 있다. 그러니까 Hierarchy창에 있는 TimerManager를 Button에 드래그로 연결해 준다.

 

 

 

 

이렇게 하면 이제 Button을 클릭할 때 마다 Button이 눌렸다고 Manager에게 전달하게 된다. 

전달이 됐으면 이제 시간 계산이 남아있다.

 

 

시간 계산을 위해서 Text를 연결해야 한다.

Text는 time을 Manager가 가지고 있게 할 거라서 시간을 바로 계산해서 한번에 하려고 Manager에서 같이 관리하도록 할 것이다. 

 

 

Text 추가를 위해서 우선 해야 할 것이 있다. 스크립트에서 UI 관련한 부분을 사용할 것이라고 알려주기 위해

 

using UnityEngine.UI;

 

이것을 위에 추가해주어야 한다. 그래야 text를 비롯한 image, sprite 등 UI와 관련한 부분을 스크립트에서 변경할 수 있다. 

 

 

 

이것을 추가했으면 Manager에 text와 time을 추가해 준다.

   public Text[] ClockText; // Text
    float time;  // 시간 계산 

 

Text는 text component를 연결하는 형식이고 hour, minute, second 이 세 개를 배열로 관리하려고 배열 형식으로 선언해 놨다. 

 

이렇게 선언해 놨으면 이제 Manager의 해당 스크립트 부분으로 가서 0으로 되어 있는 size를 3으로 변경해 주고 0번 부터 text인 hour, minute, second 순으로 넣어준다.

 

 

 

이제 이 연결된 text로 계산만 하면 된다. 

 

Manager의 Update부분에 타이머가 눌렸으면 시간이 추가 되게 하고 추가된 시간을 기준으로 시간, 분, 초를 계산해 낸다. 

 

더보기

혹시 몰라서 시간 계산

1 시간은 3600초 이므로 time 을 3600으로 나누고

1 분은 60초 이므로 time을 60으로 나눈 몫의 값이다. 근데 1시간이 됐을 때 60이상으로 넘어가면 안되니까 60으로 한 번 더 나머지 계산을 해준다. 

1 초는 60초가 모이면 1분이 되어야 하므로 time을 60으로 나눴을 때 나머지 값이 된다. 

 

    void Update()
    {
        if (TimerOn)
        {
            time += Time.deltaTime;
            ClockText[0].text = ((int)time / 3600).ToString();
            ClockText[1].text = (((int)time / 60 % 60)).ToString();
            ClockText[2].text = ((int)time % 60).ToString();
        }
    }

 

 

이렇게 추가하고 play를 눌러보면 Button을 눌렀을 때 시간 계산이 잘 되고 있는 것을 볼 수 있다.

 

 

 

 

 

 

+ Recent posts