오늘 한 일
- 카드 애니메이션 만들어보기 (오늘 쓸 내용)
- 와이어 프레임에 대해서 알고 작성해보기
- 깃허브 배우기
오늘 구현한 카드 애니메이션
- CardSmallJump
(https://velog.velcdn.com/images/b_h_b/post/86220e65-da6b-486c-838f-f45f51128301/image.mp4)- CardBigJump
(https://velog.velcdn.com/images/b_h_b/post/9483b414-17b3-4543-8118-144b14af46bf/image.mp4)- CardWhirling
(https://velog.velcdn.com/images/b_h_b/post/aea7999d-b86e-493e-a720-07efe3a4f501/image.mp4)
(뒷면)
(앞면)
애니메이터 살펴보기
간략화
평상 시 카드 - 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
코드 짜기
변수 짜기
// 배치 애니메이션 전용 코드 (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을 몰라서 경계하고 사용을 금기했었는데, 본격적으로 만져보니까. 용도를 알게되어 자주 쓰게 되었다. (모르는 건 건들기 전에 배우는 게 좋지만, 직접 건들여보지 않으면 사용법을 확실히 알 수 없다.)