9. 네 번째 연습 게임(2)

이규성·2023년 11월 2일
0

TIL

목록 보기
13/106

11/02 이어서 진행 !

카드를 랜덤으로 섞기

8장의 카드가 두 장씩 랜덤으로 생성되는 것이 목표이다. 각 카드에 (0, 0, 1, 1, 2, 2, ~) 이렇게 번호를 메겨 이미지를 설정해주자.

// gameManager.cs
using System.Linq;

void Start()
{
    int[] rtans = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 };
    // rtans 변수에 데이터를 저장해 준다.
    rtans = rtans.OrderBy(item => Random.Range(-1.0f, 1.0f)).ToArray();
    // 리스트의 데이터를 정렬해 주겠다 => 랜덤하게 !.리스트로 저장;

    for (int i = 0; i < 16; i++)
    {
        GameObject newCard = Instantiate(card);
        newCard.transform.parent = GameObject.Find("cards").transform;

        float x = (i / 4) * 1.4f - 2.1f;
        float y = (i % 4) * 1.4f - 3.0f;

        newCard.transform.position = new Vector3(x, y, 0);

        Debug.Log(rtans[i]); // 콘솔에 로그로 출력
    }
}

리스트에 있는 데이터가 랜덤하게 실행이 된다 !

카드에 이미지를 붙여주기

이미지를 코드로 사용하기 위해 Resources 폴더 생성 후 넣어준다.

// gameManager.cs
void Start()
{
    int[] rtans = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 };
    rtans = rtans.OrderBy(item => Random.Range(-1.0f, 1.0f)).ToArray();

    for (int i = 0; i < 16; i++)
    {
        GameObject newCard = Instantiate(card);
        newCard.transform.parent = GameObject.Find("cards").transform;

        float x = (i / 4) * 1.4f - 2.1f;
        float y = (i % 4) * 1.4f - 3.0f;

        newCard.transform.position = new Vector3(x, y, 0);

        string rtanName = "rtan" + rtans[i].ToString();
        // 이미지의 이름을 만들어 준다.
        newCard.transform.Find("front").GetComponent<SpriteRenderer>().sprite = Resources.Load<Sprite>(rtanName);
        // front를 찾아서 sprite를 Resources 폴더의 이미지로 변경해 준다.
    }
}

적용이 잘되었다 !

애니메이션 추가

  • Animation - card_idle 생성
  • 프리팹 시킨 card를 하이라키에 꺼낸다.
  • 0:20에 rotation z:1.3 - 0:40에 rotation z:1 설정

카드가 흔들리는 듯한 효과 !

  • Animation - card_flip 생성
  • 0:10에 scale x:1.2, y:1.2 - 0:20에 scale x:1.3, y:1.3

누르는 듯한 효과 !

  • Animator - transition 추가
  • has exit time 체크 해제 - transition duration:0

즉시 애니메이션이 실행되게 설정

  • parameters - bool 생성, isOpen 명명
  • idle에서 flip isOpen=true
  • flip에서 idle isOpen=false

애니메이션 재생 시기를 설정 !
C# 기본 문법 공부 때 bool 형식이 자주 쓰인다고 배웠을 때 언제 쓰일까? 궁금했는데 직접 설정을 해보니 아주 강력한 문법임을 알게 되었다.

// card.cs
public Animator anim; // 만들어둔 animator을 card에 알려준다

public void openCard()
{
    anim.SetBool("isOpen", true); // idle에서 flip parameters 실행
    transform.Find("front").gameObject.SetActive(true); // 카드 앞면 활성화
    transform.Find("back").gameObject.SetActive(false); // 카드 뒷면 비활성화
}
  • card script 생성 후 코드 추가

  • card script anim에 card animator 적용하기
  • button component 생성 후 openCard 설정

카드가 뒤집어 진다 !

카드 매칭하기

목표는 같은 카드를 뒤집었다면 사라지게 만들고 다른 카드라면 다시 뒤집어 준다.

// gameManager.cs  싱글톤
public static gameManager I;

void Awake()
{
    I = this;
}
// gameManager.cs
public GameObject firstCard;
public GameObject secondCard;

public void isMatched()
{
    Debug.Log("판단하자");
}
  • 카드 이름 저장 후 isMatched()함수 만들기
// card.cs
public void openCard()
{
    anim.SetBool("isOpen", true);
    transform.Find("front").gameObject.SetActive(true);
    transform.Find("back").gameObject.SetActive(false);

    if (gameManager.I.firstCard == null)
    {
        gameManager.I.firstCard = gameObject;
    }
    else
    {
        gameManager.I.secondCard = gameObject;
        gameManager.I.isMatched();
    }
}
  • gameManager의 firstCard가 비어있다면 card를 적용한다.
  • firstCard가 비어있지 않다면 secondCard에 card를 적용한다.
// gameManager.cs
 public void isMatched()
 {
     string firstCardImage = firstCard.transform.Find("front").GetComponent<SpriteRenderer>().sprite.name;
     string secondCardImage = secondCard.transform.Find("front").GetComponent<SpriteRenderer>().sprite.name;
     // front의 이미지 이름을 불러오는 변수 저장

     if (firstCardImage == secondCardImage) // 두 이미지의 이름이 같다면 !
     {
         Debug.Log("같다!");
     }
     else
     {
         Debug.Log("같지 않다!"); // 두 이미지의 이름이 다르다면 !
     }

     firstCard = null;
     secondCard = null;
     // 적용되었던 card를 비워준다.
 }

gameManager.cs에서 첫 번째 카드와 두 번째 카드가 동일한지 판단하는 함수를 만들어 준다.

// card.cs
public void destroyCard() // gamaManager에서 사용하기 위해 public 입력
{
   Invoke("destroyCardInvoke", 1.0f); // 1초 뒤에 실행
}

void destroyCardInvoke() // card에서만 작동하면 되니 public를 입력할 필요는 없다.
{
   Destroy(gameObject); // 카드 삭제
}

public void closeCard()
{
   Invoke("closeCardInvoke", 1.0f);
}

void closeCardInvoke() // 카드를 뒤집어주는 함수
{
   anim.SetBool("isOpen", false); 
   transform.Find("back").gameObject.SetActive(true);
   transform.Find("front").gameObject.SetActive(false);
}

card.cs에서 카드를 사라지게 만드는 함수, 둘 다 뒷면으로 뒤집어지는 함수를 만들어 준다.

// gameManager.cs
public void isMatched()
{
    string firstCardImage = firstCard.transform.Find("front").GetComponent<SpriteRenderer>().sprite.name;
    string secondCardImage = secondCard.transform.Find("front").GetComponent<SpriteRenderer>().sprite.name;

    if (firstCardImage == secondCardImage)
    {
        firstCard.GetComponent<card>().destroyCard();
        secondCard.GetComponent<card>().destroyCard();
    }
    else
    {
        firstCard.GetComponent<card>().closeCard();
        secondCard.GetComponent<card>().closeCard();
    }

    firstCard = null;
    secondCard = null;
}
  • 같은 카드가 뒤집어 진다면 카드를 파괴하는 함수를 실행한다.
  • 카드가 다르다면 뒤집어 주는 함수를 실행한다.

결과물 !

게임의 끝

끝났다는 텍스트를 만들어 준다.

  • font size:200, width:400, height:300, pos y:0
  • color:220, 255, 0, 255
// gameManager.cs
public GameObject endTxt; // endTxt를 gameManager에게 알려 준다.

public void isMatched()
{
    string firstCardImage = firstCard.transform.Find("front").GetComponent<SpriteRenderer>().sprite.name;
    string secondCardImage = secondCard.transform.Find("front").GetComponent<SpriteRenderer>().sprite.name;

    if (firstCardImage == secondCardImage)
    {
        firstCard.GetComponent<card>().destroyCard();
        secondCard.GetComponent<card>().destroyCard();
        
        // 카드 두 장이 동일한지 판단할 때 !
        int cardsLeft = GameObject.Find("cards").transform.childCount;
        // cards 하위에 있는 항목의 개수를 불러온다.
        if (cardsLeft == 2) // 항목의 개수가 2개일 때 !
        {
            endTxt.SetActive(true); // endTxt를 활성화
            Time.timeScale = 0.0f; // 게임의 시간을 멈춘다.
        }
    }
    else
    {
        firstCard.GetComponent<card>().closeCard();
        secondCard.GetComponent<card>().closeCard();
    }

    firstCard = null;
    secondCard = null;
}
// endTxt.cs
using UnityEngine.SceneManagement;

public void retryGame()
{
    SceneManager.LoadScene("MainScene");
}

끝~에 button component를 추가하기 위해 script 생성 후 코드 추가

// gamaManager.cs
void start()
{
   Time.timeScale = 1.0f;
}

시간을 다시 흐르게 해준다.

결과물 !

제한시간 30초 걸어주기 !

// gameManager.cs
void Update()
{
    time += Time.deltaTime;
    timeTxt.text = time.ToString("N2");

    if (time > 30.0f)
    {
        endTxt.SetActive(true);
        Time.timeScale = 0.0f;
    }
}

gameManager.cs의 Update( ) 부분에 코드를 추가해주면 간단하게 완성
if (time == 30.0f)로 처음에 적용을 했었는데 시간이 멈추지 않았다. 그래서 해설을 보고 풀었으나 딱 30이 되었을 때 왜 if가 실행되지 않았는지는 튜터님께 물어볼 예정이다.

Feedback

새로운 명령어와 기능들이 가장 많이 등장했던 예제였다.. 그렇게 어려운 개념은 없었으나 반복숙달이 요건인 듯 싶다. 나중에 시간을 내어 내가 좋아하는 이미지들로 만들 예정이며, 도전 목표의 추가 기능들도 반드시 해볼 작정이다 !

0개의 댓글