7장 연습문제 7.2
문제
7.2장의 3인칭 리스너 공식을 사용하여 이벤트 인스턴스의 가상 위치를 구현하라. CameraActor
는 과제를 위해 제공된 3인칭 카메라 클래스로 교체하여 사용하라.
풀이
사운드 이벤트의 가상 위치의 필요성이 제기된 3인칭 게임상의 리스너의 문제는 이 글을 참고하자. 가상 위치 사운드 이펙트의 핵심은 다음과 같다.
- 소리의 크기는 플레이어의 위치에 따라 결정되어야 한다.
- 소리의 방향은 카메라 위치에 따라 결정되어야 한다.
소리의 방향과 감쇠 효과는 결국 리스너의 설정에 의존한다. 때문에 SetListener
함수를 다음과 같이 수정하는 방법으로 문제를 해결할 수 있다.
- 감쇠 효과 계산할 때는 플레이어 위치를 사용하고
- 소리의 방향을 결정할 때는 카메라의 위치를 사용하여 구현할 수 있다.
구현 결과는 아래에서 확인할 수 있다. 영상에서 구는 플레이어이고, 큐브는 사운드 이벤트를 발생시키는 오브젝트이다.
1단계 SetListener 함수 수정
이번 문제는 SetListener
함수 수정이 가장 핵심이다. 기존에 리스너의 위치는 카메라 좌표에 해당하는 값인 invView.GetTranslation()
이 입력되었었다. (뷰 행렬의 역행렬이 카메라의 변환행렬이기 때문) 이 부분에 카메라의 위치가 아닌 플레이어의 위치가 입력될 수 있도록 아래와 같이 수정하자. (본 소스코드는 예제 7.1 도플러 효과를 반영하지 않은 코드이다.)
void AudioSystem::SetListener(const Matrix4& viewMatrix, const Vector3& playerPos)
{
// 카메라의 위치는 뷰 행렬의 역행렬에서 구할 수 있다.
Matrix4 invView = viewMatrix;
invView.Invert();
FMOD_3D_ATTRIBUTES listener;
// 리스너의 위치와 전방 벡터, 상향 벡터를 설정
// (삭제) listener.position = VecToFMOD(invView.GetTranslation());
listener.position = VecToFMOD(playerPos);
// 뷰 행렬의 역행렬에서 세 번째 행은 전방 벡터
listener.forward = VecToFMOD(invView.GetZAxis());
// 뷰 행렬의 역행렬에서 두 번째 행은 상향 벡터
listener.up = VecToFMOD(invView.GetYAxis());
// 속도를 0으로 설정 (도플러 효과를 사용할 시 수정)
listener.velocity = { 0.0f, 0.0f, 0.0f };
// FMOD로 보낸다 (0 = 리스너는 하나)
mSystem->setListenerAttributes(0, &listener);
}
2단계 SetListener 함수 호출
수정된 SetListener
함수에 3인칭 카메라의 위치가 아닌 플레이어의 위치를 매개변수로 전달해주면 완성이다. 저자가 제공해준 CameraActor
클래스의 UpdateActor
함수를 보자. 3인칭 카메라의 작동 원리는 플레이어 위치에 고정값을 더해서 위치를 지정하는 방식을 사용하고 있다. 때문에 실제 플레이어 위치가 바로 CameraActor
의 위치이다. 카메라의 위치(view
의 위치는 플레이어 위치에 일정 값을 더해서 사용할 뿐). 때문에 매개변수로 CameraActor
의 위치에 해당하는 GetPosition()
을 전달해주면 완성이다.
void CameraActor::UpdateActor(float deltaTime)
{
Actor::UpdateActor(deltaTime);
// Play the footstep if we're moving and haven't recently
mLastFootstep -= deltaTime;
if (!Math::NearZero(mMoveComp->GetForwardSpeed()) && mLastFootstep <= 0.0f)
{
mFootstep.SetPaused(false);
mFootstep.Restart();
mLastFootstep = 0.5f;
}
// Compute new camera from this actor
mCameraPos = GetPosition() - GetForward() * 200.0f + Vector3::UnitZ * 100.0f;
Vector3 target = GetPosition() + GetForward() * 100.0f;
Vector3 up = Vector3::UnitZ;
Matrix4 view = Matrix4::CreateLookAt(mCameraPos, target, up);
GetGame()->GetRenderer()->SetViewMatrix(view);
GetGame()->GetAudioSystem()->SetListener(view, GetPosition());
}
마지막으로 레벨에 존재하는 불필요한 Actor들을 삭제하고 사운드 이벤트를 발생시킬 오브젝트를 구성하면 시연 영상과 같은 결과를 얻을 수 있다. (이 부분은 전체 소스코드를 참고하자.)
이상으로 사운드 이벤트의 가상 위치 구현 설명을 마친다.