내일배움캠프 3일차 TIL - 애니메이션

백흰범·2024년 4월 17일
0

오늘 한 일

  • 카드 애니메이션 만들어보기 (오늘 쓸 내용)
  • 와이어 프레임에 대해서 알고 작성해보기
  • 깃허브 배우기

오늘 구현한 카드 애니메이션




카드 애니메이션 만들기

카드 다시 감추기

  • 카드를 다시 감추고 싶다면 가지고 있는 클립을 Front/Back SetActive을 활용하면 된다.

(뒷면)

(앞면)

  • 이걸 닫는 애니메이션에서는 이 애니메이션 클립을 복제하고 SetActive를 반대로 넣어주면 된다.

애니메이터 살펴보기

애니메이터 흐름

각 Transition 흐름

간략화

평상 시 카드 - isOpen -> 카드 열기 - !isOpen -> 카드 닫기 -> 평상 시 카드
애니메이션의 자연스러움을 위해 CardFlip, CardClose에서 나갈 때 Has Exit Time도 넣었다.

그냥 애니메이터의 흐름을 고치기만 한다면
별도의 코드 수정 없이 카드를 편하게 열고 닫을 수 있다.
(SetActive 부분만 빼주면 된다.)

애니메이션의 시간 설정만 유의해서 코드를 짜면 된다. (Invoke() 활용)
(아니면 애니메이션 이벤트 기능들을 알아보는 게 훨씬 더 좋다.)

작동되는지 확인!

(https://velog.velcdn.com/images/b_h_b/post/2c4d56a6-04d6-4175-a633-71cd24e2b059/image.mp4)



매칭 시 애니메이션

  • 이번에는 맞추면은 왼쪽으로 걸어서 나가는 듯한 애니메이션을 만들어보자

애니메이션 클립 설정

  • 단순히 왼쪽으로 빠지게 설정했다. (rotation을 좌우로 왔다갔다하면서)
    % 유의해야할 점 %
    ! 자식 객체(Front)가 아닌 본 객체(Card)의 Position 값을 변경할 경우 월드 좌표대로 이동하게 된다.
    (즉, 이 자식 객체는 부모 객체의 좌표를 기준으로 왼쪽으로 이동하는 것이다. )
    ! 만약에 귀찮다고 본 객체의 Rotation을 건들게 되면, 걸어가는 게 아니라 위로 갔다 아래로 갔다 할 것이다.
    + Scale을 바꾸면 자식의 position 이동 거리가 작아진다;
    (그러니까 자식 객체는 부모 객체의 Transform 값에 영향을 받게 된다.)

애니메이터 설정

  • 만약에 카드가 매치 되었을 경우 CardFlip의 애니메이션이 끝나고 나서(Exit Time = 1) 애니메이션을 실행하게 될 것이다.

필요한 코드

// 카드 맞출 시에 호출될 함수 (Invoke 활용을 위해)
void CardMatched()
{
    anim.SetBool("isMatch", true);
}
// 카드 매칭 되었을 때 호출되는 함수 DestoryCard에 넣어준다.
public void DestroyCard()
{
    CardMatched();	// Exit Time이 있으니 믿고 불러주자. (원한다면 Invoke 해줘도 된다.)
	Invoke("DestroyCardInvoke", 1.0f); // 갑자기 사라지지 않게 하기 위해 Invoke를 넣어주자.
}

결과물

(https://velog.velcdn.com/images/b_h_b/post/be95540a-5a54-44f3-bff7-f49cc31a746c/image.mp4)




순차적인 배치 애니메이션

  • 카드가 순서대로 위에서 촤르륵 깔리는 애니메이션을 연출하고자 한다.
    (이는 코드 작성에 신경을 좀 써줘야한다.)

애니메이션 클립

애니메이션을 보여주기 전에 가려줄 친구와 등장 애니메이션 클립을 만들어준다.

(HideCard)

  • 애니메이션의 부자연스러운 연출을 막기 위해서 HideCard 클립을 만들어줬다.

(ShowCard)

  • 위에서부터 빠르게 떨어진다. (나중에 Back을 활성화시킨다.)

애니메이터 설정

  • Entry에서 주황색으로 향하는 선이 그 객체의 첫 애니메이션인데 이걸 HideCard에다가 꽂아주면 된다.

(방법)

  • 원하는 노드를 우클릭한 다음 Set as Layer Default State를 눌러준다

HideCard -> ShowCard

ShowCard -> CardIdle


코드 짜기

  • Coroutine의 도움을 받거나 Invoke를 활용해보자

변수 짜기

    // 배치 애니메이션 전용 코드
    (Card.cs)
    public int showNum;		// Board에서 번호를 받아와야한다.
    Coroutine coroutine;	// 시간 지연을 위해 넣어줫다.
    (GameManager.cs)
    public bool timerOn = false;	// 타이머 작동을 책임져 줄 논리값

Board에서 정보 받아오기

        for (카드 위치값을 할당해주는 반복문)
        {
            GameObject go = Instantiate(card, this.transform);
            ...
            go.GetComponent<Card>().showNum = i;	// 이 변수는 순차를 넣기 위해 사용될 것이다.
        }

코루틴을 활용한 방법

(Card.cs)
	void Start()
    {	
    	...
    	coroutine = StartCoroutine(ShowCard());	// 코루틴을 활성화
    }

    IEnumerator ShowCard()
    {
    	// 순서에 따라서 낙하
        yield return new WaitForSecondsRealtime(showNum*0.1f);    
        anim.SetBool("ShowCard", true);
       
		// 코드에 시간차를 둬 정상적으로 애니메이션이 출력되게 지연
        yield return new WaitForSecondsRealtime(0.2f);
        anim.SetBool("ShowCard", false);
       
 		int lastCard = ((GameManager.Instance.level + 2) * 4) - 1; // 마지막 카드 확인용
        if (showNum == lastCard)	// 마지막 카드까지 깔릴 경우
        {
            yield return new WaitForSecondsRealtime(0.5f);	// 타이머 실행을 여유있게 설정
            GameManager.Instance.timerOn = true; // 타이머 작동
        }

        coroutine = null;	// 역할을 다했으니 최적화를 위해 없애준다.
    }
  • 이거 하나 넣었더니 게임이 무거워졌다는 느낌이 들었다;

Invoke를 활용한 방법

(Card.cs)
	void Start()
	{
		...  
	    Invoke("ShowCard", showNum * 0.1f);	// showNum을 활용한 순차 지연
	}

	void ShowCard()
	{
		anim.SetBool("ShowCard", true);		// 떨어지는 애니메이션 발동
		Invoke("ShowCardInvoke", 0.2f);		// 다음 애니메이션 지연
	}
	void ShowCardInvoke()
	{
		anim.SetBool("ShowCard", false);	// CardIdle 상태로 진입
	    Invoke("CheckLastCard", 0.5f);		// 타이머 실행을 여유있게 설정
	}
	void CheckLastCard()
	{
		int lastCard = ((GameManager.Instance.level + 2) * 4) - 1; // 마지막 카드 확인용
		if (showNum == lastCard)	// 마지막 카드까지 깔릴 경우
		    GameManager.Instance.timerOn = true;	// 타이머 작동
	}
  • 간단한 지연만 필요할 때는 Invoke를 활용해주는 것이 좋다.
  • 카드가 떨어지는 동안에는 게임 매니저의 타이머가 돌아가서는 안된다.

타이머 조건부 설정

(GameManager.cs)
void Update()
{
	if (timerOn)	// 타이머 조건문
    {
         time -= Time.deltaTime;
    }	
    ...
}
  • 이렇게 설정한다면, 카드 배치 애니메이션이 끝나기 전까지는 타이머가 작동되지 않는다.

카드가 배치되는 동안에도 뒤집어지는 것을 막아야한다

카드 열기 조건문 추가

public void OpenCard()
{
	if ((각종 쌓여잇는 조건문) || !GameManager.Instance.timerOn) //남의 bool을 빌려주자
            return;
}
  • 이제 배치가 잘 되는지 결과물을 한번 봐보자

결과물

coroutine 활용

(https://velog.velcdn.com/images/b_h_b/post/71befe72-aa42-4515-80aa-6e394a03d809/image.mp4)

Invoke 활용

(https://velog.velcdn.com/images/b_h_b/post/355971ee-1121-4134-a89f-f7d776948dbf/image.mp4)




그 외에 새로 알게된 점

유니티의 애니메이션에 대한 정보

  • Animator에서 Preview 창을 보면 몇 프레임인지 애니메이션 진척도가 몇 프로인지 알 수 있다!

~ 처음에는 Animation 창에 있는 숫자가 초인줄 알았는데 알고보니 Frame이였다.
~ Animation 창의 TargetFrame은 60 Animator Inspector는 30으로 잡혀있다.
(그래서 타임라인으로는 절반으로 줄여져서 보이는 것이다.)
~ 애니메이션의 끝나는 지점에 맞춰서 코드를 짜기 힘들다면 애니메이션 이벤트를 활용하는 것도 좋다.




오늘 공부하면서 느낀점

오늘은 알게된 정보가 너무 많아서 혼자 정리하는 것만으로도 힘들었다. 많이 배운만큼 얻어갈 게 있으면 좋겠지만, 정보라는 존재는 단순히 듣고 본다고만 해서 얻어가는 녀석은 아닌 것 같다. 정보를 어디서 얻을 수 있는지 아는 것도 중요하지만, 그만큼 정보를 담아내는 능력도 매우 중요하다고 생각한다. 아무리 배울 거리들이 많더라도 담아내는 능력이 부족하다면 좋은 정보들을 흘리게 될 뿐이다. 이 캠프에서 배울거리가 많은만큼 나의 정보 가방을 열심히 늘려줘야할 필요가 있다.

그리고 분명 어제 Has Exit Time을 몰라서 경계하고 사용을 금기했었는데, 본격적으로 만져보니까. 용도를 알게되어 자주 쓰게 되었다. (모르는 건 건들기 전에 배우는 게 좋지만, 직접 건들여보지 않으면 사용법을 확실히 알 수 없다.)

profile
게임 개발 꿈나무

0개의 댓글