오늘은 미니 프로젝트 제출날이다.
스테이지 잠금 기능에 버튼이 보이지 않게 구현했고,
오디오 기능을 추가했다.
도중에 메모리 관리도 하는 코드를 넣었다.
규승님이 고생 많이 하셨다.
감사하다.
Audio 관리
스테이지 버튼 비활성화
Update 메모리 관리
Scriptable Objects 공부
public AudioClip ~~
오디오 데이터를 저장하는 Unity 엔진의 리소스 타입이다.
public AudioSource ~~
소리를 재생하기 위해 사용하는 컴포넌트이다.
AudioSource를 설정하면
public AudioSource mainAudioSource;
public void Start(){
mainAudioSource.clip(audioClip 변수명);
mainAudioSource.loop = true; // true면 오디오가 루프를 돈다
mainAudioSource.Play() // 플레이!
}
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를 사용하면 코드가 더 간결하고, 데이터 접근이 쉽지만
보안에 취약하다.
그러나
나처럼 전역변수를 받아 클래스로 제작하면 유지보수가 더 필요하고
초기 설정이 많이 생긴다.
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를 부여해 게임이 실패했다고 판정되면, 한번 만 업데이트 되도록 수정했다.
규승님이 구현하신 Scriptable objects를 공부했다.
우리 조에서 가장 효과적으로 쓰인 기능인데,
카드 게임을 제작하다보면,
우리가 관리해야할 오브젝트가 적다면 괜찮지만
만약 대량의 오브젝트가 필요한 게임이라면 그 데이터를 게임매니저에서 다 관리하기 너무 어렵다.
그래서 유니티는 대량의 데이터를 저장 할 수 있게 데이터 컨테이너를 제공해준다.
스크립터블을 작성해서 데이터를 모아두면,
메모리는 줄이면서 기능을 더 간편하게 제작할 수 있게 됐다.
꼭 오브젝트에 모든 기능을 부여해주자! 또한 AudioSource 명은 전부 다른게 좋다.
에셋이 굉장히 많다 꼭 써보자!
해당 스테이지를 클리어하지 못하면, 버튼이 interactable = false 상태다.
해당 스테이지를 클리어하면 버튼이 열린다!
게임에서 오디오를 재생하는데, Update부분에 실패했을 경우 FaileUI 가 출력된다.
그런데 오디오가 엄청 겹쳐서 들렸다.
즉, Update()가 엄청 많이 실행되어서 오디오가 겹치는 것이다.
isGameOver를 부여해 오디오가 단 한번 재생되었다.
버그도 잡고, 오디오도 넣고 일석이조!
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랑 대본은 준비됐는데, 발표할 때 떨지만 말자!
자신있게 잘 하자 화이팅!
규승님이 정말 고생하셨다.
나도 훗날 코드를 잘 짜는 사람이 되어야겠다.