- 장애물 오브젝트가 리스트에 5개 들어있고 모두 활성화 되어있을 경우, 추가적인 장애물을 생성되게 하기
- 사운드 매니저로 사운드 관리
- 게임 매니저로 게임 전체 관리
리스트에 들어있는 장애물 오브젝트가 다 활성화 상태라면, 새로운 장애물 추가
-> 리스트에 추가하여 크기 증가 (capacity x )
모든 리스트 요소들이 다 활성화 되었는지 확인하는 함수가 필요하다
ObstacleManager check() 함수리스트 크기만큼 반복하여 들어있는 요소들 중 하나라도 비활성화 상태라면 false를 반환한다
모든 요소를 다 순회하였으면 true를 반환한다

2.5초마다 실행되는 코루틴 함수에 코드 작성
Check() == true 즉, 모든 리스트의 요소들이 활성화 된 상태라면 게임 오브젝트 비활성화 상태로 생성 & 리스트에 추가

Obstacle Manager 전체코드using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.VisualScripting;
using UnityEngine;
public class ObstacleManager : MonoBehaviour
{
[SerializeField] List <GameObject> obstacles;
[SerializeField] List<string> obstacleNames;
[SerializeField] int createCount = 5;
[SerializeField] int random;
void Start()
{
obstacles.Capacity = 10;
Create();
StartCoroutine(ActiveObstacle());
}
public void Create()
{
for(int i = 0; i < createCount; i++)
{
GameObject prefab =
Instantiate(Resources.Load<GameObject>(obstacleNames[Random.Range(0, obstacleNames.Count)]), gameObject.transform);
prefab.SetActive(false);
obstacles.Add(prefab);
}
}
// 하나라도 비활성화가 있다면 false 반환
public bool Check()
{
for(int i = 0; i < obstacles.Count; i++)
{
if (obstacles[i].activeSelf == false)
{
return false;
}
}
return true;
}
IEnumerator ActiveObstacle()
{
while(GameManager.Instance.check)
{
yield return new WaitForSeconds(2.5f);
random = Random.Range(0, obstacles.Count);
// 현재 게임 오브젝트가 활성화되어 있는 지 확인합니다.
while (obstacles[random].activeSelf == true)
{
// 현재 리스트에 있는 모든 게임 오브젝트가 활성화되어 있는 지 확인합니다.
if(Check() == true)
{
// 모든 게임 오브젝트가 활성화되어 있다면 게임 오브젝트를 새로 생성한 다음
// obstacles 리스트에 넣어줍니다.
GameObject clone =
Instantiate(Resources.Load<GameObject>(obstacleNames[Random.Range(0, obstacleNames.Count)]), gameObject.transform);
clone.SetActive(false);
obstacles.Add(clone);
}
// 현재 인덱스에 있는 게임 오브젝트가 활성화되어 있으면
// random 변수의 값을 +1을 해서 다시 검색합니다.
random = (random + 1) % obstacles.Count;
}
obstacles[random].SetActive(true);
}
}
}
게임 오브젝트마다 각자의 사운드들을 가질것이다 (사운드 클립)
이 '사운드 재생'을 사운드 매니저에서 담당한다
요리사와 손님 3명이 있다.
각 손님들은 본인의 재료를 들고있다.
요리사는 손님의 재료를 담을 바구니를 가지고 있다.
손님은 바구니에 재료를 담는다. 그러면 요리사가 직접 그 재료로 요리를 해준다
각각의 게임 오브젝트들은 본인의 오디오 클립을 가지고 있다.
게임 오브젝트에서 사운드 매니저가 가진 함수를 호출한다. (바구니)
매개변수로 본인의 오디오 클립을 넣는다.
사운드 매니저의 함수를 실행한다.

AudioManager게임 오브젝트마다 소리를 가지는 경우가 많기 때문에, Listener 함수의 호출도 잦다
-> 싱글톤을 상속받아 사용한다
매개 변수로 받아온 audioClip을 오디오 소스 컴포넌트에서 재생한다

Sound 정의하기Monobehaviour를 상속받지 않는 Sound 클래스를 생성한다
-> 게임 오브젝트에 부착할 수 없다(인스펙터 창에 표시x)
[System.Serializable]로 직렬화 해준다
-> 다른 클래스의 변수로 들어갔을 때 인스펙터 창에 표시된다 (public)
-> 직렬화 해야만 다른 클래스에서 접근이 가능

버튼 오브젝트에 부착된 Select 스크립트에서 Sound 스크립트 가져오기
Sound 타입의 객체를 참조하는 참조변수 sound

유니티 버튼 오브젝트의 인스펙터창에 직렬화된 Sound 클래스를 확인할 수 있다


갑자기 궁금해져서 찾아보았다
static class 내부의 함수들을 다른 클래스에서 사용할 수 있을까?
우리가 씬 전환에서 사용했던 클래스 SceneManager는 클래스 자체가 static이 아니어도 내부 함수들을 사용할 수 있었다
SceneManager나 Resources 클래스들 같은 경우 정적 클래스는 아니지만,
구성된 메서드들과 속성이 static이면 인스턴스 생성없이 바로 메서드를 사용할 수 있다
ex) 씬 매니저에서 사용한 비동기 씬 로드의 LoadSceneAsync메서드
SceneManager 자체는 정적클래스가 아니지만 LoadSceneAsync 함수는 정적 메서드이다
-> 따라서 인스턴스 생성없이 바로 사용가능

게임 매니저는 게임의 총괄을 담당한다
현재 코드에서는 게임 매니저에 정의된 bool 변수를 사용하여 스위치 작동 ON / OFF를 제어하는 느낌이다
GameManager는 여러 스크립트에서 가져다가 사용하는 경우가 잦기 때문에 싱글톤을 상속받는다
private로 속성값을 정의하고, public으로 해당 속성값을 읽기전용(get)으로 반환할 수 있게 프로퍼티를 생성한다
Excute : state = true면 게임이 실행된다
Finish : state = false가 되는 조건인 곳에서 호출하고, false가 되면 스테이지가 종료되기 때문에, 관련된 스크립트에서 다 종료된다


처음에 짰던 코드에서는 게임매니저의 속성값을 set으로 다른 스크립트에서 변경할 수 있게 했다
그런 뒤, 게임 매니저에서 해당 속성값의 여부에 따라 시행하는 함수를 짰는데, 이러면 참조가 너무 많이 들어간다
위 코드에서는 싱글톤 게임 매니저를 다른 스크립트에서 조건을 작성하고, 함수도 스위치 역할로만 호출된다
하지만 아래의 예시를 보자

참조가 많아지기 때문에, 구조를 잘 짜야할 거 같다
GameManager 스크립트using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class GameManager : Singleton<GameManager>
{
private bool state;
public bool State
{
get { return state; }
//set { check = value; }
}
public void Execute()
{
state = true;
}
public void Finish()
{
state = false;
MouseManager.Instance.State(0);
}
}