🐧 들어가기 앞서

오늘은 미니 프로젝트 제출날이다.

스테이지 잠금 기능에 버튼이 보이지 않게 구현했고,

오디오 기능을 추가했다.

도중에 메모리 관리도 하는 코드를 넣었다.

규승님이 고생 많이 하셨다.

감사하다.


🐧 오늘 배운 것

  1. Audio 관리

  2. 스테이지 버튼 비활성화

  3. Update 메모리 관리

  4. Scriptable Objects 공부


🐧 기억할 것 & 진행

1. Audio 관리

public AudioClip ~~ 

오디오 데이터를 저장하는 Unity 엔진의 리소스 타입이다.

public AudioSource ~~

소리를 재생하기 위해 사용하는 컴포넌트이다.

AudioSource를 설정하면

public AudioSource mainAudioSource;

public void Start(){
	mainAudioSource.clip(audioClip 변수명);
    mainAudioSource.loop = true; // true면 오디오가 루프를 돈다
    mainAudioSource.Play() // 플레이!
}

2. 스테이지 관리

using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class StageSelectBtn : MonoBehaviour
{
    public Button[] stageButtons; // 스테이지 버튼 배열
    int currentClearedStage = Global.Instance.CurrentStage;

    void Start()
    {
        // 설정하실 때, 각 StageSelectBtn1~3까지 Script 넣어주시고
        // StageSelectBtn(Script)에서 Stage Button 3, Element0~2까지 각 버튼 다 넣어주시면됩니다!
        // 각 스테이지 버튼의 상태 설정
        for (int i = 0; i < stageButtons.Length; i++)
        {
            // i + 1이 클리어한 스테이지보다 작거나 같으면 잠금 해제
            stageButtons[i].interactable = i + 1 <= currentClearedStage + 1;
        }

        // 마지막 스테이지 이후의 버튼 비활성화
        for (int i = currentClearedStage + 1; i < stageButtons.Length; i++)
        {
            stageButtons[i].interactable = false;
        }
    }

    public void SetStage(int stage)
    {
        // 현재 클리어한 스테이지 다음 스테이지를 선택한 경우
        if (currentClearedStage + 1 >= stage)
        {
            Global.Instance.CurrentStage = stage;
            SceneManager.LoadScene("MainScene");
        }
    }
}

PlayerPrefs를 사용하지 않고 구현했다.

PlayerPrefs를 사용하면 코드가 더 간결하고, 데이터 접근이 쉽지만

보안에 취약하다.

그러나

나처럼 전역변수를 받아 클래스로 제작하면 유지보수가 더 필요하고

초기 설정이 많이 생긴다.

3. Update() 관리

 private bool _isGameOver = false;
 
 private void Update()
    {

        if (isStageReady)
        {

            _time -= Time.deltaTime;
            if (_time <= 0 && !_isGameOver)
            {
                _time = 0;
                Time.timeScale = 0.0f;

                // 게임 오버
                gmAudiosource.PlayOneShot(failStageClip); // 실패 스테이지 소리
                _gameFailureUI.SetActive(true);
                var resultTextUpdate = _gameFailureUI.GetComponent<ResultTextUpdate>();
                Debug.Assert(resultTextUpdate);
                resultTextUpdate.UpdateText();
                _isGameOver = true;

            }

            if (_time <= currentStageHurryUpTime)
            {
                _timeTextAnimator.SetBool("On", true);
            }

        }
        
        _timeText.text      = $"{_time:N2}";
        _countText.text     = $"Count : {_tryCount}";

    }

GameManager.cs의 Update다. 지금은 오류가 없지만,

이전 코드에서는

if(_time<=0)
~~~

로 실행되었기 때문에 타임이 0 아래로 내려가는 순간 무한 업데이트가 진행된다.

따라서 isGameOver를 부여해 게임이 실패했다고 판정되면, 한번 만 업데이트 되도록 수정했다.

4. Scriptable Objects

규승님이 구현하신 Scriptable objects를 공부했다.

우리 조에서 가장 효과적으로 쓰인 기능인데,

카드 게임을 제작하다보면,

우리가 관리해야할 오브젝트가 적다면 괜찮지만

만약 대량의 오브젝트가 필요한 게임이라면 그 데이터를 게임매니저에서 다 관리하기 너무 어렵다.

그래서 유니티는 대량의 데이터를 저장 할 수 있게 데이터 컨테이너를 제공해준다.

스크립터블을 작성해서 데이터를 모아두면,

메모리는 줄이면서 기능을 더 간편하게 제작할 수 있게 됐다.


🐧 게임에 구현한다면?

1. Audio 관리

꼭 오브젝트에 모든 기능을 부여해주자! 또한 AudioSource 명은 전부 다른게 좋다.

https://freesound.org/browse/
https://opengameart.org/

에셋이 굉장히 많다 꼭 써보자!

2. 스테이지 관리

해당 스테이지를 클리어하지 못하면, 버튼이 interactable = false 상태다.

해당 스테이지를 클리어하면 버튼이 열린다!

3. Update()관리

게임에서 오디오를 재생하는데, Update부분에 실패했을 경우 FaileUI 가 출력된다.

그런데 오디오가 엄청 겹쳐서 들렸다.

즉, Update()가 엄청 많이 실행되어서 오디오가 겹치는 것이다.

isGameOver를 부여해 오디오가 단 한번 재생되었다.

버그도 잡고, 오디오도 넣고 일석이조!

4. Scriptable Objets

  • 쓰기 전
 public void CreateCard(int stageLevel)
    {
        if (_cardParentTransform.childCount != 0)
        {
            for (int i = 0; i < _cardParentTransform.childCount; i++)
                Destroy(_cardParentTransform.GetChild(i).gameObject);
        }

        int[] randData = { };
        switch (stageLevel)
        {
            case 1:
                _maxCardCount = 12;
                int[] randData_1 = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 };
                randData = randData_1;
                break;

            case 2:
                _maxCardCount = 16;
                int[] randData_2 = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 };
                randData = randData_2;
                break;

            case 3:
                _maxCardCount = 20;
                int[] randData_3 = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8 };
                randData = randData_3;
                break;
        }
        randData = randData.OrderBy(items => Random.Range(-1.0f, 1.0f)).ToArray();
        for (int i = 0; i < _maxCardCount; i++)
        {
            GameObject newCard = Instantiate(_cardPrefab);
            newCard.transform.SetParent(_cardParentTransform);
            newCard.transform.localScale = new Vector3(1, 1, 1);
        }
    }
  • 쓰고난 후
private void CreateCard(int stageLevel)
    {
        if (_cardParentTransform.childCount != 0)
        {
            for (int i = 0; i < _cardParentTransform.childCount; i++)
                Destroy(_cardParentTransform.GetChild(i).gameObject);
        }

        Debug.Assert(cardIndexNumber <= _cardScriptable.array.Length);

        int[] randData = new int[maxCurrentStageCardNumber];
        for(int i =0; i < cardIndexNumber; ++i)
        {
            randData[i * 2 + 0] = i;
            randData[i * 2 + 1] = i;
        }
        randData = randData.OrderBy(items => Random.Range(-1.0f, 1.0f)).ToArray();

        for (int i = 0; i < maxCurrentStageCardNumber; i++)
        {

            GameObject newCard = Instantiate(_cardPrefab);
            newCard.transform.SetParent(_cardParentTransform);
            newCard.transform.localScale = new Vector3(1, 1, 1);

            Card cardScript = newCard.GetComponent<Card>();
            Debug.Assert(cardScript != null);
            cardScript.cardIndex = randData[i];

        }

    }

여기서 카드를 모두 관리한다! 스테이지도! 매우 간편하다.


🐧 내일 할 일

내일은 마지막 발표다 PPT랑 대본은 준비됐는데, 발표할 때 떨지만 말자!

자신있게 잘 하자 화이팅!

규승님이 정말 고생하셨다.

나도 훗날 코드를 잘 짜는 사람이 되어야겠다.

0개의 댓글