카드 만들기 - 애니메이션과 뒤집기(4-5)
카드 애니메이션 만들기
- 카드가 까딱까딱 움직이고, 카드를 클릭했을 때 살짝 작아졌다가 이미지가 뜨는 애니메이션을 만들어 보자. (+클릭시 카드 뒤집기까지)
- Card Prefab을 꺼내와서 씬에 올려 놓고 CardIdle 애니메이션 클립 생성, 루프 체크 -> 클립을 카드 오브젝트에 드래그 드롭 -> Apply All
- CardIdle 더블클릭 -> 10(Rotation z0) -> 20(Rotation z3) -> 40(Rotation z0)
- 까딱까딱 애니메이션 완성
- 다음은 카드 클릭 시 살짝 작아지는 애니메이션. 역시 카드 애니메이션 클립에서 CardIdle 우클릭 -> Create New Clip -> 'Card Flip' -> 10(Scale 1.2)
- 이건 카드 클릭 시 한 번만 동작해야 되기 때문에 루프타임 체크 해제
- 두 개의 애니메이션이 만들어 졌으니, 조건에 따라 둘 중 어느 애니메이션이 실행될 건지 결정해 줘야 한다.
- Card 애니메이션 더블클릭해 컨트롤러로 들어가기 -> Parameters에서 'isOpen' bool값 생성 -> CardIdle 우클릭 -> Make Transition 우클릭해 Flip으로 하나, Flip에서 Idle로 하나 만들기
- Idle에서 Flip으로 가는 건 isOpen이 true일 때 작동시켜주면 됨. Conditions + 눌러주고 Exip Time 체크 해제, Duration 0
- Flip에서 Idle로 가는 건 Exip isOpen이 flase일 때 작동. Conditions + 눌러주고 false 변경 -> Time 체크 해제, Duration 0
- 이렇게 해주면 코드에서 Setbool을 통해 그때그때 원하는 애니메이션을 실행시킬 수 있다.
- 이제 스크립트로 가서 카드가 열리고 닫히는 기능을 만들어 보자.
- 카드를 눌러서 어떤 기능이 실행되려면 버튼이 달려 있어야 한다. Card의 Back 밑에 Text에다가 Button 컴포넌트 추가
- 마지막으로 Card Prefab Apply All 시켜주기
카드 뒤집기 기능 만들기
public void OpenCard()
{
}
- Card 스크립트에 OpenCard 함수를 만들어 줬다.
- 이 OpenCard가 실행될 때 앞면(Front) 카드가 SetActive(true)가 되어야 함. 원래 켜저 있던 물음표가 달려 있는 뒷면(Back) 모양은 꺼져야 함
- 그럼 두 개의 게임 오브젝트가 필요하고, 세 번째로는 애니메이션을 실행시켜 줘야 함
public GameObject front;
public GameObject back;
public Animator anim;
public SpriteRenderer frontImage;
- front back 변수 추가, 애니메이션을 실행시켜 주기 위해 Animator도 가져옴
- 기존에 SpriteRenderer front로 되어있던 건 frontImage로 수정
public void OpenCard()
{
front.SetActive(true);
back.SetActive(false);
anim.SetBool("isOpen", true);
}
- OpenCard에서 front는 켜주고 back은 꺼준다.
- 애니메이션은 SetBool 사용. 파라미터 이름은 isOpen. isOpen값을 true로 해줘야 CardFlip 애니메이션이 실행됨
public void OpenCard()
{
anim.SetBool("isOpen", true);
front.SetActive(true);
back.SetActive(false);
}
- 그런데 애니메이션이 먼저 실행된 다음에 앞면이 켜지고 뒷면이 꺼져야 하니 SetActive을 맨 위로 올려줌
- 이 로직이 제대로 작동하기 위해서 유니티로 가서 세팅을 해주자.
- Card Prefab에 들어가서 Card 스크립트 변수에 각각 넣어준다. anim에는 Card 오브젝트 넣기. front Image는 front를 넣는다.
- 이제 클릭했을 때 실행될 수 있게끔 만들어줘야 함. Text 컴포넌트로 가서 Button -> On Click + -> None에 Card 오브젝트 -> No Function -> Card -> OpenCard() 로직 등록
- 플레이 해보면, 카드를 클릭했을 때 애니메이션이 동작하며 카드의 이미지가 보여진다.
카드 만들기 - 판정 시스템(4-6)
카드 판정 시스템 만들기
- 이제 닫혀야 한다. 클릭한 두 개의 카드를 비교해서 이미지가 같다면 파괴시켜주고 같지 않다면 다시 닫아주는 판정 시스템을 만들어 보자.
- 각각 카드에 입력된 숫자가 짝을 이루고 있기 때문에 카드 이미지를 비교하기 위해서는 이 숫자가 같으면 파괴시켜 주면 되겠다. 결국 이 카드의 숫자를 비교하면 됨
public void Match()
{
Debug.Log("판단하자.");
}
- GameManager 스크립트에다 만들어 보자.
- 먼저 Matched 함수와 디버그 로그를 적어줬다.
public Card firstCard;
public Card secondCard;
- 그리고 필요한 변수 두 개를 작성했다.
- 카드 2개의 정보를 가지고 있어야 하니, 그 정보를 가지고 있는 Card 스크립트 필요. Card 스크립트를 자료형으로 하는 firstCard, secondCard 작성
- 그러면 첫 번째 카드가 열렸을 때 이 카드가 게임 매니저에 있는 firstCard에 정보를 넘겨주면 되겠다.
- 그리고 두 번째로 열리는 카드는 firstCard라고 하는 변수에 정보가 있는지 없는지를 확인하고, 있다면 secondCard 변수에 정보를 넘겨주고 Matched라는 함수를 발동시켜주면 될 것.
- 게임 매니저에 있는 firstCard, secondCard에 접근하기 위해서는 싱글톤을 만들어 줘야 함
public static GameManager Instance;
public Card firstCard;
public Card secondCard;
public Text timeTxt;
float time = 0.0f;
private void Awake()
{
if(Instance == null)
{
Instance = this;
}
}
public void OpenCard()
{
anim.SetBool("isOpen", true);
front.SetActive(true);
back.SetActive(false);
// firstCard가 비었다면,
// firstCard에 내 정보를 넘겨준다.
// firstCard가 비어있지 않다면,
// secondCard에 내 정보를 넘겨준다.
// Matched 함수를 호출해 준다.
}
- 그리고 Card 스크립트로 넘어가서, 이 OpenCard에서 아까 말한 전략처럼 정보를 firstCard에 넣을지 secondCard에 넣을지 결정해 주면 되겠다.
- 위 주석의 내용을 코드로 작성해 보자.
public void OpenCard()
{
anim.SetBool("isOpen", true);
front.SetActive(true);
back.SetActive(false);
// firstCard가 비었다면,
if(GameManager.Instance.firstCard == null)
{
// firstCard에 내 정보를 넘겨준다.
}
public void OpenCard()
{
anim.SetBool("isOpen", true);
front.SetActive(true);
back.SetActive(false);
// firstCard가 비었다면,
if(GameManager.Instance.firstCard == null)
{
// firstCard에 내 정보를 넘겨준다.
GameManager.Instance.firstCard = this;
}
- 내 정보를 넘겨주는 방법은?
- firstCard의 자료형은 Card. 그런데 그게 담겨있는 클래스가 바로 Card 클래스. 그래서 나 자기 자신을 넘겨주면 되기 때문에 this라고 적어주면 됨
- 싱글톤을 만들 때 Instance = this라고 했던 것과 같다.
public void OpenCard()
{
anim.SetBool("isOpen", true);
front.SetActive(true);
back.SetActive(false);
// firstCard가 비었다면,
if(GameManager.Instance.firstCard == null)
{
// firstCard에 내 정보를 넘겨준다.
GameManager.Instance.firstCard = this;
}
// firstCard가 비어있지 않다면,
else
{
// secondCard에 내 정보를 넘겨준다.
GameManager.Instance.secondCard = this;
// Matched 함수를 호출해 준다.
GameManager.Instance.Matched();
}
}
- '비어있지 않다면'은 위와 이어지니 else문 사용
- secondCard 역시 this 사용
- 유니티로 가서 플레이 해보면, 카드 클릭 시 GameManager 창에 클릭한 카드의 정보가 들어간다.
- 이제 이 first, second Card의 정보를 확인해서 숫자가 같지 않다면 닫아주고 같다면 파괴해주는 로직을 Matched에다가 적어보자.
public void Matched()
{
Debug.Log("판단하자.");
}
- GameManager 스크립트로 돌아왔다.
- 이제 이 디버그 로그 대신 firstCard에 있는 idx와 secondCard에 있는 idx를 비교해주는 코드를 적자.
public class Card : MonoBehaviour
{
public int idx = 0;
- 그 전에 잠깐 Card 스크립트로 가서 idx에 접근할 수 있게 int idx 변수를 public으로 만들어 줌
- 이제 GameManager에서 idx를 꺼내올 수 있다.
public void Matched()
{
if(firstCard.idx == secondCard.idx)
{
// 파괴해라.
}
else
{
// 닫아라.
}
}
- 만약에 firstCard에 있는 idx가 같다면 파괴해라. 그렇지 않다면 닫아라.
- 카드를 파괴하고 닫는 이 기능은 Card에다 만들어 주도록 하자.
public void DestroyCard()
{
Destroy(gameObject);
}
public void CloseCard()
{
}
- Card 스크립트로 가서 파괴(DestroyCard)와 닫는(CloseCard) 코드 작성
public void CloseCard()
{
anim.SetBool("isOpen", false);
front.SetActive(false);
back.SetActive(true);
}
- 닫아주는 건 열어줬던 때와 반대로 해주면 되겠다. 애니메이션은 다시 Idle로 바꿔주고 front는 닫아주고 back은 켜준다. 해당 코드 작성
public void Matched()
{
if(firstCard.idx == secondCard.idx)
{
// 파괴해라.
firstCard.DestroyCard();
secondCard.DestroyCard();
}
else
{
// 닫아라.
firstCard.CloseCard();
secondCard.CloseCard();
}
}
- 그리고 GameManager에서 DestroyCard와 CloseCard 함수를 불러준다.
- 유니티로 돌아가서 플레이 해보면, 클릭한 두 카드의 정보가 GameManager 창에 그대로 남아있어서 한번 클릭되면 안 바뀐다.
- 파괴시키든 닫든 간에 기존의 firstCard, secondCard에 있는 정보들은 다시 비워줘야 한다. 이 로직까지 추가하자.
public void Matched()
{
if(firstCard.idx == secondCard.idx)
{
// 파괴해라.
firstCard.DestroyCard();
secondCard.DestroyCard();
}
else
{
// 닫아라.
firstCard.CloseCard();
secondCard.CloseCard();
}
firstCard = null;
secondCard = null;
}
- 뭐가 됐든 일단 정보들은 비워준다(null).
- 다시 플레이 해보면, 이미지가 같을 때 파괴되고 정보들도 null되는 걸 볼 수 있다.
- 문제는 확인할 겨를도 없이 바로 파괴돼버린다. 두 번째 카드는 시간차를 두고 닫거나 파괴해 주자.
- 딜레이 필요. 딜레이는 Invoke를 통해 구현해 보자.
public void DestroyCard()
{
Destroy(gameObject);
}
void DestroyCardInvoke()
{
}
- Card 스크립트에 DestroyCardInvoke 함수 작성
- 안에는 위에 있는 로직을 그대로 가져와 보자.
public void DestroyCard()
{
Invoke("DestroyCardInvoke", 1.0f);
}
void DestroyCardInvoke()
{
Destroy(gameObject);
}
- Destroy(gameObject);를 DestroyCardInvoke에 옮겨주고, DestroyCard에는 Invoke를 실행해 주자. 딜레이는 1초.
public void CloseCard()
{
Invoke("CloseCardInvoke", 1.0f);
}
void CloseCardInvoke()
{
anim.SetBool("isOpen", false);
front.SetActive(false);
back.SetActive(true);
}
- Close도 똑같이 작성해 준다.
- 플레이 해보면, 딜레이가 반영돼 두 개의 카드 이미지를 확인하고서 닫히거나 파괴된다.