Unity) 발사체 반사
반사되는 발사체 트러블슈팅
Unity에서 반사되는 발사체를 제작하는 과정에서 발생한 주요 이슈를 정리하고 해결 방법을 제시해본다.
반사되는 발사체 제작
문제
- 발사체가 반사되도록 구현해야 하지만, 기존 발사체는 직진만 가능하다.
ProjectileManager
를 통해 발사체를 생성하고 방향을 지정하지만, 반사된 방향으로 다시 생성하는 기능이 없다.
해결 방법
ShotBullet
함수에서bouncing
값을 인자로 받아, 남은 반사 횟수가 있을 경우 새로운 발사체를 생성하도록 구현- 충돌 이벤트에서 반사각을 계산하고 새로운 방향으로 발사체를 재생성.
public void ShootBullet(RangeWeaponHandler rangeWeaponHandler,
Vector2 startPosition,
Vector2 direction,
int key,
int bouncing)
{
// 무기 핸들러의 탄환 인덱스를 사용하여 발사할 탄환 프리팹을 가져옴
GameObject origin = projectilePrefab;
// 탄환 오브젝트를 생성 (위치: startPosition, 회전: 없음)
GameObject obj = Instantiate(origin, startPosition, Quaternion.identity);
// 생성된 탄환의 ProjectileController 컴포넌트를 가져옴
ProjectileController projectileController = obj.GetComponent<ProjectileController>();
// 탄환 초기화 (이동 방향 및 무기 핸들러 정보 설정)
projectileController.Init(direction, rangeWeaponHandler, this, key, bouncing, obj);
}
반사 각도 계산
문제
- 발사체가 충돌 시, 반사각을 정확하게 계산해야 했지만, 초기 구현에서 반사 방향이 비정상적으로 설정됨.
- 반사각을 계산하는 과정에서 충돌한 물체의 표면의 노멀(Normal)벡터를 잘못 설정하며, 예상과 다른 방향으로 반사됨.
해결 방법
OnTriggerEnter2D
에서 충돌한 물체의 표면의 노멀벡터를ContactPoint2D
를 통해 새로 계산.- 노멀벡터를 기준으로 입사각과 같은 반사각을 가지도록 벡터 연산.
private void OnTriggerEnter2D(Collider2D collision)
{
// 충돌 표면의 법선 가져오기 (2D 전용)
ContactPoint2D[] contacts = new ContactPoint2D[1];
if (collision.TryGetComponent(out Rigidbody2D rb) && rb.GetContacts(contacts) > 0)
{
reflect_normal = contacts[0].normal; // 첫 번째 충돌 표면의 노말벡터
}
else
{
// 대체 노말벡터 (비정상적인 경우)
reflect_normal = (transform.position - collision.transform.position).normalized;
}
.
.
.
}
반사는 되지만 바로 콜라이더에 막히는 현상
문제
- 반사된 발사체가 바로 벽에 충돌해, 반사가 제대로 작동하지 않음.
- 충동 시 반사된 위치가 기존 충돌 위치와 너무 가까워, 바로 다시 충돌 판정이 발생(Raytracing의 특징인듯)
해결방법
- 충돌 후 반사된 발사체를 생성할 때, 시작 위치를 충돌 지점에서 발사될 방향으로 조금 떨어뜨려 다시 지정.
ClosestPoint
를 사용해 충돌 위치를 얻고, 반사 방향으로0.5f
만큼 이동
// 반사각 계산을 통한 새로운 방향
Vector3 relect_dir = Vector3.Reflect(this.direction, reflect_normal).normalized;
// 충돌 부위에서 새로운 발사 위치
Vector3 startPoint = transform.position + relect_dir * .5f;
재귀 함수 위치 고려
문제
- 반사 횟수가 줄어들 때마다 새로운 발사체를 생성하는 방식이므로, 무한 루프를 방지하고 성능을 최적화해야 함.
OnTriggerEnter2D
내에서 재귀적으로ShootBullet
을 호출하는 구조로 인해 성능 저하 가능성 존재.
해결 방법
bouncing
값이 0보다 큰 경우에만ShootBullet
을 호출하여 재귀적으로 반사 구현.- 반사 횟수가 다하면 발사체를 제거하여 불필요한 연산을 줄임.
Destroy(gameObject);
를 적절한 위치에 배치하여 불필요한 오브젝트가 남지 않도록 처리.
// 레벨 충돌 레이어에 닿았는지 확인
if (levelCollisionLayer.value ==
(levelCollisionLayer.value | (1 << collision.gameObject.layer)))
{
// 충돌 위치에서 투사체 파괴
DestroyProjectile(collision.ClosestPoint(transform.position) - direction * .2f
, fxOnDestroy);
if (bouncing != 0)
{
projectileManager.ShootBullet(rangeWeaponHandler,
startPoint,
relect_dir,
this.key,
bouncing - 1);
}
}
결론
위와 같은 과정을 거쳐 반사 발사체가 정상적으로 동작하도록 수정할 수 있었다.
ProjectileManager
에서 반사된 발사체를 다시 생성할 수 있도록 수정.OnTriggerEnter2D
에서 정확한 반사 방향을 계산.- 충돌 직후 너무 가까운 위치에서 다시 충돌하는 문제를 해결하기 위해
0.2f
만큼 이동하여 생성. - 재귀 함수 호출을 최적화하여 불필요한 연산을 줄이고 성능을 개선.
이러한 수정으로 반사되는 발사체의 비교적 자연스러운 움직임을 구현할 수 있었다.
댓글남기기