오늘 배운것
디자인패턴
싱글턴이라는건 하나의 인스턴스가 유지되어야 한다.
static SpawnManager instance;
static public SpawnManager Instance
{
get
{
return instance;
}
}
private void Awake()
{
instance = this; // 이런식으로 처리를 하면 싱글턴이 아니다.싱글턴이라는건 하나의 인스턴스가 유지되어야 한다.
DontDestroyOnLoad(gameObject); // Scene이 바뀌어도 Scene이 유지가 된다. //씬이 생성될떄마다 게임 오브젝트가 새로 생기고 하면 안됌. 오직 하나만 유지되어야 한다.
}

그림처럼 한개가 생겨야 싱글턴인데, NewScene을 계속 누르니 SpawnManger가 계속 생기는 경우, 이건 싱글턴이라고 할 수 없다.
그래서 아래 코드처럼 수정해야 한다.static SpawnManager instance; static public SpawnManager Instance { get { return instance; } } private void Awake() { if(instance!=null) { Destroy(gameObject); } else { // 이렇게 해줘야 새로 생기는 SpawnManger 오브젝트를 삭제 함으로서, 싱글턴으로 만든다. instance = this; DontDestroyOnLoad(gameObject); } }
Awake(), OnEnable(), Instance순서
public class EventBus : MonoBehaviour { // EvnetBus는 굳이 싱글턴일 필요가 없음 // 한 GameObject내에서의 통신을 위해 GameObject의 컴포넌트로 사용할 수도 있다. // 여기서 싱글턴으로 만든 이유는 그냥 싱글턴의 다양한 구현 방법을 보여주기 위해 작성했다. static EventBus instance; public static EventBus Instance { get { if(instance == null) { GameObject go = new GameObject("EventBus"); instance = go.AddComponent<EventBus>(); // 이 문장에서 Awake,OnEnable실행하고, 인스턴스를 할당한것, Debug.Log("Instance"); DontDestroyOnLoad(go); } return instance; } } // Awake는 생성자와 같은 시간? / 어웨이크와OnEnable과 연달아서 실행됨. // 스타트는 생성될때 보다 한참 뒤, // 생성자 타입에 뭘 하려고 한다, 그러면 Awake()에서 하면됨. private void Awake() { Debug.Log("Awake"); } private void OnEnable() { Debug.Log("OnEnable"); } public void EmptyMethod() { } }

실행 순서를 보면 Awake, OnEnable이 연달아 실행되고, Instance가 실행된다.
장점
- 유지보수 하기 편해지고,
- 오브젝트 풀링과 함께 작업할 때, 효율적으로 초기화 하고 재사용 가능
- 새로운 객체가 필요하면, 팩토리 클래스에만 생성로직을 추가하면된다. 그래서 수정사항이 적어진다.
using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine; using UnityEngine.Pool; public class FactoryBarbarianA : UnitFactory { public GameObject productPrefab; //public override IUnitProduct GetProduct(Vector3 position) //{ // GameObject gameobject = Instantiate(productPrefab, position, Quaternion.identity); // ProductBaBarianA newProduct = gameobject.GetComponent<ProductBaBarianA>(); // newProduct.Initialize(); // return newProduct; //} ObjectPool<ProductBarbarianA> pool; private void Awake() { pool = new ObjectPool<ProductBarbarianA>( createFunc: CreateNewProduct, actionOnGet: product => product.gameObject.SetActive(true), actionOnRelease: product => product.gameObject.SetActive(false), actionOnDestroy: product => Destroy(product.gameObject), collectionCheck: false, defaultCapacity: 10 ); // Unity의 오브젝트풀은 미리 생성은 안해준다. // 만약 미리 생성까지 미리 해두고 싶다면?? List<ProductBarbarianA> tempList = new List<ProductBarbarianA>(); for (int i = 0; i < 10; i++) { tempList.Add(pool.Get()); } for (int i = 0; i < 10; i++) { pool.Release(tempList[i]); } } public override IUnitProduct GetProduct(Vector3 position) { ProductBarbarianA product = pool.Get(); product.transform.position = position; product.Initialize(); return product; } ProductBarbarianA CreateNewProduct() { GameObject go = Instantiate(productPrefab); return go.GetComponent<ProductBarbarianA>(); } public override void ReturnProduct(IUnitProduct product) { if (product is ProductBarbarianA) { pool.Release(product as ProductBarbarianA); } } }
위에 스크립트를 사용해서 Pool에 생성된 Clone들을 아래의 스크립트로 반환 시킨다.// UnitFactory.cs // 반환 using UnityEngine;
public abstract class UnitFactory : MonoBehaviour
{
public abstract IUnitProduct GetProduct(Vector3 position);
// 생산 요청에 대한 대응 - Factory Pattern
public abstract void ReturnProduct(IUnitProduct product);
}
>
```cs
// FactoryBarbarianA.cs
public override void ReturnProduct(IUnitProduct product)
{
if(product is ProductBarbarianA)
{
pool.Release(product as ProductBarbarianA);
}
}
public void Spawn() { Vector3 viewportPoint = new Vector3(Random.Range(0f, 1f), Random.Range(0f, 0.5f), 0); // viewport는 화면을 x와 y축으로 나타냈을 때 0,0부터 1,1로 나타낼 수 있다. Vector3 hitpoint = GetTerrainHitPoint(viewportPoint); //Instantiate(SpawnPrefab, hitpoint, Quaternion.Euler(0, Random.Range(0, 360), 0)); // 랜덤하게 y축을 기준으로 회전시킨 오브젝트 생성 UnitFactory factory = unitFactories[0]; IUnitProduct product = factory.GetProduct(hitpoint); //10초후 반환 StartCoroutine(ReturnAfterDelay(factory, product, 10)); } IEnumerator ReturnAfterDelay(UnitFactory factory, IUnitProduct product, float delay) { yield return new WaitForSeconds(delay); if (factory is FactoryBarbarianA factoryA) { factoryA.ReturnProduct(product); } }