리듬게임을 만들어보자 5. 경우별로 나눠서 관리하기

Posted by 적분 ∫2tdt=t²+c
2009.11.26 17:39 프로그래밍/스페샬
게임을 켜면 곧바로 게임이 시작되는건 아니다. 세부 설정을 조작하는 부분도 있고, 로딩하는 부분도 있고, 실제로 게임을 하는 부분도 있다. 이런 경우별로 따로따로 코딩을 해줘야 한다. 가장 손쉬운 방법은 if-else문을 이용하는 것이다.
void GGame::OnProc()
{

if(state==Select_Game)
{
...
}
else if(state==Load_Game)
{
...
}
else if(state==Play_Game)
{
...
}
...
}
이 짓을 OnProc(처리하는 부분)말고도 OnDraw(그리는 부분), 키입력 받는 부분 등등에서 다 해줘야 한다. 함수가 지저분하게 계속 부풀어오르다가 터져버릴지도 모른다. (ㅡ,,ㅡ) 게다가 각종 상태별로 필요한 변수들도 한 클래스에 모조리 선언하다 보면 클래스도 터져버릴지 모른다...
이건 c++다운 방법이 아니다. 현명한 c++사용자라면 어떻게 할까?
답: 클래스를 만든다.



MState클래스를 만들어보았다.

*OnProc(DWORD ElapsedTime)
매 프레임을 처리할 때마다 호출된다.  ElapsedTime은 전 프레임과의 시간차(ms)이다.
*OnPostProc()
프레임 처리를 마무리 지을 때 호출된다.
*OnDraw()
그리기를 수행할 때 호출된다.
프레임레이트를 조절하기 위해 호출이 생략될 수 있다.
*OnLost()
D3D장치를 잃었을 때 호출된다.
*OnReset()
D3D장치를 복구했을 때 호출된다.
*OnKeyDown(int KeyCode)
키가 눌려진 것을 감지했을 때 호출된다. 반드시 OnProc()과 OnDraw()사이에 호출된다.
*OnKeyUp(int KeyCode)
눌려진 키가 떼어진 것을 감지했을 때 호출된다. 반드시 OnProc()과 OnDraw()사이에 호출된다.
*OnMouseMove(int x, int y)
마우스가 움직인 것을 감지했을 때 호출된다.
*OnLButtonDown(), OnMButtonDown(), OnRButtonDown()
마우스의 왼쪽/중간/오른족 버튼이 눌려진 것을 감지했을 때 호출된다. OnProc()과 OnDraw()사이에 호출된다.
*OnLButtonUp(), OnMButtonUp(), OnRButtonUp()
마우스의 왼쪽/중간/오른족 버튼이 눌려진 것을 감지했을 때 호출된다. OnProc()과 OnDraw()사이에 호출된다.
*OnDrawSkip()
현재 프레임의 그리기가 생략되었을 때 OnDraw대신에 호출된다.

이 정도 이벤트면 충분할 것이다. 이런 함수들을 가지는 MState클래스를 만들었다. 그런데 virtual void OnProc(DWORD ElapsedTime)=0; 에서 =0 은 뭘까...
이것은 MState클래스가 OnProc함수의 실제 몸통을 가지지 않고 있다는 것을 뜻한다. 그러면 실제로 몸통이 없는 클래스에서 어떻게 OnProc함수가 호출될수 있을까?
상속. 상속을 통해서, OnProc함수의 빈 몸통을 채워주면 된다. 이렇게 빈 몸통을 가지고 있는 클래스를 가상 클래스라고 부른다. 그리고 함수들 앞에 붙은 virtual은 상속받은 클래스가 이 함수를 덮어써버릴수 있다는 표시이다.

자, 이제 GGame클래스에서 MState클래스로 다리를 놓아야 한다.

GGame클래스의 멤버변수로 MState* m_pstate와 MState* m_pnewstate를 만들자.
초기화는 NULL로 반드시 해주는것 빼먹지 말고.


각각의 함수들에서 m_pstate가 있으면 m_pstate의 함수들을 호출해준다. MState의 함수들은 virtual로 덮어쓸수 있다고 해놓았으므로, MState의 함수를 덮어쓴 상속받은 클래스의 함수가 호출될 것이다. 이렇게 GGame클래스에서 MState클래스로 다리는 쉽게 놓였다.


SetState함수는 현재 상태를 설정해주는 함수이다. m_pnewstate에 새 MState*를  받았다. m_pstate에 곧바로 받지않고 m_pnewstate에 받은이유는 이제 나온다.


OnProc함수를 호출하기 전에 m_pnewstate에 값이 들어있는지 확인하여서 값이 들어있다면 m_pstate를 해제하고, m_pstate에 m_pnewstate를 넣는다. 이렇게 번거롭게 바꾸는 이유는 다음과 같다.
SetState에서 m_pstate를 곧바로 바꿔버린다고 해보자. 그런데 SetState함수가 어디서 호출될지는 아무도 알 수 없는 일이다. 만약 OnProc을 호출하고, OnPostProc을 호출하기 전에 SetState가 호출되어 MState를 바꿔버린다면, 어떤 MState는 OnProc만 호출되고, 어떤 MState는 OnPostProc만 호출되는 일이 일어날지도 모른다. 그래서 OnProc/OnPostProc 짝을 맞춰주기 위해, 일부로 OnProc함수를 호출하기 전에 m_pstate를 바꿔치우는 것이다.

자, 이제 MState를 한 번 써먹어 보자. MState를 상속받아 MPlayGame을 만드는 것이다.


이제 함수들의 몸통을 적당히 구현해 놓자.


자, 그리고 프로그램이 처음 시작할 때, MPlayGame을 최초 상태로 지정해준다.


이렇게 함으로써, if-else떡칠하지 않고, 여러가지 경우를 처리할수 있게 되었다.
게임을 로딩하는 부분을 만들고 싶으면 MState를 상속받아 MLoadGame을 만들고, SetState의 인자로 MLoadGame을 넘겨주면 되고, 게임을 선택하는 부분을 만들고 싶으면 MState를 상속받아 MSelectGame을 만들고, SetState로 설정하면 되고...

이 댓글을 비밀 댓글로
    • 학생^^;
    • 2009.11.27 12:18
    안녕하세요 ^^ 게임학과에 다니고 있는 대학생입니다..
    어쩌다보니 제가 이번 프로젝트에서 리듬게임과 AR을 합쳐서 만드는
    게임을 기획 및 제작을 하게 되었는데..
    다름이 아니라.. ∫2tdt=t²+c님께서 제작하신 것을
    다이렉트 엑스를 다 설치하고; 폴더까지 지정하여 프로젝트를 실행하여도
    디버깅은 다 되어 실행창은 뜨지만 작업중단창(오류보고보냄, 보내지 않음의 버튼이 있는)이 뜹니다
    왜 그런지 모르겠어요 ㅠㅠ 답변좀 부탁드립니다 ㅠㅠㅠㅠ
    • bab2min@hanmail.net 으로 좀더 자세한 상황 설명이나, 스샷 등을 보내주시면 확실히 알아보는데 좋겠습니다.