오늘 배운 내용
-
월드 좌표를 스크린 좌표로 변환 WorldToScreenPoint와 WorldToViewportPoint의 차이점
-
방향 벡터를 Quaternion으로 회전 시키기
월드 좌표를 스크린 좌표로 변환 WorldToScreenPoint와 WorldToViewportPoint의 차이점
팀 프로젝트에서 내가 담당한 부분인 Enemy관련 부분의 기능을 만들면서
Enemy가 플레이어의 위치로 Bullet을 발사하고 Bullet이 화면 밖으로 나가면 Destroy되는 부분을 만들고 있었다.
화면 밖으로 나갔는지를 판별하기 위해서 처음에 WorldToScreenPoint()가 떠올라서 사용했다.
private void CheckOutScreen()
{
Vector3 screenPos = BulletManager.Instance.mainCamera.WorldToScreenPoint(transform.position);
if (screenPos.x > 1 || screenPos.x < 0 || screenPos.y > 1 || screenPos.y < 0)
{
BulletManager.Instance.DestroyBullet(this);
}
}
이렇게 코드를 작성하고 실행하니 Bullet 객체들이 생성되는 동시에 사라져 버렸다 (??)
원인을 찾아보니 나는 스크린좌표를 0~1까지의 좌표라고 생각하고 WorldToScreenPoint() 메서드를 사용해서
스크린 좌표는 0~ 화면해상도 까지의 좌표를 뜻했고, 0~1까지의 화면상 좌표는 스크린 좌표가 아니라 뷰포트 좌표였다.
결국 내가 원하는 메서드는 WorldToViewportPoint() 였고 코드를 수정해서 기능을 구현할 수 있었다.
private void CheckOutScreen()
{
Vector3 screenPos = BulletManager.Instance.mainCamera.WorldToViewportPoint(transform.position);
if (screenPos.x > 1 || screenPos.x < 0 || screenPos.y > 1 || screenPos.y < 0)
{
BulletManager.Instance.DestroyBullet(this);
}
}
기능을 구현하고 나서 CheckOutScreen() 메서드를 Update()문에서 호출하고 있는데,
Bullet객체는 정말 많은 수가 생성될텐데 CheckOutScreen()를 호출되면 WorldToViewportPoint()을 한번 실행해야
다음 조건을 체크할 수 있기 때문에 문제가 되진 않을까? 라는 생각이 들었다.
그래서 일단 WorldToViewportPoint() 메서드에 대해서 gpt를 통해 찾아보니 거의 무시할 수준의 연산 리소스가 필요하기 떄문에
많은 오브젝트가 이 메서드를 프레임마다 호출하여도 성능 문제를 일으키지는 않을 것이라는 답변을 받았고,
자세한 정보는 좀 더 구글링을 해봐야겠지만 일단은 문제가 없는 것 같다.
2. 방향 벡터를 Quaternion으로 회전 시키기
Enemy들이 플레이어를 향해서 Bullet을 발사는 하지만 정확하게 방향을 조준하는게 아니라 일정 범위내에서 랜덤으로
방향을 지정하는 식으로 Enemy의 공격을 구현하기로 정해졌다.
방향 자체를 바꾸는 것은 간단했는데 변한 방향 만큼 Bullet의 오브젝트를 어떻게 회전 시켜줘야될지가 문제였고,
방법을 찾아본 결과 Quaternion에 Vector3를 곱하는 방법을 찾을 수 있었다.
그런데 막상 해보려고 하니 Vector3에 Quaternion을 * 연산자를 사용해서 연산할 수 없다는 에러 메세지가 발생했다.
이유는 Vector3의 방향을 Quaternion으로 바꿀때는 Quaternion * Vector3 순서로는 연산이 되지만
그 반대인 Vector3 * Quaternion 으로는 연산을 수행할 수 없다고 한다.
Quaternion * Vector3 연산을 통해서 회전을 적용한 위치를 얻을 수 있고, 이를 transform.rotation에 적용해서
오브젝트를 회전 시킬 수 있다.
protected void RotateBullet(Vector3 pos)
{
moveDirection = (pos - transform.position).normalized; // 임시로 EnemyManager 참조
float rotZ = Mathf.Atan2(moveDirection.y, moveDirection.x) * Mathf.Rad2Deg + Random.Range(-80f, 80f);
moveDirection = Quaternion.AngleAxis(rotZ, Vector3.up) * moveDirection;
transform.rotation = Quaternion.Euler(0, 0, rotZ);
}