개요
앞으로 주구장창 하게 될 얘기일지도 모르겠지만 게임 프로그래밍 시간은 특징이 하나 있습니다.
'이번엔 이것을 하겠습니다.' 라고 하고선 딱 그것만 하지 않아요.
수업 중간중간에 자잘한 것들을 자주 배우고 그것을 이 글의 제목에 대주제와 함께 녹여낼 능지가 저에겐 없습니다.
오늘은 메모리와 빌드에 대해 배웠다고 적고있지만 사실 메모리와 빌드만 배운게 아닙니다.
콘솔창에 관해서도 배웠는데 아직 미미한 수준이기에 메인으로 배운 메모리와 빌드를 대주제로 삼은 것이죠.
라고... 적었었는데 콘솔 분량이 생각보다 너무 길어지더군요.
콘솔과 메모리와 빌드는 분리하겠습니다.
콘솔
콘솔창에서 입력을 받으려면 cin나 scanf() 같은 것들을 사용합니다.
하지만 저희가 앞으로 만들게임은 텍스트 게임에 국한되지 않고 봄버맨 (진짜임 어케함) 같은 게임을 만들것입니다.
여기서 알아야 할것은 cin이나 scanf 같은 것들이 실시간 입력을 지원하지 않는다는 것 입니다.
이게 무슨 말이냐?
우리가 움직일떄 WASD를 쓴다고 가정해봅시다.
그러면 위로 움직이기 위해 W를 입력하고 cin이나 scanf에게 무엇을 입력했는지 알려주기 위해
Enter를 눌러야 하겠죠?
그렇게 되면 굉장히 경박해집니다.
제가 만약 술래잡기 같은 게임을 하고 있다고 한다면 술래에게서 도망치기 위해
A Enter
A Enter
A Enter
A Enter
S Enter 과 같은 쓸데 없는 Enter를 하염없이 눌러야 하겠죠?
이런것을 방지하기 위해 conio.h 라는 헤더에선 getch()와 kbhit()이라는 함수를 지원합니다.
getch()는 내가 무슨 키를 눌렀는지에 대해 int로 반환하고
kbhit()은 내가 키를 눌렀는지의 여부만을 반환합니다.
둘 다 반환형은 int지만 getch는 알잘딱 char로 사용하고 kbhit은 알잘딱 bool로 사용 하면 됩니다.
아, 참고로 둘 다 버전이 올라가면서 보안성에 문제가 생겨서 함수명 앞에 언더바를 붙인 최신형 함수를 사용해야 합니다.
_kbhit((), _getch() 처럼요.
저희는 이 함수를 사용하기 위해서 시간 맞추기 게임을 만들기로 했습니다.
대충 게임을 시작한 시점부터 10초를 세고 플레이어는 시간이 얼마나 경과했는지 10초에 가장 가깝게 찍어 맞추는 게임입니다.
일단 시작은 아무 키나 눌렀을때 가능하게 해야 합니다.
while(true)
{
if(_kbhit())
break;
}
이런식으로 그냥 입력이 들어오면 넘어가게 하면 끝입니다.
그리고 3, 2, 1 카운트를 세고 게임을 시작합시다.
근데 여기서 3,2,1 카운트를 셀때 해야할 과제가 하나 있었는데 이건 뒤에 서술하겠습니다.
아무튼 이제 시작한 시간을 제고 입력이 한번 더 들어왔을때 끝나는 시간을 재서 차를 구하면 끝이죠?
int startTime = time(NULL);
while(true)
{
if(_kbhit())
break;
}
int endTime = time(NULL);
cout << endTime - startTime;
근데 이렇게 되면 입력하지 않았는데도 입력이 들어가서 게임이 바로 끝나버립니다.
그 이유는 _kbhit()는 버퍼를 남기기 때문입니다.
_kbhit()를 사용할때 우리가 입력한 값은 대체 어디로 가겠습니까?
저희가 _kbhit()를 사용하며 입력한 키보드 값은 입력 버퍼로 이동했고 _kbhit()은 뒤처리가 깔끔하지 않습니다.
입력 버퍼를 지워주지 않는다는 뜻이죠.
이런식으로 s를 입력하면 _kbhit()은 true를 반환하고 사라집니다.
buffer에 's'를 남기고선 말이죠.
이게 대체 뭐가 문제냐구요?
엄밀히 말하자면 _kbhit()은 입력 버퍼가 비었는지 확인하는 함수와 같습니다.
그 전에 _kbhit()을 이미 한번 호출해서 채워져 있는 버퍼를 다음에 호출된 _kbhit()가 보고선 어? 입력이 들어왔네? 하면서 그냥 넘겨버린다는 뜻이죠.
그렇다면 어떻게 해결 할 수 있을까요?
저같은 경우는 "다시 생각해보니 블로그에서 빼먹은 cin이 고장났을때를 대비한 대처법과 예외처리"로 해결하려고 했습니다.
간단하게 설명하자면 cin이 고장나는 경우는 예를 들어 int 자료형의 입력을 받으려고 하는데
'a' 라던가 이상한 문자열이 들어올때 발생합니다.
cin.ignore()
그땐 버퍼를 비우고
cin.clear()
정신 좀 차리라고 좀 후려치면 애가 정신을 차려요.
물론 저같은 경우엔 cin으로 입력을 받은것이 아니지만 버퍼가 여러개는 아닐거라 생각하며 사용해보기로 했습니다.
#include<iostream>
#include<Windows.h>
#include<conio.h>
using namespace std;
int main()
{
cout << "시간 맞추기 게임!" << endl;
cout << "아무키나 누르세요." << endl;
while (true)
{
if (_kbhit())
break;
}
for (int i = 3; i > 0; i--)
{
cout << '\r' << i << "..."; // \r : Carriage return
Sleep(1000);
}
cout << " 시작!!!" << endl;
int startTime = time(NULL);
cin.ignore(); // _kbhit이 한번 더 실행되면 바로 true를 반환하는데 아마 버퍼에 값이 남아있어서 그런것 같다.
// 그렇기 때문에 cin.ignore()로 버퍼를 지워줌.
while (true)
{
if (_kbhit())
break;
}
int endTime = time(NULL);
int result = endTime - startTime;;
result == 10 ? cout << "정확" : result < 10 ? cout << "빠름" : cout << "느림";
}
코드 일부분만 따오기 그래서 그냥 전문을 가져왔습니다.
해결이 이래도 되긴 합니다.
하지만 _getch()또한 알아보기로 했습니다.
_getch()는 _kbhit()과 달리 깨끗한 친구입니다.
입력이 들어오고 나면 입력이 들어온 키를 알려주고 입력 버퍼까지 비워줍니다.
그렇기 때문에 _getch()를 먼저 사용하고 그 다음에 _kbhit()을 사용할 경우 깨끗하게 비워져 있는 버퍼로 인해 멀쩡하게 작동한다는 뜻이죠.
예제코드는 제가 만들다가 먹어버렸습니다.
사실 원래 이런지는 모르겠지만 일반 문자열을 누르면 그냥 입력되고 엔터나 스페이스바만 되길래 제가 코드를 맞게 적었는지에 대한 확신이 없어서 못 올려요...
콘솔창 렌더링
너무 거창한 소제목을 적었습니다.
감당따위 될리가 있겠습니까? 제가 배운건 개념 한 두개 뿐입니다.
제가 숫자 카운트 다운을 출력할때의 조건은 3,2,1을 똑같은 위치에 출력해야 하며 깜빡여서는 안된다는 조건이였습니다.
system("cls")를 사용하면 깜빡였기 때문에 머릿속에 저거밖에 없던 저는 당연히 과제를 해오지 못했습니다.
답은 Carrage Return이라고 불리는 개행 문자와 비슷한 '\r'이였습니다.
대충 뜻은 번역기 돌려도 캐리지 반환이라고 뜨기 때문에 모르겠습니다.
이 문자는 입력되는 순간 지금 출력되는 줄의 가장 처음 칸으로 이동합니다.
그래서 무슨 상관이냐구요?
콘솔은 이미 무언가 적혀있던 칸에 다시 한번 적으려 할 경우 그 칸 위에 적으려고 하는 것을 덮어 씌웁니다.
이러한 특성으로 인해서
for (int i = 3; i > 0; i--)
{
cout << '\r' << i << "..."; // \r : Carriage return
}
이런식으로 3... 2... 1...을 똑같은 자리에 적을 수 있는 겁니다.
마치는 말
이거 쓰는데 2시간 걸렸습니다.
마무리 단계중에 친구가 갑자기 웹페이지를 꺼버려서 큰일날 뻔 했으나 저에겐 임시저장 이라는 기능이 있었죠.
다음 글은 메모리와 빌드입니다. 이게 C++에서 가장 중요하다고 해요
수업은 열심히 들었지만...
뇌로 이해하고 글로 다시 쓰기는 세상에서 가장 어렵습니다.
'개발 > C++' 카테고리의 다른 글
게임 프로그래밍 | C++ | 메모리 구조 (0) | 2024.04.17 |
---|---|
게임 프로그래밍 | C++ | 열거형 (1) | 2024.04.10 |
자료구조 | C++ | 클래스 (1) | 2024.04.05 |
자료구조/게임 프로그래밍 | C++ | 함수, 포인터 (1) | 2024.03.28 |
게임 프로그래밍 | C++ | 랜덤 (2) | 2024.03.22 |