모바일플랫폼에서 게임오브젝트및 프리팹을 동적으로 생성하는 방식은 부하를 증가시킨다. 따라서 미리 만들어 놓고 필요할때 가져다 쓰는 방식을 오브젝트 풀링이라 한다.
동적으로 만들던 몬스터를 게임시작시 미리 만들어 놓고 비활성화해서 하나씩 불러다 쓰고 사망시 Destroy하지 않고 오브젝트풀에서 재사용할 수 있게 변경한다. 다음과 같이 GameManager.cs 를 수정한다.
몬스터 풀로 사용할 List<>와 최대생성수를 정의한다.
public List<GameObject> monsterPool = new List<GameObject>(); // 몬스트풀 리스트
public int maxMonsters = 10; //몬스터 최대 생성 수
CreateMonsterPool()을 호출 몬스터프리팹을이용 Instantiate()로 몬스터를 생성하고 이름을 Monster_00방식으로 변경하고 비활성화후 풀 List에 add해준다.
void CreateMonsterPool() { //몬스트풀 생성 함수
for(int i=0; i < maxMonsters; i++) {
var _monster = Instantiate<GameObject>(monster); //몬스터 생성
_monster.name = $"Monster_{i:00}"; //몬스터이름 생성
_monster.SetActive(false); //몬스터 비활성화
monsterPool.Add(_monster); //생성된 몬스트를 풀에 저장
}
}
풀생성후 InvokeRepeating()함수를 통해 몬스터가 주기적으로 자동생성된다.
void Start() {
CreateMonsterPool(); // 몬스터풀 생성 함수
InvokeRepeating("CreateMonster", 2.0f, createTime); //2초후 반복적으로 몬스터를 만든다
}
몬스터 생성은 Instantiate대신 풀에서 비활성환된 몬스터를 골라 리턴 GetMonsterInPool()을 호출
void CreateMonster() { //동적 몬스터 생성
int idx = Random.Range(0, points.Count);
//Instantiate(monster, points[idx].position, points[idx].rotation); //몬스트프리팹 동적 생성
GameObject _monster = GetMonsterInPool();
_monster?.transform.SetPositionAndRotation(points[idx].position, points[idx].rotation);
_monster?.SetActive(true);
}
풀을 순환하며 비활성환된 몬스터를 골라 리턴해주는 GetMonsterInPool()
public GameObject GetMonsterInPool() {
foreach(var _monster in monsterPool) {
if(_monster.activeSelf == false) {
return _monster; }
}
return null;
}
이제 플레이해보면 10개까지 잘 생성되는데 몬스터를 죽이면 다시 생성되지 않는다 왜냐하면 몬스터가 Destroy되지만 active한 상태가 되기 때문에 풀에서 비활성 몬스터가 없어지기 때문에 Destroy대신 active를 false로 해준다
몬스터의 Hp관리는 MonsterCtrl에서 한다. Start()함수를 Awake()로 이름을 바꾸고
void Awake()
{
monsterTr= GetComponent<Transform>();
playerTr = GameObject.FindWithTag("PLAYER").GetComponent<Transform>();
agent = GetComponent<NavMeshAgent>();
anim = GetComponent<Animator>();
bloodEffect = Resources.Load<GameObject>("GoopSpray"); //혈흔효과
}
StartCourotine()2개를 OnEnable함수로 옮겨 연결되지 않는 컴포넌트의 참조하는 오류를 막는다
private void OnEnable() { //스크립트가 활성활 될때 콜백되는 함수
PlayerCtrl.OnPlayerDie += this.OnPlayerDie; //교재는 PlayerCtrl.
StartCoroutine(CheckMonsterState()); //몬스트 상태를 체크하는 코루틴
StartCoroutine(MonsterAction()); //상태에 따라 행동을 수행하는 코루틴
}
MonsterAction()의 case State.DIE부분을 수정한다. 책 코드대로 실행해본 결과 죽은 몬스터가 부활할때 다시 죽는 경우가 발생했다. 따라서 state=State.IDLE을 추가해서 해결하였다.(DIE상태로 부활하면 다시 DIE상태로 무한 반복한다)
IEnumerator MonsterAction() {
while (!isDie) {
transform.LookAt(playerTr);
switch (state) {
//중략
case State.DIE: //사망상태
isDie= true;
agent.isStopped = true; //추적 정지
anim.SetTrigger(hashDie); // 애니메이트 hasDie 트리거발생
GetComponent<CapsuleCollider>().enabled = false; //충돌컴포넌트 비활성화
//일정시간 경과후 오브젝트풀링으로 환원
yield return new WaitForSeconds(0.3f); //일정시간경과후
hp = 10; //hp를 초기화하고
isDie= false; // 상태를 살리고
GetComponent<CapsuleCollider>().enabled = true; //콜라이더를 다시 true
this.gameObject.SetActive(false); //몬스터를 비활성화 한다.
state = State.IDLE; //Idel로 안하면 태어나자 마자 죽는다
break;
}
yield return new WaitForSeconds(0.3f);
}
}
'유니티게임강좌 > 게임매니저' 카테고리의 다른 글
[게임매니저] 싱글턴디자인 (0) | 2023.03.17 |
---|---|
[게임매니저] 적 캐릭터의 출현로직 (0) | 2023.03.16 |