반사되는 발사체 트러블슈팅

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);
    }
}

결론

위와 같은 과정을 거쳐 반사 발사체가 정상적으로 동작하도록 수정할 수 있었다.

  1. ProjectileManager에서 반사된 발사체를 다시 생성할 수 있도록 수정.
  2. OnTriggerEnter2D에서 정확한 반사 방향을 계산.
  3. 충돌 직후 너무 가까운 위치에서 다시 충돌하는 문제를 해결하기 위해 0.2f 만큼 이동하여 생성.
  4. 재귀 함수 호출을 최적화하여 불필요한 연산을 줄이고 성능을 개선.

이러한 수정으로 반사되는 발사체의 비교적 자연스러운 움직임을 구현할 수 있었다.

댓글남기기