[과제 소개]
교수님이 WinAPI를 가지고 3D 게임의 프레임워크를 만들어서 학생들에게 뿌려주셨다. 우리는 그 프레임워크의 기본적인 구조만 가지고 비행기 게임을 만들어야 했다. 과제의 내용은 이러했다.
◆ 플레이어가 움직일 때 상/하/좌/우 방향에 사각형들을 그려 공간의 입체감을 표현해야 함.
◆ 플레이어와 적은 이 공간을 벗어날 수 없음
◆ 공간의 반대쪽 끝에 적 보스가 있음.
◆ 적 보스는 플레이어를 향하여 총을 발사할 수 있음.(적 보스의 모양 ,이동, 무기 등은 각자 설정)
◆ 플레이어가 보스를 제거하면 게임이 종료 됨.
◆ 적은 4종류이며 색상은(Red,Blue,Green,Pink)으로 구분함.
◆ 모양은 육면체이고 크기가 다를 수 있음.
◆ (Blue,Green,Pink)색상의 적은 플레이어 진행방향 100m 앞에서 1초마다 생성
◆ (Red) 색상의 적은 플레이어 진행 방향 100미터 앞에서 10초마다 생성
◆ 적의 생성 위치와 이동방향은 무작위 이며, 항상 회전하며 이동함. 회전속력과 이동속력은 각자 설정
◆ 적은 직육면체 공간에 부딪치면 반사 벡터의 방향으로 이동해야 함.
◆ 적은 플레이어가 발사한 총알에 맞으면 100조각 이상으로 폭발하면서 사라져야 함.
◆ W/A/S/D를 사용하여 앞/뒤/좌/우 방향으로 이동할 수 있음.
◆ Q/E를 사용하여 위 아랫방향으로 이동할 수 있음.
◆ 마우스를 사용하여 플레이어를 x-축, y-축, z-축으로 회전할 수 있음
◆ 플레이어와 적이 충돌하면 게임이 새로 시작됨
◆ Ctrl 키를 누를 때마다 플레이어가 향하는 방향으로 총알을 발사할 수 있음.
◆ 플레이어는 최소 20발 이상 총알을 갖고 있음.
◆ 총알은 항상 회전하면서 이동함.
◆ 플레이어가 직육면체 공간에 부딪히면 색상이 바뀌어야 함.
◆ 플레이어의 뒤에서 접근하는 적이 있으면 플레이어의 색이 Pink색으로 바뀌어야 함.
◆ 총알이 Red색상의 적과 충돌할 때마다 특수 아이템을 가지게 됨.
◆ 자동 조준할 적을 마우스로 선택(Picking)할 수 있음.
굉장히 많은 내용이 있었고, 나는 이 중에서 피킹 알고리즘 구현이 가장 어려웠었다. OpenGL 배울 때도 구현해 본 적이 없었기 때문에 인터넷을 미친듯이 뒤졌다. 그렇게 얻어낸 결론으로는
1. 로컬(모델) 좌표계 상에서 정의된 3D 객체의 정점들을 화면 좌표계 기준으로 변환한다.
2. 마우스의 위치를 얻는다.( 물론 화면 좌표계 기준으로 )
3. 변환된 3D 객체의 화면 좌표계 상의 범위( 다각형들 )와 마우스 의 위치( 점 )를 비교해
점이 다각형 안에 포함됐는지를 판별 한다.
와 같은 방식으로 구현해야 했다.
그래서 나는 이렇게 구현했다.
-Ray(광선) 구조체(광선의 위치벡터와 방향벡터가 존재함.)를 만들었다. 그리고 스크린좌표-> 화면의 역변환-> 투영 역변환 -> 카메라 좌표가 되도록 해서 광선에 정규화된 방향벡터와 위치벡터를 얻었다. 그리고 이 광선에 대해 부딪히는 모든 큐브 오브젝트 중에서 가장 distance가 가까운 오브젝트를 m_pickedObject에 넣고, Ctrl키를 누를 때마다 PickedObject를 조준해서 총알을 쏘도록 하였다.
float dist = 100000000;
float intersectDist=10000000; // 최소 플레이어와 오브젝트간 거리
float vx, vy, vz;
mx = LOWORD(lParam); //xScreen
my = HIWORD(lParam); //yScreen
vx = (((((mx - 0/*뷰포트의 left*/)*2.0f / 640/*뷰포트의 width*/) - 1.0f)) - m_pPlayer->m_pCamera->m_xmf4x4Projection._31) / m_pPlayer->m_pCamera->m_xmf4x4Projection._11;
vy = ((-(((my - 0/*뷰포트의 top*/)*2.0f / 480) - 1.0f/*뷰포트의 height*/)) - m_pPlayer->m_pCamera->m_xmf4x4Projection._32) / m_pPlayer->m_pCamera->m_xmf4x4Projection._22;
vz = 1.0f;
XMFLOAT4X4 m = Matrix4x4::Inverse(m_pPlayer->m_pCamera->m_xmf4x4View);
ray.dirX = vx * m._11 + vy * m._21 + vz * m._31;
ray.dirY = vx * m._12 + vy * m._22 + vz * m._32;
ray.dirZ = vx * m._13 + vy * m._23 + vz * m._33;
ray.posX = m._41;
ray.posY = m._42;
ray.posZ = m._43;
picked = false;
for (auto &p : m_ppObjects)
{
if (p.m_xmOOBB.Intersects(XMLoadFloat3(&XMFLOAT3(ray.posX, ray.posY, ray.posZ)), XMLoadFloat3(&Vector3::Normalize(XMFLOAT3(ray.dirX, ray.dirY, ray.dirZ))), dist))
{
if (sqrt((p.GetPosition().x-m_pPlayer->GetPosition().x)*(p.GetPosition().x - m_pPlayer->GetPosition().x)
+(p.GetPosition().y-m_pPlayer->GetPosition().y)*(p.GetPosition().y - m_pPlayer->GetPosition().y)
+ (p.GetPosition().z - m_pPlayer->GetPosition().z)*(p.GetPosition().z - m_pPlayer->GetPosition().z))<intersectDist)
{
intersectDist = sqrt((p.GetPosition().x - m_pPlayer->GetPosition().x)*(p.GetPosition().x - m_pPlayer->GetPosition().x)
+ (p.GetPosition().y - m_pPlayer->GetPosition().y)*(p.GetPosition().y - m_pPlayer->GetPosition().y)
+ (p.GetPosition().z - m_pPlayer->GetPosition().z)*(p.GetPosition().z - m_pPlayer->GetPosition().z));
m_pickedObject = &p;
picked = true;
}// 최고 작은 거리에서의 오브젝트 찾기.
}
}
관련 코드는 대충 이러했다.
[영상]
[후기]
3D게임을 API로 제작하는 과정도 굉장히 신기했고, 재미있었다. 다만 피킹알고리즘이 너무 어려워서 시간을 많이 잡아먹었었던 것 같다. 미니맵은 어렵지 않게 만들 수 있었던 것 같다.
'Direct3D12' 카테고리의 다른 글
[DirectX12] 렌더링 파이프라인 (0) | 2021.07.11 |
---|---|
[Direct3D12] 과제2 (1) | 2019.11.01 |