오늘은 숫자기억게임과 시간맞추기게임을 만들어보도록 하겠습니다.
숫자기억게임
이 게임을 만들기 위해서는 enum class와 Sleep 함수, 메모리 누수에 관해 알아야합니다.
enum class란?
C++에는 enum과 enum class 두개가 존재합니다.
enum : 암시적 형변환이 가능, enum값 이름을 바로 사용가능
enum class : 암시적 형변환이 불가능, enum값 이름앞에 enum class 이름:: 을 붙여줘야함
사용예시
enum weapon
{
a,
b,
c
};
enum class weapon1
{
a1,
b1,
c1
};
void Test()
{
int test = a;
int test2 = weapon1::a1; // 오류발생
int test3 = (int)weapon1::b1; // 형변환 필요
}
C++11이후부터 enum class가 탄생하였고, enum class가 좀더 오류발생 가능성이 낮기 때문에 enum class를
쓰는게 좋습니다.
Sleep()이란?
Sleep은 #include <Windows.h> 안에 있는 함수로 콘솔창을 일정시간동안 멈추게 하는 기능을 가지고 있습니다.
밀리세컨드를 매겨번수로 넘겨줍니다 1000 = 1초
메모리 누수란?
메모리 누수란 힙에 저장한 값이 지우지 않았을때 생기는 현상입니다.
힙에 값을 저장하면 프로그램이 종료되도 값은 남아있게 되는데 이러면 심각한 메모리 낭비가 생기므로 힙에 있는
데이터를 지워야합니다.
void GameStart(MODE _eMode, int _sleeptime)
{
const int mode = (int)_eMode * 5;
int* ptr = new int [mode]; // 동적할당 : 실행중에 크기를 확인하고 그때에 맞춰서 크기를 지정해주는 방식
//int* ptr2 = new int;
switch (_eMode)
{
case MODE::EASY:
cout << "EASY" << endl;
break;
case MODE::NORMAL:
cout << "NORMAL" << endl;
break;
case MODE::HARD:
cout << "HARD" << endl;
break;
}
for (int i = 0; i < mode; ++i)
{
ptr[i] = rand() % 100 + 1;
cout << ptr[i] << ' ';
//둘이 같은거다.
//*(ptr + i) = rand();
Sleep(_sleeptime);
}
system("cls");
cout << "숫자를 입력해라." << endl;
int inp;
for (int i = 0; i < mode; ++i)
{
cin >> inp;
if (ptr[i] != inp)
{
cout << "틀림." << endl;
delete[] ptr;
return;
}
}
cout << "다맞춤" << endl;
// 메모리 leak (메모리 누수)
//delete ptr2;
delete[] ptr;
}
다음 코드를 예시로 들면, int* ptr = new int [mode];에서 포인터에 int변수에 주소를 저장합니다.
이를 동적할당이라고 합니다.
동적할당은 힙메모리에 저장되므로 앞서 말한대로 지워야합니다.
지우는 방법은 delete[] prt;로 만약 배열이 아니라 그냥 변수라면 delete ptr;처럼 쓰면 됩니다.
이때 힙메모리의 주소를 가지고 있는 포인터 변수는 힙메모리의 주소를 알고있기 때문에 주소를 통해서 힙메모리에 있는 데이터를 지우는 방식입니다.
주소를 통해 힙메모리를 지우고 포인터 변수는 지역변수이기 때문에 스택 메모리에 있어서 코드 블록이 끝나면
알아서 지워집니다.
이렇게 3가지를 알아보았으니 숫자기억게임을 만들어보겠습니다.
#include <iostream>
#include <Windows.h>
using namespace std;
enum class MODE
{
EASY = 1, NORMAL, HARD
};
/*
* enum: 가독성, 암시적 형변환
*
* modern c++
* c++11, c++14, c++17, c++20, c++23
* enum class(c++11): 개발자가 편합니다(오류를 잡아줘요)
* => 손이 아파져요.
*
*/
// 동적할당
void Init(); // 게임 시작시 한번 실행
int UserInput(); // 유저입력을 받는 함수
void GameStart(MODE _eMode, int _sleeptime); // 게임 시작을 하는 함수
int main()
{
Init();
int select = UserInput(); // 난이도를 입력받음
switch (select)
{
case (int)MODE::EASY:
GameStart(MODE::EASY, 1000);
break;
case (int)MODE::NORMAL:
GameStart(MODE::NORMAL, 700);
break;
case (int)MODE::HARD:
GameStart(MODE::HARD, 500);
break;
}
}
void Init()
{
cout << " ==================================== " << endl;
cout << " 숫자 기억 게임입니다. 모드를 선택하세요." << endl;
cout << " EASY:1, NORMAL:2, HARD: 3" << endl;
cout << " ==================================== " << endl;
srand((unsigned int)time(NULL));
}
int UserInput()
{
int input;
while (true)
{
cin >> input;
if (cin.fail() || input < 0 || input > 3)
{
cin.clear();
cin.ignore(1024, '\n');
cout << "잘못된 입력" << endl;
}
else if (input == 0)
{
cout << "0이 눌려 종료" << endl;
system("pause");
break;
}
else
return input;
}
}
void GameStart(MODE _eMode, int _sleeptime)
{
const int mode = (int)_eMode * 5;
int* ptr = new int [mode]; // 동적할당 : 실행중에 크기를 확인하고 그때에 맞춰서 크기를 지정해주는 방식
//int* ptr2 = new int;
switch (_eMode)
{
case MODE::EASY:
cout << "EASY" << endl;
break;
case MODE::NORMAL:
cout << "NORMAL" << endl;
break;
case MODE::HARD:
cout << "HARD" << endl;
break;
}
for (int i = 0; i < mode; ++i)
{
ptr[i] = rand() % 100 + 1;
cout << ptr[i] << ' ';
//둘이 같은거다.
//*(ptr + i) = rand();
Sleep(_sleeptime);
}
system("cls");
cout << "숫자를 입력해라." << endl;
int inp;
for (int i = 0; i < mode; ++i)
{
cin >> inp;
if (ptr[i] != inp)
{
cout << "틀림." << endl;
delete[] ptr;
return;
}
}
cout << "다맞춤" << endl;
// 메모리 leak (메모리 누수)
//delete ptr2;
delete[] ptr;
}
별로 어려운 내용없이 만들 수 있습니다.
그러면 이제 시간 맞추기 게임을 만들어 보겠습니다.
시간 맞추기 게임
이 게임을 만들기 위해서는 _getch와 _kbhit, time_t, '\r'를 알아야합니다.
_getch와 _kbhit
_getch와 _kbhit는 각각 getchar와 keyboardhit를 뜻합니다.
뜻 그대로
_getch는 아무 한글자나 입력을 받는 함수이고
_kbhit는 키보드 입력이 되었는지만 판단하는 함수입니다.
또한 둘다 무엇을 입력했는지 보여주지 않습니다.
둘다 #include <conio.h> 를 써줘야지 사용 가능합니다.
time_t
time_t는 1970년 1월 1일 00:00:00으로부터 지금까지 몇초가 지났는지를 가지고 있는 타입입니다.
이때 time_t t = time(NULL);이라는 코드를 쓴다면 time_t에는 몇 초가 지났지는지 값이 담길것입니다.
'\r'
'\r'은 커서를 현재줄 맨 앞으로 이동시키는 이스케이프 문자입니다.
이를 이용해서 커서를 맨 앞으로 이동시킬 수 있습니다.
그러면 이를 이용해서 시간 맞추기 게임을 만들어보겠습니다.
#include <iostream>
#include <conio.h>
#include <Windows.h>
using namespace std;
void Init();
void GameStart();
void RenderCount();
int main()
{
Init();
while (true)
{
GameStart();
cout << "게임을 그만 두시겠습니다?(Yes = 1 / N) " << endl;
char ch = _getch();
//if (ch == 'y' || ch == 'Y')
if(ch - '0' == 1)
break;
// char는 아스키 코드로 변환되므로 0 = 48, 1 = 49 ...이다.
// 이때 '0'은 48이므로 지금 입력받은 단어가 1인지 아닌지를 확인하려면 '0'이나 48을 빼면 된다.
}
}
void Init()
{
cout << "================================" << endl;
cout << "|\t 시간 맞추기 게임\t|" << endl;
cout << "================================" << endl;
cout << "설명: 마음속에 10초를 세고 눌러보세요." << endl;
cout << "=============================================================" << endl;
cout << "아무 키나 누르세요." << endl;
cout << "=============================================================" << endl;
}
void GameStart()
{
char ch = _getch();
RenderCount();
cout << endl << "시작 ! " << endl;
time_t startTime, endTime, elapsedTime;
startTime = time(NULL); // 현재 시간 저장
while (true)
{
if (_kbhit()) // 키보드 입력이 생기면 break;
break;
else
{
}
}
endTime = time(NULL); // 클릭되었을때 시간 저장
elapsedTime = endTime - startTime; // 두 시간에 차이를 저장
if (elapsedTime == 10)
cout << "10초 맞춤" << endl;
else if (elapsedTime > 10)
cout << "10초 지남" << endl;
else if (elapsedTime < 10)
cout << "10초 안지남" << endl;
}
void RenderCount()
{
for (int i = 0; i < 3; ++i)
{
// 이스케이프 문자
cout << '\r' << 3 - i << "...";
Sleep(1000);
// 이때 만약 2...이 입력될 차례에 3...이 써져있더라도
// 콘솔창은 3...위에 2...을 덮어씌우기 때문에 3...이 지워진것처럼 보인다.
}
}
이렇게 한다면 시간 맞추기 게임도 만들 수 있습니다.
'게임프로그래밍(C++)' 카테고리의 다른 글
[게임프로그래밍] 수행 대비 (0) | 2025.04.13 |
---|---|
[게임프로그래밍] 아스키 아트와 사운드 (0) | 2025.04.03 |
[게임프로그래밍] 메모리와 빌드 (0) | 2025.04.01 |
[게임프로그래밍] 난수와 시간2 (0) | 2025.03.23 |
[게임프로그래밍] 난수와 시간1 ⭐ (0) | 2025.03.19 |