12/21 본캠프 #1

guno park·2023년 12월 21일
0

본캠프

목록 보기
1/77
post-custom-banner

Github

우리 조에 Git에 익숙하신 분이 한분 계셔서 많은 도움이 되었다. 여러가지 있지만 기억나는 사용법을 적어본다. (Git repository 작성 제외)
1. Github 아이디가 필요함(예전에 쓰던 아이디 찾음)
2. Fork, sourcetree, github desktop 3개 중 하나를 선택 (직관적이기로는 3번이 제일 편했다)
3. git 생성자의 초대를 받아 접속한 후 프로그램에서 Clone 생성을 함.
4. Branch도 중요함. 메인이 있고 가지별로 push를 하면 나중에 통합하는 식으로 사용가능
5. 가끔 다른 컴퓨터에선 멀쩡한데 바이러스 있다고 하기도 함(이 디바이스 허용으로 돌림)

미니프로젝트

내가 맡은 파트는
"클릭할 때(카드 뒤집을 때), 시작할 때, 진행 중일 때 성공, 실패 소리 넣어보기"
초기에 사용 중이던 Sound는 4주차 수업 때 제공받은 음원으로, 다른 음원을 사용하기 위해 지난 세일 때 사놨던 에셋 번들 중 "Cute UI & Interact Sound Effects Pack" 에서 몇가지 뽑아서 사용하였다.

Audioclip으로 사용하기 위해선 AudioClip "Name"으로 선언하고 참조를 직접 물려주고 각각의 동작에 해당하는 코드에 AudioSource.PlayOneShot(name,float Volume) 해주면 된다.

하다가 어렵거나 애매했던 부분 2가지를 짚고 넘어간다.

Flip과 ReFlip

public class card : MonoBehaviour
{
    Animator anim;
    GameObject front;
    GameObject back;
    AudioSource audioSource;
    [SerializeField] AudioClip flip;
    [SerializeField] AudioClip reFlip;

    private void Start()
    {
        anim = GetComponent<Animator>();
        front = transform.Find("front").gameObject;
        back = transform.Find("back").gameObject;
        audioSource = GetComponent<AudioSource>();
    }
    public void openCard()
    {
>         audioSource.PlayOneShot(flip); //#1
        anim.SetBool("isOpen", true);
        front.SetActive(true);
        back.SetActive(false);

        //checkFirst()는 firstCard가 있을때 true로 반환.
        //반대로 null값을 가지면 false
        //만약 GameManager가 가진 firstCard가 없다면
        if(!GameManager.Instance.checkFirst())
        {
            GameManager.Instance.setFirst(gameObject);
        }
        //반대로 firstCard가 있다면
        else
        {
            GameManager.Instance.setSecond(gameObject);
            GameManager.Instance.isMatched();
        }
    }
    public void destroyCard()
    {
        Invoke("destroyCardInvoke", 1.0f);
    }

    private void destroyCardInvoke()
    {
        Destroy(gameObject);
    }
    public void closeCard()
    {
        Invoke("closeCardInvoke", 1.0f);
        
    }
    void closeCardInvoke()
    {
>         anim.SetBool("isOpen", false); //#2
        transform.Find("back").gameObject.SetActive(true);
        transform.Find("front").gameObject.SetActive(false);
        audioSource.PlayOneShot(reFlip, 0.5f);
    }
}

#1은 OpenCard()로 카드가 열릴 때, 클릭했을 때 동작하는 구문으로, 이 게임은 '2장의 카드를 뒤집어 짝을 맞추는 게임'이므로 클릭도 2번하기에 문제가 없다.
#2는 closeCard()의 실제 실행구문으로 #1로 펼친 카드들이 '동시에' 닫힌다.
이 말인 즉슨 각각의 카드 오브젝트에서 reFlip이 재생된다는 소리다.
처음에는 분리해보려 했으나, 두번밖에 재생되지 않아 소리가 커진다는 부작용밖에 존재하지 않아
단순하게 볼륨을 0.5f로 줄이는 방향으로 손을 봤다.

Gameend()

게임이 종료되었을 때(승리 or 패배)도 효과음을 넣고 싶었다. 앞선 내용처럼 단순하게 풀릴 것으로 기대했으나 반은 맞고 반은 틀렸더라.

public class GameManager : MonoBehaviour
{
    public static GameManager Instance;
    [SerializeField] Text timeTxt;
    [SerializeField] Text endTxt;
    [SerializeField] GameObject card;
    [SerializeField] AudioClip start;
    [SerializeField] AudioClip match;
    [SerializeField] AudioClip missmatch;
    [SerializeField] AudioClip win;
    [SerializeField] AudioClip lose;
    AudioSource audioSource;
    public AudioManager audioManager;
    GameObject firstCard = null;
    GameObject secondCard = null;
    GameObject cards;
    bool hasplayed = true; 
    float time;
    int cardsLeft;
    private void Awake()
    {
        Instance = this;
    }
    void Start()
    {
        cards = GameObject.Find("cards");
        audioSource = GetComponent<AudioSource>();
        audioSource.PlayOneShot(start, 0.3f);
            InitGame();
    }

>     void Update() #1
    {
        time += Time.deltaTime;
        timeTxt.text = time.ToString("N2");
        if (time >= 5.0f)
        {
            
            GameEnd();
        }
    }

    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)
        {
            audioSource.PlayOneShot(match,0.5f);

            firstCard.GetComponent<card>().destroyCard();
            secondCard.GetComponent<card>().destroyCard();
            
             if(cardsLeft == 2)
            {
                Time.timeScale = 0;
>                 audioSource.PlayOneShot(win, 0.5f);#2
                audioManager.gameObject.SetActive(false);#3
                endTxt.gameObject.SetActive(true);
            }
            cardsLeft -= 2;
        }
        else
        {
            audioSource.PlayOneShot(missmatch, 0.5f);
            firstCard.GetComponent<card>().closeCard();
            secondCard.GetComponent<card>().closeCard();
        }
        firstCard = secondCard = null;
    }
   
    public bool checkFirst()
    {
        if (firstCard != null) return true;
        else return false;
    }
  
    public void setFirst(GameObject obj)
    {
        firstCard = obj;
    }
   
    public bool checkSecond()
    {
        if (secondCard != null) return true;
        else return false;
    }
    public void setSecond(GameObject obj)
    {
        secondCard = obj;
    }
    void GameEnd()
    {
>         if (hasplayed) #1
            audioSource.PlayOneShot(lose, 0.25f);
         hasplayed = false; 
        Time.timeScale = 0f;        
>        audioManager.gameObject.SetActive(false);   #3          
        endTxt.gameObject.SetActive(true);

    }

    public void InitGame()
    {
        Time.timeScale = 1.0f;
        firstCard = secondCard = null;
       
        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 = 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);
        }
        cardsLeft = cards.transform.childCount;
    }    
}

지난 TIL 작성 때에도 이와 비슷한 문제를 만나 박살이 났다. 그 문제를 해결하기 위해 써봤던 방법 중 하나가 먹혀들어 풀어적는다.
#1 게임에서 이겼을 때는 따로 Time.timescale = 0.0f를 하는 등 졌을 때 시동하는 Gameend()와 다른 조건을 가진다. 이겼을 때는 '카드가 2장 남았을 때'라는 조건으로 '1번'만 작동하지만 졌을 때는 Update() 안의 GameEnd()가 작동한다. 이 말은 저 조건이 만족되는 한 프레임단위로 GameEnd()함수를 호출한다는 의미다. 그래서 Update의 조건으로 패배 시 soundclip을 재생시키면 수백번의 소리가 겹쳐나온다. 그래서 GameEnd()의 호출은 여러번 되더라도 bool을 사용하여 PlayOneShot()한 뒤 hasplayed = false시켜 한번의 재생만 할 수 있도록 만들었다. #2는 코드 한줄로 끝났는데 참 비교된다.

#3의 경우 게임이 종료될 때 종료됬다는 느낌도 주고, 결과에 따른 효과음이 더 잘 들리게 하기위해 배경음을 재생하는 AudioManager를 false해주었다.

정리

아침 9시부터 이것저것 듣고 팀원과 편해지기 위해 먼저 말도 걸고, 팀장님도 고생하시니까 나도 다른분 신경써야겠다 하면서 이리저리 하다보니 코딩은 별로 한게 없지만 시간은 빨리 지나갔다.

다시 생각해보니 Github가 다 잡아먹은거 같다. 그래도 재밌었다.

post-custom-banner

0개의 댓글