Game Class를 만들 것인데, 추후 다른 게임을 만들 때 이 Class를 상속받아서 만들 것이다.
이제 만들 Game Class는 그래픽을 담당하는 Graphics Class와 입력을 담당하는 Input Class를 포함하고 있다. Graphics Class는 이전 포스팅에서 만들었던 DirectX와 비슷하므로 다른 점을 설명하고 넘어갈 것이다.
Game Class를 만드는 법을 살펴보자!
(Game Class의 include)
Game Class를 만든다.
(Game Class의 protected)
public의 메서드들을 만든다.
(생성자와 소멸자)
생성자는 input 클래스의 객체를 만든다. 다음 포스팅글에서 얘기하겠지만 Input 클래스의 생성자에서 기초적인 입력에 관한 버퍼들을 초기화 한다.
생성자에서 기초적인 초기화를 하고 추가적인 초기화가 없으므로 initialized 라는 변수에 false를 담아둔다. 후에 이 변수를 보고 game 클래스의 초기화를 시켜줄 것이다.
소멸자는 Game 클래스가 삭제될 때 예약된 메모리를 해제하는 순서로 구성되어있다.
(윈도우 메시지 핸들러)
각 이벤트에 대한 처리를 담당한다.
기존에 있던 winmain.cpp 에 있던 WinProc 의 메시지 핸들하는 부분이 Game 클래스로 옮겨 졌으므로 winmain.cpp의 WinProc 도 수정해주어야한다.
(WinProc 수정)
Game 클래스의 초기화를 담당하는 메서드이다.
(Game클래스의 초기화)
여기서 Graphics 객체를 생성한다. 그 후 그래픽 객체를 초기화 하고, input 객체를 초기화한다.
마지막의 타이머 사용은 frametime을 계산하기 위한 것이다.
화면에서 움직이는 그래픽 아이템들이 정지된 이미지를 적절한 시간에 연속적으로 화면에 보여주므로써 움직이는 것처럼 보이는 것이다. 이 프레임시간이 너무 빠르면 그래픽 아이템이 너무 빠르게 움직일 것이고, 너무 느리면 그래픽 아이템이 화면에서 너무 느리게 움직일 것이다. 그러므로 우리는 적절한 시간을 간격으로 화면에 출력해주어야한다. (우리가 원하는 속도로)
그 계산을 위해 고성능의 타이머를 사용한다. 요즘 PC에는 기본적으로 고성능의 타이머를 갖추고 있으므로 이것을 사용할 것이다. QueryPerformanceFrequency 함수를 이용해 timerFreq에 고성능 타이머의 발생 빈도를 저장한다. 그리고 QueryPerformanceCounter함수를 이용해 시간을 가져오는데 이 때 timeStart 변수에 시작 시간을 가져온다.
그 후 초기화를 진행 하였으므로 initialized 변수에 true라고 저장한다.
그 다음 처리해야할 문제는 디바이스 로스트 상태이다.
디바이스 로스트 상태란 Direct3D 그래픽 디바이스를 놓친 상태이다. 예를 들어 우리가 게임을 하다가 Alt-Tab을 눌러서 다른 화면을 보다가 다시 게임을 틀면 게임이 나와야한다. 이 때 게임이 화면에 그려지지 않는 상태가 그래픽 디바이스 로스트 상태이다.
그렇기에 이러한 상황을 처리해주어야한다. 디바이스 로스트 상태가 되면 리셋하고 리소스를 다시 생성해야한다.
(디바이스 로스트 처리)
이 때 주의할 점은 리셋 함수를 호출하기 전 기존에 할당되었던 모든 리소스를 해제하지 않으면 reset 함수가 실패한다.
위에서 사용한 getDeviceState와 reset 함수는 아래와 같다.
(getDeviceState, reset)
D3DERR_DEVICELOST : 디바이스가 로스트 상태가 되서 현재 복구할 수 없다.
D3DERR_DEVICEENOTRESET : 디바이스를 다시 작동할 수 있다.
D3DERR_DRIVERINTERNALERROR : 디바이스에 내부 에러가 있다. 사용자에게 보고하지만, 할 수 있는 방법이 없다.
또 releaseAll 함수와 resetAll 함수이다.
(releaseAll, resetAll)
그 다음 구현해야할 것이 게임 그래픽 렌더링이다. 그래픽을 그리는 과정을 렌더링이라고 부른다.
렌더링은 DirectX의 BeginScene 함수를 호출해 신을 시작하고, 모든 렌더링이 끝난 후 DirectX의 EndScene 함수를 호출해 신을 끝낸다. 이 Scene 밖에서 렌더링을 하면 실패한다.
그러므로 graphics 클래스 안에 beginScene함수와 endScene을 만들어준다.
(beginScene, endScene)
endScene에서는 디바이스 객체의 EndScene() 함수를 호출한다.
이 두 함수 사이에서 그래픽을 렌더링해야한다. 실제 게임 아이템을 렌더링하는 renderGame() 함수는 아래와 같이 생겼다.
(renderGame 함수)
백버퍼를 지워 준 후 그 백버퍼에 그래픽을 그린다. 그리고 endScene으로 그리는 것을 마무리한다. 그 후 디바이스 로스트를 체크하고 (로스트상태면 처리해주고)
그래픽 객체의 showBackbuffer 함수를 이용해 화면에 그렸던 백버퍼를 출력해준다.
이제 메인 메시지 루프에서 계속 호출될 게임클래스의 run 함수를 만들 것이다.
(메인 메시지 루프)
(Game 클래스의 run 함수)
처음 부분으로는 그래픽 객체 확인이다.
(그래픽 객체 확인)
그 후 frametime을 계산하기 위한 부분이 나온다.
(frametime 계산)
-> 그런 방법으로 frametime을 구한다면 게임 루프 밖의 소요되는 시간을 고려하지 못하게 되므로 실제 렌더링 되는 시간이 정확하지 않게 된다.
위 코드를 보면 frametime의 최소시간과 최대 시간을 정해둔다. 최소시간을 정해두고 그거보다 빠르다면 게임을 잠깐 재워둔다. 왜(?) -> 굳이 게임을 재워야할까 싶긴 하지만 이건 절전을 위한 것이다. 빠르게 처리 되었을 때는 잠깐 쉬게한다면 CPU 부하를 줄여주고 환경 친화적인 프로그램으로 만들어준다.
이건 앞으로 어떤 프로그램을 만들든지 중요한 부분이다. 우리의 배터리는 소중하므로!
그 후 frametime을 이용해 평균 fps를 계산해준다. 만약 frametime이 너무 느리다면, 최대 frame 시간으로 frametime을 설정해준다. 보통 이정도 까지 도달할 경우는 거의 없다.
그 다음부분은 게임동작 관련부분이다.
(게임 함수)
Game 클래스의 순수 가상함수 update(), ai(), collision() 함수를 실행해준다. 앞으로 만들 게임의 클래스에서 Game 클래스를 상속받을 것인데 그 때 이 함수들을 우리가 채워주는 것이다. 지금은 Game 클래스를 만드는 것으로 게임엔진의 틀을 만드는 과정이다.
(마지막 과정)
(* 참조 - 2D 게임 프로그래밍, 찰스 켈리)
'Programing > Game Programing' 카테고리의 다른 글
(게임프로그래밍) 간단한 애니메이션 추가 (0) | 2018.02.07 |
---|---|
스프라이트를 이용하여 이미지 그리기 (0) | 2018.02.05 |
게임 엔진 기초 틀 만들기(2) - Input Class (0) | 2018.01.31 |
DirectX 입문 초록색화면 프로그램 만들기 (0) | 2018.01.24 |
Visual Studio에서 DirectX 개발 설정 (0) | 2018.01.24 |