크롬에서 인터넷이 끊겼을 때 할 수 있는 공룡 게임입니다.
Flappy Bird 게임처럼 키 하나로 간단하게 즐길 수 있는 킬링 타임용 게임이죠.
(위의 공룡 게임은 Space Bar 하나 만을 이용하여 진행합니다.)
공룡이 앞으로 전진(사실 배경과 장애물이 공룡에게 접근)하고 선인장을 피해서 점프하는 게임입니다.
이 공룡 게임을 C++(콘솔 프로젝트)을 이용하여 간단히 구현해보겠습니다.
프로젝트 생성 (C++ 빈 프로젝트) |
C++ 빈 프로젝트를 생성 합니다.
다른 파일 없이 메인 파일 하나로만 구현할 겁니다.
기능 나누기 |
구현할 기능들을 나누어봅시다.
- 메인 함수 (딱 메인만 있는 함수)
- 게임 관리 함수
- 키 입력 감지 함수
- 커서 위치 설정 함수 (콘솔이기 때문에 커서의 이동을 자주 사용하게 됩니다.)
- 커서 초기 설정 함수
- 공룡 그리는 함수
- 나무(선인장)를 그리는 함수
- 점수판 함수
진행 순서는 제가 꼴리는 대로 진행합니다.
기본 include, define 및 전역 변수 |
기본적으로 사용하는 include와 define 및 전역 변수입니다.
#include <iostream>
#include <Windows.h>
#include <conio.h>
#define PRINTLN(STR) {printf(STR);printf("\n");}
constexpr int KEY_ESC = 27; // ESC 키
constexpr int KEY_SPACE = 32; // SpaceBar 키
constexpr int MAX_JUMP = 6; // 최대 점프 높이
constexpr int Y_BASE = 10; // 공룡의 초기 Y축 위치
constexpr int Y_COLLISION = 4; // Y축의 충돌 기준 위치
constexpr int TREE_COLLISION = 7; // 나무가 공룡과 충돌 가능한 X축 위치
constexpr int TREE_START = 50; // 나무가 생성되는 위치
constexpr int TREE_END = -6; // 나무가 사라지는 위치
constexpr int SLEEP_TIME = 35; // 게임 갱신 주기
키 입력 감지 함수 (3번 항목) |
키 입력 감지 함수를 작성합니다.
#include <conio.h>
/**
키 눌림 확인
@param
@return
*/
int GetKeyDown()
{
if (_kbhit() != 0)
return _getch();
return 0;
}
여기서 핵심 함수는 두 가지 인데 "_kbhit()"와 "_getch()" 입니다.
_kbhit() | 키가 눌렸으면 0이 아닌 값이 반환, 안 눌렸으면 0을 반환 |
_getch() | 읽은 문자를 반환 (콘솔에서 단일 문자를 읽어 들임) |
그렇기에 GetKeyDown 함수를 while문으로 계속 실행하여 키가 입력되었을 때 해당 키가 어떤 키인지 구분하여 그에 맞는 행동을 취하도록 할 것입니다.
커서 초기 설정 함수 (5번 항목) |
먼저 커서를 설정하는 함수를 작성합니다.
#include <Windows.h>
/**
커서 관련 설정 (커서 안보이기)
@param
@return
*/
void CursorSettings()
{
CONSOLE_CURSOR_INFO cursorInfo = { 0, };
cursorInfo.dwSize = 1;
cursorInfo.bVisible = FALSE;
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursorInfo);
}
커서의 깜빡임을 없애고 크기를 줄여 콘솔 창에 안 보이게 합니다.
커서 위치 설정 함수 (4번 항목) |
커서 위치를 설정할 수 있는 함수를 작성합니다.
/**
커서 옮기기
@param _nX X위치
@param _nY Y위치
@return
*/
void SetKeyCursor(int _nX, int _nY)
{
COORD cursorPos = { (SHORT)_nX, (SHORT)_nY };
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), cursorPos);
}
인자로 X위치와 Y위치를 설정하면 그 위치로 커서의 위치를 옮깁니다.
참고로 커서의 증감은 위의 그림과 같습니다.
공룡 그리는 함수 (6번 항목) |
공룡을 그리는 함수를 작성합니다.
/**
공룡 그리기
@param nY 공룡의 Y위치
@return
*/
void DrawDino(int nY)
{
static bool bFootToggle = false; // 공룡 그리기가 갱신될 때 발의 위치를 바꿈
SetKeyCursor(0, Y_BASE - nY); // 공룡의 그리기 위치 변경
PRINTLN(" ■■");
PRINTLN(" ■");
PRINTLN("■■■■");
if (bFootToggle)
PRINTLN(" ■")
else
PRINTLN(" ■")
bFootToggle = !bFootToggle;
}
인자로 공룡의 Y축 위치를 입력 받습니다.
(공룡은 X축이 아닌 Y축 점프만 하기 때문이죠.)
static 토글을 두어서 호출할 때 마다 공룡의 발을 바꾸게 합니다.
나무(선인장)를 그리는 함수 (7번 항목) |
나무를 그리는 함수를 작성합니다.
/**
나무 그리기
@param nX 나무의 X위치
@return
*/
void DrawTree(int nX)
{
if (nX >= 0)
{
SetKeyCursor(nX, Y_BASE);
PRINTLN(" ■");
SetKeyCursor(nX, Y_BASE + 1);
PRINTLN("■ ■");
SetKeyCursor(nX, Y_BASE + 2);
PRINTLN("■■■");
SetKeyCursor(nX, Y_BASE + 3);
PRINTLN(" ■");
}
else if (nX >= -2)
{
nX = 0;
SetKeyCursor(nX, Y_BASE);
PRINTLN(" ■");
SetKeyCursor(nX, Y_BASE + 1);
PRINTLN(" ■");
SetKeyCursor(nX, Y_BASE + 2);
PRINTLN("■■");
SetKeyCursor(nX, Y_BASE + 3);
PRINTLN("■");
}
else if (nX >= -4)
{
nX = 0;
SetKeyCursor(nX, Y_BASE);
PRINTLN("■");
SetKeyCursor(nX, Y_BASE + 1);
PRINTLN("■");
SetKeyCursor(nX, Y_BASE + 2);
PRINTLN("■");
SetKeyCursor(nX, Y_BASE + 3);
PRINTLN("");
}
}
여기서 인자로는 X위치만 입력 받습니다.
나무를 그리는데 if문으로 3가지 조건에 따라서 그리게 되는데 나무가 맵 뒤로 사라지는 것을 구현하기 위함입니다.
(나무를 그릴 때 쓰이는 ■이 특수 문자는 콘솔 창에서 X좌표를 2개 사용합니다.)
점수판 함수 (8번째 항목) |
점수판을 그리는 함수를 작성 합니다.
/**
점수판 그리기
@param nScore 점수
@return
*/
void DrawScore(int nScore)
{
SetKeyCursor(0, 0);
printf("Score : %d\n", nScore);
}
인자로는 점수만 받습니다.
게임 관리 함수 (2번째 항목) |
게임 관리 함수를 작성 합니다.
게임의 전반적인 동작이 구현되며 모든 매커니즘이 이 함수 안에 들어가 있습니다.
/**
게임 시작
@param
@return
*/
void GameStart()
{
int nScore = 0; // 점수
int nCurKey = -1; // 현재 눌린 키
int nYPos = 0; // 공룡의 Y위치 (점프에 쓰임)
int nTreePos = TREE_START; // 나무가 생성되는 초기 X위치
bool bIsJumpping = false; // 현재 공룡이 점프 중인가
bool bIsJumpped = false; // 현재 공룡이 점프하여 최고 지점에 올랐는가
bool bIsCollision = false; // 현재 공룡이 나무와 충돌하였는가
while (true)
{
// 점수 출력
DrawScore(nScore);
// 키 입력 확인
nCurKey = GetKeyDown();
switch (nCurKey)
{
case KEY_ESC: // ESC 키
return;
case KEY_SPACE: // SPACE BAR 키
bIsJumpping = true;
break;
default:
break;
}
// 점프 관련
// 점프 중인가
if (bIsJumpping)
{
// 최고 지점에 이르지 않았다면
if (nYPos < MAX_JUMP &&
bIsJumpped == false)
nYPos++;
// 최고 지점에 도달 후 점프가 끝났다면
else if (bIsJumpped &&
nYPos == 0)
{
bIsJumpped = false;
bIsJumpping = false;
}
// 최고 지점에 도달 후라면 (중력을 표현)
else if (bIsJumpped)
nYPos--;
// 최고 지점에 도달했다면
else if (nYPos == MAX_JUMP)
bIsJumpped = true;
}
// 점프 중이 아니라면
else
{
if (nYPos > 0)
nYPos--;
}
// 나무 위치 관련
if (nTreePos > TREE_END)
nTreePos -= 2;
else
nTreePos = TREE_START;
// 충돌 관련
// 나무의 X위치가 충돌 가능 X위치라면
if (nTreePos < TREE_COLLISION)
{
// 공룡의 Y위치가 충돌 가능 위치이고
// 나무의 X위치가 충돌 가능 위치라면
if (nYPos < Y_COLLISION &&
nTreePos > TREE_END + 1)
bIsCollision = true;
}
// 나무 그리기
DrawTree(nTreePos);
// 공룡 그리기
DrawDino(nYPos);
Sleep(SLEEP_TIME);
system("cls");
// 충돌 시 게임 오버
if (bIsCollision)
{
printf("\n");
printf("\n");
printf(" Game Over\n");
printf(" Score : %d\n", nScore);
system("pause");
return;
}
// 충돌 상태가 아닐 때는 점수 증가
else
{
nScore += 1;
}
}
}
주석을 보시면 아마 이해가 가실 겁니다.
(점프 부분만 잘 이해하시면 됩니다.)
(점프 부분을 저렇게 나눈 이유는 중력 표현과 Spacebar를 꾹 눌렀을 때를 위하여 나누었습니다.)
메인 함수 (1번째 항목) |
메인 함수를 작성 합니다.
int main(void)
{
CursorSettings();
GameStart();
return 0;
}
커서 설정과 게임 시작 함수만 호출해줍니다.
게임 스크린샷 |
전체 소스 코드는 아래에 있습니다.
'Study > C++' 카테고리의 다른 글
[C++11] 스마트 포인터3 (weak_ptr) (0) | 2022.09.21 |
---|---|
[C++11] 스마트 포인터2 (shared_ptr) (1) | 2022.09.20 |
[C++11] 스마트 포인터1 (unique_ptr) (0) | 2022.09.19 |
[C++] std::array (C++ 표준 라이브러리 배열 컨테이너 이야기) (0) | 2022.09.18 |
[C++] 데이터 직렬화 라이브러리 씨리얼(Cereal) (0) | 2021.03.25 |