Singleton 시연 영상(Scene전환을 통한 Singleton 확인)
(코드에 너무 집착하지말고 개념을 확실히 하는것이 좋음. 물론 코드도 알면 좋다)
public class SingletonTestBuf : MonoBehaviour
{
private static SingletonTestBuf _instance;
public static SingletonTestBuf Instance //프로퍼티로 싱글톤 객체를 호출했을때 null체크
{
get
{
if(_instance == null)
{
_instance = FindObjectOfType<SingletonTestBuf>();
if(_instance == null)
{
_instance = new SingletonTestBuf();
}
}
return _instance;
}
}
private void Awake() //남들한테 호출당해서 하는게 아닌 자기자신이 깨어났을경우 null처리
{
if(_instance != null && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
DontDestroyOnLoad(_instance); //다른씬에서도 사용할 수 있어야하면 추가
}
}
싱글톤과 제너릭을 함께 사용하여 매번 싱글톤으로 생성할 클래스를 정의할 필요가 없다. (싱글톤 클래스마다 static 인스턴스 만들고.. Awake에 null처리 하는 과정 제네릭으로 편하게 통합)
싱글톤과 제네릭의 존재이유가 충돌
싱글톤 -> 하나만 존재
제네릭 -> 여러 타입으로 여러개 만듬
정체성이 흔들림(타입별로 하나씩 존재하는 싱글톤?) -> 전역 유일성이 떨어짐
1.Singleton.cs
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T _instance;
//싱글톤 구조를 가져가지만 다음씬에서 제거되어야 할때 사용 *씬별로 존재하는 UI매니저 등)
protected bool isDestroyOnLoad = true;
//MVP에서 싱글톤 매니저의 이벤트를 해제(Dispose)해야되는데 이때 Null체크가 필요해서 추가
//isHaveInstance를 안쓰고 if(Manager.Instance != null) 이렇게 하면 인스턴스에 접근해서 새로 생겨버림
public static bool isHaveInstance => _instance != null;
//Instance 프로퍼티와 Awake에서 둘다 null체크를 하는 이유:
//외부에서 Instance로 호출하는경우 생성되고
//이미 씬에서 생성되어있을경우 프로퍼티로 호출되지는않고 Awake에서 최초 호출되므로
//외부 호출용 프로퍼티, 해당 타입의 싱글톤이 없으면 찾아보고 그래도 없으면 새로 생성 후 결정
public static T Instance
{
get
{
if (_instance == null)
{
_instance = FindObjectOfType<T>(); //찾아봄
if (_instance == null)
{
GameObject singletonObj = new GameObject();
_instance = singletonObj.AddComponent<T>();
singletonObj.name = typeof(T).ToString();
}
}
return _instance;
}
}
protected virtual void Awake() //중복체크 및 연결기능 구현 (재정의 가능)
{
if (_instance != null && _instance != this)
{
Destroy(gameObject);
return; //이게 없으면 자식의 Awake가 끝까지 실행됨
}
else
{
_instance = this as T;
if (isDestroyOnLoad)
{
DontDestroyOnLoad(gameObject);
}
}
OnAwake(); //이거 추가해보자 . 구조 개선 필요할듯
}
개선 예약 -> Awake에 virtual 지우고, protected virtual void OnAwake() 추가
그다음에 자식에서 OnAwake를 정의하면 된다
}
2.GameManager.cs
public class GameManager : Singleton<GameManager>
{
private bool _isPlaying = false;
public bool IsPlaying { get { return _isPlaying; } }
public event Action OnGameStartAction;
[field: SerializeField] public UnityEvent OnGameEndAction { get; private set; } = new UnityEvent();
//현재 게임이 진행중인지 여부를 전환하는 함수
public void ChangeGameState()
{
_isPlaying = !_isPlaying;
if (_isPlaying) OnGameStartAction?.Invoke();
}
protected override void Awake()
{
//현재 클래스에서 Awake를 써야할경우. 사용가능
//isDestroyOnLoad = false; //다음씬에서 제거되어야 하는경우 추가
//부모의 Awake 호출
base.Awake();
//이미 있는 싱글톤일때 base.Awake에서 Destroy하고 return하는데 return이라는건 현재 메소드를 빠져나가는거라서 자식에서의 override Awake는 빠져나가지않음.
//그래서 여기서도 한번 더 싱글톤 중복 체크해야함, 이걸 안하면 중복 싱글톤이 아래 코드를 실행한다...
if (Instance != this) return;
//다음 원하는 코드 작성
}
}
싱글톤처럼 현재 씬에서 1개만 유지되고, 전역적으로 호출이 되지만, 다음 씬에서 삭제되도록 하고싶다면? 아래 코드처럼 static으로 선언하면 된다
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
}
private void OnDestroy()
{
if (Instance == this)
Instance = null;
}
}
중재자 패턴 : 중재자 패턴이라고 내 코드에는 이미 적용되어있는데 객체끼리 직접 접근해서 결합도가 높아지는게 아닌 그냥 하나의 중재자 클래스에서 기능을 수행한다고 보면됨
싱글톤에서