: 클래스를 하나의 인스턴스로만 존재하도록 만드는 패턴
단순한 싱글톤
public class SimpleSingleton : MonoBehaviour
{
public static SimpleSignleton Instance {get; private set;}
-> static 기반의 Instance
void Awake()
{
Instance = this;
-> 필요할 때 Instance에 this 할당
}
}
-> 유일성이 보장된거는 아닌 단순한 static 기반의 싱글톤 생성
일반적인 싱글톤
public class BasicSingleton : MonoBehaviour
{
private static BasicSingleton instance;
public static BasicSingleton Instance
{
get => instance;
}
void Awake()
{
if(instance == null)
{
instance = this; -> 싱글턴 인스턴스 할당
DontDestoryOnLoad(gameObject); -> 씬 전환되도 파괴되지 않도록 설정
}
else
{
Destroy(gameObject); -> 중복 방지
}
}
}
오브젝트가 아예 없을경우의 싱글톤
public class BasicSingleton : MonoBehaviour
{
private static BasicSingleton instance;
public static BasicSingleton Instance
{
get
{
if(instance == null)
{
var obj FindFirstObjectByType<BasicSingleton>();
-> 씬 상에서 오브젝트 찾기
if (obj != null) -> 씬 상에서 오브젝트 찾음
instance = obj; -> 오브젝트 할당
else -> 씬 상에서 오브젝트를 찾지 못했으면 직접 만들어서 연결
{
var newObj = new GameObject("Basic Singleton");
-> 오브젝트 생성
newObj.AddComponent<BasicSingleton>();
->컴포넌트 생성
instance = newObj.GetComponent<BasicSingleton>();
-> 컴포넌트 할당
}
}
return instance
}
}
void Awake()
{
if(instance == null)
{
instance = this; -> 싱글턴 인스턴스 할당
DontDestoryOnLoad(gameObject); -> 씬 전환되도 파괴되지 않도록 설정
}
else
{
Destroy(gameObject); -> 중복 방지
}
}
}
: 싱글톤 제조기를 하나 만들어 두고, 다른 클래스들이 이 틀을 상속받기만 하면 자동으로 싱글톤이 되도록 만든 기법
Generic<T>를 활용한 Singleton
어떤 타입이던지 싱글톤을 생성 가능
public class SingletonCore<T> : MonoBehaviour where T : MonoBehaviour
{
private static T instance;
public static T Instance
{
get
{
if(instance == null)
{
instance == FindFirstObjectByType<T>();
if (instance == null)
{
var newObj = new GameObject(typeof(T).ToString());
instance = newObj.AddComponent<T>;
}
}
return instance
}
}
protected virtual void Awake() -> 이 싱글톤이 Awake문을 사용할 수 도 있으니 선택적으로 오버라이드할 수 있게 생성
{
if(instance == null)
{
instance = this as T; -> 싱글턴 인스턴스 할당
DontDestoryOnLoad(gameObject); -> 씬 전환되도 파괴되지 않도록 설정
}
else
{
Destroy(gameObject); -> 중복 방지
}
}
}
해당 클래스를 싱글톤화
public class BasicClass : SingletonCore<BasicClass> -> 싱글톤코어를 상속 <원하는타입>
{
public int level;
public void LevelUp()
{
level++;
}
//만약 Awake를 사용한다면 base.Awake()를 불러준다.
public void override void Awake()
{
base.Awake(); //부모의 싱글톤 설정 기능을 실행
level = 1;
}
}
: 동작을 캡슐화하여 런타임에 알고리즘을 쉽게 교체하는 패턴
1. 인터페이스 정의
2. 전략 구현
3. 컨텍스트(사용자) 설정
1.인터페이스 정의
이동 기능에 대한 인터페이스
public interface IMove
{
void Move(Transform transform);
}
2.전략구현
달리는 이동 기능
public class MoveRun : IMove
{
private float speed;
public MoveRun(float speed)
{
this.speed = speed;
}
public void Move(Transform transform)
{
}
}
날아서 이동 기능
public class MoveFly : IMove
{
private float speed;
public MoveRun(float speed)
{
this.speed = speed;
}
public void Move(Transform transform)
{
}
}
수영해서 이동 기능
public class MoveSwim : IMove
{
private float speed;
public MoveRun(float speed)
{
this.speed = speed;
}
public void Move(Transform transform)
{
}
}
이동하는 대상
public class Mover : MonoBehaviour
{
private IMove move;
void Start()
{
move = new MoveRun(3f);
}
void Update()
{
move.Move(transform); -> start에 transform 주입 시, 생성자 주입 방식이 된다.(너는 평생 이 Transform만 움직여)
if(Input.GetKeyDown(KeyCode.Shift)
{
move = new MoveRun(3f);
}
else if(Input.GetKeyDown(KeyCode.Space)
{
move = new MoveFly(7f);
}
else if(Input.GetKeyDown(KeyCode.Tap)
{
move = new MoveSwim(2f);
}
}
}
: 오브젝트를 미리 생성하여 Pool에 저장해두고, 재사용하는 패턴
public class StudyObjectPool : MonoBehaviour
{
public Queue<GameObject> objQueue = new Queue<GameObject>();
public GameObject objPrefab;
-> 풀을 채워 넣을 원본(ex 총알, 몬스터 등)
public Transform parent;
-> 생성된 오브젝트들을 깔끔하게 정리하기 위한 폴더 역할의 부모 트랜스폼
void Start()
{
CreateObject(50);
-> 시작하자마자 50개 미리 생성
}
private void CreateObject(int amount)
{
for (int i = 0 ; i < amount ; i++)
{
GameObject obj = Instantiate(objPrefab, parent);
-> 부모 밑에 오브젝트 생성
EnqueueObject(obj);
-> 생성 후 창고queue에 넣기
}
}
private void EnqueueObject(GameObject newObj) -> 재활용
{
Rigidbody rb = newObj.GetComponent<Rigidbody>();
rb.lineraVelocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
-> 물리적 힘 초기화
//총알이 발사되기 이전, 상호작용 된것을 초기화
newObj.SetActive(false);
-> 비활성화
obj.Queue.Enqueue(newObj);
-> 창고에 넣기
}
public GameObject DequeueObject() -> 총알을 발사할 때, DequeueObject 호출을 통해 풀에서 꺼내 쓴다.
{
if(objQueue.Count < 3) -> 자동확장 로직 //준비해둔 50개를 쓰고 3개밖에 안남았을때 불안하기 때문에 즉시 50개 생성
CreateObject(50);
GameObject obj = objQueue.Dequeue();
->창고에서 꺼내기
obj.SetActive(true);
-> 활성화
return obj; -> 배달
}
}
public class PoolController : MonoBehaviour
{
public StudyObjectPool pool;
public Transform shootPoint;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
GameObject bullet = pool.DequeueObject();
-> 풀에서 꺼낸다.(기존의 Instantiate 대체)
bullet.transform.position = shootPoint.position;
//총알 발사 기능
}
}
}
public class Bullet : MonoBehaviour
{
public StudyObjectPool pool;
void OnEnable()
{
Invoke("ReturnPool",10f);
-> 10초 후 해당 오브젝트를 queue에 저장
}
private void ReturnPool()
{
pool.EnqueueObject(gameObject);
-> 오브젝트를 queue에 저장
}
}
using UnityEngine.Pool;
public class UnityPoolManager : MonoBehaviour
{
public ObjectPool<GameObject> pool;
public GameObject prefab;
void Awake()
{
pool = new ObjectPool<GameObject>(CreateObject, GetObject, ReleaseObject);
}
private GameObject CreateObject()
{
GameObject obj = Instantiate(prefab);
return obj;
}
private void GetObject(GameObject obj)
{
obj.SetActive(true);
}
private void ReleaseObject(GameObject obj)
{
obj.SetActive(false);
}
}
public class PoolController : MonoBehaviour
{
public UnityPoolManager poolManager;
public Transform shootPoint;
void Update()
{
if(Input.GetMouseButtonDown(0))
{
GameObject bullet = poolManager.pool.Get(); -> pool.Get()을 통해 GetObject()실행, 재고가 없을시 Create하고 Getobject 실행
bullet.transform.position = shootPoint.position;
}
}
}
-> 유니티 풀 내부 함수 pool.Get() 이해하기 쉽게 하기 위한 함수 예시
public GameObject Get()
{
GameObject item;
// 1. 재고 확인
if (재고가_없으면)
{
// 이때만 CreateObject가 호출
item = CreateObject();
}
else
{
// 재고가 있으면 그냥 꺼냅니다.
item = 창고.꺼내기();
}
// 2. 나가기 전에 활성화 (이건 항상 실행됨)
GetObject(item);
return item;
}
public class Bullet : MonoBehaviour
{
public UnityPoolManager poolManager;
void OnEnable()
{
Invoke("ReturnPool", 10f);
}
private void ReturnPool()
{
poolManager.pool.Release(gameObject);
}
}
: 객체 생성을 별도의 클래스(팩토리)에게 위임하고 객체 생성 로직을 캡슐화하여 유연성을 높이는 패턴
-> 추상 클래스 : 몬스터 뼈대, 몬스터가 가져야 할 기능들
public abstract class MonsterCore
{
public abstract string Name {get;}
public abstract void Attack();
}
public class Slime : MonsterCore
{
public override string Name => "슬라임";
public override void Attack() => Debug.Log("슬라임공격");
}
public class Orc : MonsterCore
{
public override string Name => "오크";
public override void Attack() => Debug.Log("오크공격");
}
public class Dragon : MonsterCore
{
public override string Name => "드래곤";
public override void Attack() => Debug.Log("드래곤 공격");
}
public abstract class MonsterFactory : MonoBehaviour -> 최상위 팩토리
{
public MonsterCore Spawn(string type)
{
MonsterCore monster = CreateMonster(type); -> 추상함수랑 연결되어 있음(구체적인 생성은 자식에게 미룸)
Debug.Log($"{monster.Name}생성");
return monster;
}
protected abstract MonsterCore CreateMonster(string type);
-> 타입에 따라 어떻게 만들지 자식에서 정해라
}
public class ForestSpawner : MonsterFactory
{
protected override MonsterCore CreateMonster(string type) -> 오버라이드 재정의
{
MonsterCore monster = null;
switch(type)
{
case : "Slime"
monster = new Slime();
break;
case : "Orc"
monster = new Orc();
break;
}
return monster; -> 만든 몬스터 반환
}
IEnumerator Start() -> 1초마다 랜덤하게 몬스터 생성
{
while(true)
{
int ranIndex = Random.Range(0,2);
string monsterType = ranIndex == 0 ? "Slime" : "Orc";
Spawn(monsterType);
yield return new WaitForSeconds(1f);
}
}
}
: 명령을 객체로 만들어 캡슐화 하는 것