오늘은 오브젝트 풀을 활용한 수류탄 구현 과정에서 발생한 문제를 해결했다.
처음에는 잘 작동하는 것처럼 보였지만, 특정 상황에서 폭발 이펙트가 재생되지 않고, 수류탄이 사라지지 않는 문제가 발생했다.
오브젝트 풀에서 수류탄을 가져와 던지면, 첫 번째 던질 때는 정상적으로 폭발했다.
그런데 두 번째 이후로 던진 수류탄은 이펙트가 발동하지 않고, 사라지지도 않고, 그냥 굴러가기만 했다.
OnEnable()에서 Explosion()을 실행한 문제
기존 코드에서는 OnEnable()에서 StartCoroutine(Explosion())을 호출했다.
오브젝트 풀을 사용할 때는 SetActive(true)를 통해 오브젝트가 활성화되는데,
이때마다 Explosion()이 실행되기 때문에, 재사용된 수류탄에서 제대로 폭발이 일어나지 않았다.
이전 폭발의 Coroutine이 끝나지 않은 상태에서 새로운 Coroutine이 실행되지 않아서,
폭발 타이머가 동작하지 않았던 것.
폭발 전 effectObj를 초기화하지 않은 문제
수류탄이 폭발할 때 이펙트 오브젝트(effectObj)를 SetActive(true)로 변경했지만,
새로운 수류탄이 생성될 때 false로 초기화하지 않았다.
그 결과, 한 번 폭발한 이후로는 다시 폭발 이펙트가 보이지 않게 된 것.
OnEnable()에서 자동으로 Explosion()을 실행하지 않도록 변경
대신 수류탄을 던질 때 Throw() 메서드를 호출하도록 수정
Throw() 내부에서 StartCoroutine(Explosion())을 실행
ResetGrenade() 메서드를 추가하여 상태 초기화
meshObj.SetActive(true);
effectObj.SetActive(false); → 이펙트는 초기 상태에서 꺼두기
rb.velocity = Vector3.zero; → 물리 상태도 초기화
GrenadeState에서 Throw()를 직접 호출하도록 수정
GrenadeState에서 오브젝트 풀에서 가져온 후 Throw() 실행
이제 수류탄을 던질 때마다 정상적으로 폭발 이펙트가 재생되고, 사라지는 동작도 올바르게 수행된다.
오브젝트 풀을 사용할 때는 오브젝트가 재사용될 때 초기화하는 과정이 중요하다는 걸 다시 한 번 깨달았다. 🚀
수류탄
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Grenade : MonoBehaviour
{
public GameObject meshObj;
public GameObject effectObj;
public Rigidbody rb;
public void Throw()
{
ResetGrenade(); // 초기화
StartCoroutine(Explosion());
}
IEnumerator Explosion()
{
yield return new WaitForSeconds(3f);
rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
meshObj.SetActive(false);
effectObj.SetActive(true);
RaycastHit[] rayHits = Physics.SphereCastAll(transform.position, 15, Vector3.up,
0, LayerMask.GetMask("Enemy"));
foreach (RaycastHit hitObj in rayHits)
{
hitObj.transform.GetComponent<Enemy>().HitByGrenade(transform.position);
}
StartCoroutine(DisableCoroutine(gameObject, 2));
}
private IEnumerator DisableCoroutine(GameObject obj, float delay)
{
yield return new WaitForSeconds(delay);
obj.SetActive(false);
}
public void ResetGrenade()
{
meshObj.SetActive(true);
effectObj.SetActive(false);
rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
}
}
수류탄 던지는상태패턴 구현
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GrenadeState : Istate
{
private StateMachine stateMachine;
private Animator animator;
private Player player;
private Weapon weapon;
public GrenadeState(StateMachine machine, Animator animator, Player player, Weapon weapon)
{
stateMachine = machine;
this.animator = animator;
this.player = player;
this.weapon = weapon;
}
public void Enter()
{
animator.Play("Throw");
GameObject poolGrenade = GameManager.Instance.objectpool.Get(3);
Grenade grenadeScript = poolGrenade.GetComponent<Grenade>();
grenadeScript.ResetGrenade(); // 초기화
grenadeScript.Throw();
// 플레이어 앞쪽에 소환되도록 위치 설정
Vector3 spawnPosition = player.transform.position + player.transform.forward * 1.5f + Vector3.up * 1.0f;
poolGrenade.transform.position = spawnPosition;
Rigidbody rigidGrenade = poolGrenade.GetComponent<Rigidbody>();
// 플레이어방향으로 힘을 주기
rigidGrenade.AddForce(player.transform.forward * 10 + Vector3.up * 10, ForceMode.Impulse);
rigidGrenade.AddTorque(Vector3.back * 10, ForceMode.Impulse);
player.hasGrenades--;
player.grenades[player.hasGrenades].SetActive(false);
}
public void Execute(Vector3 playerVector)
{
}
public void Exit()
{
}
}