/* Push Push Ver 1.0 */ #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <conio.h> #include <iostream> using namespace std; const int WIDTH = 10; const int HEIGHT = 10; const int TOTAL_STAGE = 10; const int SZ_HISTORY = 999; enum e_MoveDirection { UP, DOWN, RIGHT, LEFT }; enum e_Object { EMPTY, WALL, HART, BOX, PLAYER = 5, GOAL }; struct _Environment { COORD currentPlayerPos; int stage; int step; int tmSec; // 스테이지 경과 시간 int totalScore; // 총 점수 DWORD tmStart; // 스테이지 시작 시간. 경과 시간을 구하는데만 사용. BOOL pause; // 스레드 동작을 멈출 때 사용. BOOL restart; // 스레드를 다시 시작할 때 사용. BOOL quit; // 스레드를 종료할 때 사용. } Env; struct _GoalInfo{ int numOfGoal; COORD arrLocateOfGoal[99]; } GoalInfo; typedef struct { int stage; int score; } ScoreSet; HANDLE hMutex; // Mutex 핸들 HANDLE hEvent; // Event 핸들 HANDLE hThread; // Thread 핸들 DWORD dwThreadID; // Thread 아이디 int g_TableOfMap[HEIGHT][WIDTH]; // 맵 테이블 char *aTile[] = {" ", "▨", "♡", "⊙", "☆", ". "}; /* " " = EMPTY "▨" = WALL "♡" = HART "⊙" = BOX "☆" = PLAYER ". " = GOAL */ void gotoxy(int, int); //------------------------------------- // main.h //------------------------------------- class CHighScore { public: CHighScore(); void GetHighScore(ScoreSet); void SetHighScore(ScoreSet); void ShowHighScore(); private: char *filename; ScoreSet S; }; class CDrawScreen { public: CDrawScreen(int, int); void DrawMenu(); void DrawBackground(); void DrawBoard(BOOL visible = TRUE); void DrawScoreTable(); void ShowClearEvent(); COORD GetBoardPos() { return boardPos; }; private: int defDrawPosX; int defDrawPosY; RECT backgroundRect; COORD menuPos; COORD boardPos; COORD scoreTablePos; }; class CStage { public: void StageLoad(); void StageEnd(); BOOL NewStage(); BOOL CheckStageClear(); }; class CMoveRec { public: CMoveRec(); void SavePath(int, int, BOOL); void Undo(LPVOID); private: struct { COORD pos; BOOL bWithBox; } MoveInfo[SZ_HISTORY]; int MoveIdx; }; class CMoveMap { public: BOOL Move(e_MoveDirection); void MarkObject(int, int, e_Object); void RemoveObject(int, int); BOOL isEmpty(int, int); BOOL isGoal(int, int); void Undo(); private: CMoveRec MoveRec; }; class CPushPush { public: CPushPush( int x = 3, int y = 3, int stage = 1 ) : DrawScreen(x, y) { Env.stage = stage; hMutex = CreateMutex(NULL, FALSE, NULL); // 뮤텍스를 생성한다. } ~CPushPush() { ReleaseMutex(hMutex); // 뮤텍스를 반환한다. } void InitializeGame(); void StartMenu(); void StartGame(); void PauseGame(int, int); BOOL QuitGame(int, int); private: CStage Stage; CDrawScreen DrawScreen; CMoveMap MoveMap; CHighScore HighScore; BOOL KeyProcess(); friend DWORD WINAPI ThreadProc(LPVOID lpParam); }; //--------------------------------------------- // CPushPush.cpp //--------------------------------------------- void CPushPush::InitializeGame() { // 스레드를 생성한다. hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); hThread = CreateThread(NULL, 0, ThreadProc, (LPVOID)this, 0, &dwThreadID); if(hThread == NULL) { gotoxy(1, 1); cout << "CreateThread failed." << endl; exit(1); } // STD_OUT의 커서를 숨긴다. CONSOLE_CURSOR_INFO CurInfo; CurInfo.dwSize = 1; CurInfo.bVisible = FALSE; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &CurInfo); } void CPushPush::StartMenu() { char slct = 0; while(slct != 3) { DrawScreen.DrawMenu(); if(slct = _getch()) { switch(slct) { case '1': StartGame(); break; case '2': HighScore.ShowHighScore(); break; case '3': Env.pause = TRUE; Env.quit = TRUE; SetEvent(hEvent); // 스레드에 종료 시그널을 보낸다. WaitForSingleObject(hThread, INFINITE); // 스레드가 종료될 때까지 기다린다. CloseHandle(hThread); // 핸들을 닫는다. CPushPush::~CPushPush(); // 클래스 파괴자 임의 호출 gotoxy(1, 1); exit(1); default: break; } } } } void CPushPush::StartGame() { // 배경 화면을 그린다. DrawScreen.DrawBackground(); // 모든 스테이지를 클리어 할 때까지 새 스테이지를 읽어들인다. while(Stage.NewStage()) { while(Stage.CheckStageClear() != TRUE) { DrawScreen.DrawBoard(); DrawScreen.DrawScoreTable(); if(KeyProcess()) { // QuitGame()에서 '1. 메인 메뉴로'가 선택되면. // 메인 메뉴로 돌아간다. return; } if(Env.pause) { Env.pause = FALSE; Stage.NewStage(); } } DrawScreen.DrawBoard(); DrawScreen.DrawScoreTable(); DrawScreen.ShowClearEvent(); ++(Env.stage); // 다음 스테이지 } } BOOL CPushPush::KeyProcess() { int ch = 0; if(ch = _getch()) { //ch = ((97 <= ch) && (ch <= 122))?ch-=32:ch; switch(ch) { case 72: // UP MoveMap.Move(UP); break; case 80: // DOWN MoveMap.Move(DOWN); break; case 75: // LEFT MoveMap.Move(LEFT); break; case 77: // RIGHT MoveMap.Move(RIGHT); break; case 32: // SPACE break; case 'p': // P Env.pause = TRUE; DrawScreen.DrawBoard(FALSE); PauseGame(static_cast<int>((DrawScreen.GetBoardPos().X + WIDTH)/2) + 4, static_cast<int>((DrawScreen.GetBoardPos().Y + HEIGHT)/2) + 1); Env.pause = FALSE; break; case 'q': // Q Env.pause = TRUE; DrawScreen.DrawBoard(FALSE); if(QuitGame(static_cast<int>((DrawScreen.GetBoardPos().X + WIDTH)/2) + 9, static_cast<int>((DrawScreen.GetBoardPos().Y + HEIGHT)/2) - 2)) { // QuitGame() 에서 '1. 메인 메뉴로'가 선택되었다. // TRUE 를 리턴한다. return TRUE; } Env.pause = FALSE; break; case 'r': // R MoveMap.Undo(); break; case 's': // S Env.pause = TRUE; cin >> Env.stage; break; case 'z': // Z Env.pause = TRUE; DrawScreen.DrawBoard(FALSE); cin >> Env.stage; break; default: break; } } return FALSE; } void CPushPush::PauseGame(int nx, int ny) { gotoxy(nx, ny); cout << "P A U S E" << endl; _getch(); } BOOL CPushPush::QuitGame(int nx, int ny) { char ch = 0; gotoxy(static_cast<int>(nx / 2), 4 + static_cast<int>(ny / 2)); cout << "1. 메인 메뉴로" << endl; gotoxy(static_cast<int>(nx / 2), 5 + static_cast<int>(ny / 2)); cout << "2. 게임 종료 " << endl; gotoxy(static_cast<int>(nx / 2), 6 + static_cast<int>(ny / 2)); cout << "3. 취소" << endl; ch = _getch(); switch(ch) { case '1': // 1. 메인 메뉴로 Env.restart = TRUE; // 스레드에 다시 시작 시그널을 보낸다. Env.stage = 1; return TRUE; case '2': // 2. 게임 종료 Env.quit = TRUE; // 스레드에 종료 시그널을 보낸다. WaitForSingleObject(hThread, INFINITE); // 스레드가 종료될 때까지 기다린다. CloseHandle(hThread); // 핸들을 닫는다. CPushPush::~CPushPush(); // 클래스 파괴자 임의 호출 gotoxy(1, 1); exit(1); case '3': // 3. 취소 return FALSE; } return FALSE; } //-------------------------------------------- // CHighScore.cpp //--------------------------------------------- /* Space-Separated Values files 기반의 푸시푸시 최고점수 파일 사양에 대한 스펙 1. 파일의 정의 각 레코드는 ' ' 화이트 스페이스에 의해 구분된다. 라인의 경계는 캐리지 리턴 라인 피드로 결정된다. For example: 111 222 333 CRLF 777 888 999 CRLF 2. 각 레코드의 의미 첫 10줄은 최고 점수와 완료 스테이지를 1위부터 10위까지 기억한다. rank(1-10) totalScore stage 다음 줄은 TOTAL_STAGE 에 정의된 스테이지의 개수 만큼 입력되며 각 스테이지의 최소 스텝과 최소 경과시간을 기억한다. stage(1-TOTAL_STAGE) step time For example: 1 23604 10 2 24000 10 3 17050 7 ... 10 607 3 11 23 5 12 53 20 ... 20 231 120 */ CHighScore::CHighScore() { int i; filename = "highscore.txt"; FILE *fp = fopen(filename, "r"); // highscore.txt 파일을 읽기 모드로 연다. if(fp == NULL) // 만약 파일이 없으면 초기화된 최고 점수 파일을 만든다. { fp = fopen(filename, "w"); for(i = 1; i <= 10 + TOTAL_STAGE; ++i) { fprintf(fp, "%d 0 0\n", i); } } fclose(fp); // 파일을 닫는다. } void CHighScore::ShowHighScore() { int nx = 8; int ny = 3; int n1, n2, n3; // 파일 라인 임시 저장 변수. int i; FILE *fp = fopen(filename, "r"); // highscore.txt 파일을 읽기 모드로 연다. system("cls"); gotoxy(nx,ny++); cout << "▤▤▤▤▤▤▤▤▤▤▤▤▤▤"; gotoxy(nx,ny++); cout << "▤ HIGH SCORE ▤"; gotoxy(nx,ny++); cout << "▤▤▤▤▤▤▤▤▤▤▤▤▤▤"; gotoxy(nx,ny++); cout << "▤ RANK SCORE STAGE ▤"; for(i = 0; i < 10; ++i) { gotoxy(nx,ny); cout << "▤ ▤"; fscanf(fp, "%d %d %d", &n1, &n2, &n3); gotoxy(nx+5,ny++); printf("%-2d %8d %5d\n", n1, n2, n3); } gotoxy(nx,ny++); cout << "▤▤▤▤▤▤▤▤▤▤▤▤▤▤"; _getch(); fclose(fp); // 파일을 닫는다. } //--------------------------------------------- // CDrawScreen.cpp //--------------------------------------------- CDrawScreen::CDrawScreen(int x, int y) : defDrawPosX(x), defDrawPosY(y) { // 배경화면의 초기위치 backgroundRect.left = x; backgroundRect.top = y; backgroundRect.right = 25; backgroundRect.bottom = 16; // 메뉴의 초기위치 menuPos.X = x + 7; menuPos.Y = y + 5; // 게임 화면의 초기위치 boardPos.X = x + 2; boardPos.Y = y + 1; // 스코어 테이블의 초기위치 scoreTablePos.X = x + 25; scoreTablePos.Y = y + 1; } void CDrawScreen::DrawMenu() { // 메뉴의 초기 위치를 입력한다. int nx = menuPos.X; int ny = menuPos.Y; // 전체 화면을 지운다. system("cls"); // 메뉴를 출력한다. gotoxy(nx, ny++); cout << "1. 게임 시작"; gotoxy(nx , ny++); cout << "2. 최고 점수"; gotoxy(nx , ny++); cout << "3. 게임 종료"; } void CDrawScreen::DrawBackground() { // 배경화면의 초기위치를 입력한다. int nx = backgroundRect.left; int ny = backgroundRect.top; int i,j; // 전체 화면을 지운다. system("cls"); // 배경화면을 그린다. for(i = 0; i < backgroundRect.bottom; ++i) { gotoxy(nx, ny++); for(j = 0; j < backgroundRect.right; ++j) { cout << "▒"; } } } void CDrawScreen::DrawBoard(BOOL visible) { // Mutex 를 얻는다. WaitForSingleObject(hMutex, INFINITE); // 게임 화면이 그려질 위치를 입력한다. int nx = boardPos.X; int ny = boardPos.Y; int i, j; // 게임 화면을 그린다. for(i = 0; i < HEIGHT; ++i) { gotoxy(nx, ny++); for(j = 0; j < WIDTH ; ++j) { if(visible == FALSE) { cout << aTile[0]; continue; } if(g_TableOfMap[i][j] == EMPTY) cout << aTile[0]; else if(g_TableOfMap[i][j] == WALL) cout << aTile[1]; else if(g_TableOfMap[i][j] == BOX) cout << aTile[3]; else if(g_TableOfMap[i][j] == PLAYER) cout << aTile[4]; else if(g_TableOfMap[i][j] == GOAL) cout << aTile[5]; else cout << aTile[2]; } cout << endl; } gotoxy(1, 1); // Mutex 를 반환한다. ReleaseMutex(hMutex); } void CDrawScreen::DrawScoreTable() { // Mutex 를 얻는다. WaitForSingleObject(hMutex, INFINITE); // 스코어 테이블이 그려질 위치를 입력한다. int nx = scoreTablePos.X; int ny = scoreTablePos.Y; gotoxy(nx , ny++); cout << "P u s h P u s h Ver1.0 "; ++ny; gotoxy(nx + 6, ny); cout << " "; // 갱신되는 부분을 지운다. gotoxy(nx , ny); cout << "Stage " << Env.stage; gotoxy(nx + 14 , ny); cout << "최고점수"; ++ny; ++ny; gotoxy(nx + 6, ny); cout << " "; // 갱신되는 부분을 지운다. gotoxy(nx , ny); cout << "Step : " << Env.step; gotoxy(nx + 18 , ny); cout << " "; // 갱신되는 부분을 지운다. gotoxy(nx + 14 , ny); cout << "Step: 0"; ++ny; gotoxy(nx + 6 , ny); cout << " "; // 갱신되는 부분을 지운다. gotoxy(nx , ny); cout << "Time : " << Env.tmSec; gotoxy(nx + 18 , ny); cout << " "; // 갱신되는 부분을 지운다. gotoxy(nx + 14 , ny); cout << "Time: 0"; ny+=2; gotoxy(nx , ny++); cout << "방향키 : 이동 "; gotoxy(nx , ny++); cout << "R : 되돌리기, P : 멈춤"; gotoxy(nx , ny++); cout << "Z : 다시시작, Q : 종료 "; gotoxy(1, 1); // Mutex 를 반환한다. ReleaseMutex(hMutex); } void CDrawScreen::ShowClearEvent() { // 클리어 화면을 보여준다. int nx = static_cast<int>((defDrawPosX + WIDTH)/2) + 2; int ny = static_cast<int>((defDrawPosY + HEIGHT)/2); int i, j; Env.pause = TRUE; DrawScoreTable(); for(i = 1; i <= HEIGHT; ++i) { gotoxy(defDrawPosX + 2, defDrawPosY + i); for(j = 0; j < WIDTH; ++j) { cout << ". "; Sleep(25); } cout << endl; } gotoxy(nx, ny); cout << "Stage " << Env.stage << " clear!"; ny+=3; gotoxy(nx, ny++); cout << "Step " << Env.step; gotoxy(nx, ny++); cout << "Time " << Env.tmSec; gotoxy(nx, ny++); cout << "Point " << Env.tmSec * Env.step; _getch(); } //--------------------------------------------- // CStage.cpp //--------------------------------------------- BOOL CStage::NewStage() { if(10 < Env.stage) { return FALSE; } Env.restart = TRUE; StageLoad(); return TRUE; } void CStage::StageLoad() { Env.step = 0; // STEP 초기화 Env.pause = FALSE; // PAUSE 초기화 Env.restart = FALSE; // RESTART 초기화 Env.quit = FALSE; // QUIT 초기화 Env.tmSec = 0; // 경과시간 초기화 GoalInfo.numOfGoal = 0; // GOAL 개수 초기화 int i, j; char fLine[WIDTH+1] = { '0' }; // stageN.txt 파일의 한 라인을 입력받기 위한 임시 변수. char str[15] = { '0' }; // sprintf 를 위한 임시 변수. sprintf(str, "stage%d.txt", Env.stage); // buff[] 에 stageN.txt 를 입력한다. FILE *fp = fopen(str, "r"); // 파일을 연다. if(fp == NULL) // 만약 파일 열기가 실패하면 종료한다. { cout << "! File open ERROR." << endl;; exit(1); } for(i = 0; i < HEIGHT; ++i) { fscanf(fp, "%s", fLine); // 파일에서 한 라인을 스트링으로 읽어들인다. for(j = 0; j < WIDTH; ++j) { g_TableOfMap[i][j] = static_cast<int>(fLine[j]) - 48; // 스트링을 정수 토큰으로 분리한다. if(g_TableOfMap[i][j] == PLAYER) { /* 로딩한 스테이지 토큰이 플레이어라면 현재 좌표를 플레이어의 초기 좌표로 입력한다. */ Env.currentPlayerPos.X = j; Env.currentPlayerPos.Y = i; } else if(g_TableOfMap[i][j] == GOAL) { /* 로딩한 스테이지 토큰이 GOAL 이라면 arrLocateOfGoal 배열에 좌표를 저장한다. */ GoalInfo.arrLocateOfGoal[GoalInfo.numOfGoal].X = j; GoalInfo.arrLocateOfGoal[GoalInfo.numOfGoal++].Y = i; } } } fclose(fp); // 파일을 닫는다. SetEvent(hEvent); } BOOL CStage::CheckStageClear() { // 클리어 조건을 만족하는지 확인하고 아니면 FALSE 를 리턴한다. for(int i = 0; i < GoalInfo.numOfGoal; ++i) { if(g_TableOfMap[GoalInfo.arrLocateOfGoal[i].Y][GoalInfo.arrLocateOfGoal[i].X] != BOX) return FALSE; } return TRUE; } //--------------------------------------------- // CMoveMap.cpp //--------------------------------------------- BOOL CMoveMap::Move(e_MoveDirection t) { int mx = 0; int my = 0; BOOL bWithBox = FALSE; // 이동 할 방향으로의 X, Y 증감값을 입력한다. switch(t) { case UP: mx = 0; my = -1; break; case DOWN: mx = 0; my = 1; break; case LEFT: mx = -1; my = 0; break; case RIGHT: mx = 1; my = 0; break; default: cout << "Wrong Move Type." << endl; return FALSE; } // 이동 할 방향이 박스일 경우 박스 다음 타일이 빈칸인지 판정한다. if(g_TableOfMap[Env.currentPlayerPos.Y + my][Env.currentPlayerPos.X + mx] == BOX) { if(isEmpty(Env.currentPlayerPos.X + (mx * 2), Env.currentPlayerPos.Y + (my * 2))) { // 박스 다음이 빈칸이면 현재 박스를 지우고 그 앞에 그린다. RemoveObject(Env.currentPlayerPos.X + mx, Env.currentPlayerPos.Y + my); MarkObject(Env.currentPlayerPos.X + (mx * 2), Env.currentPlayerPos.Y + (my * 2), BOX); bWithBox = TRUE; } else { return FALSE; } } // 이동 할 방향이 박스가 아닐 경우 빈칸인지 판정한다. else { if(!isEmpty(Env.currentPlayerPos.X + mx, Env.currentPlayerPos.Y + my)) { // 빈칸이 아니면 아무것도 하지 않고 리턴한다. return FALSE; } } // 현재 플레이어를 지우고 위치를 이동시켜 그린다. RemoveObject(Env.currentPlayerPos.X, Env.currentPlayerPos.Y); Env.currentPlayerPos.X += mx; Env.currentPlayerPos.Y += my; ++(Env.step); MarkObject(Env.currentPlayerPos.X, Env.currentPlayerPos.Y, PLAYER); MoveRec.SavePath(mx, my, bWithBox); return TRUE; } void CMoveMap::Undo() { MoveRec.Undo(this); } BOOL CMoveMap::isEmpty(int x, int y) { if(g_TableOfMap[y][x] == EMPTY || g_TableOfMap[y][x] == GOAL) return TRUE; // 빈칸 혹은 GOAL 이면 TRUE 를 리턴 else return FALSE; // 빈칸이 아니면 FALSE 를 리턴 } void CMoveMap::RemoveObject(int x, int y) { // 맵핑 테이블 특정 위치를 빈칸으로 만든다. g_TableOfMap[y][x] = EMPTY; for(int i = 0; i < GoalInfo.numOfGoal; ++i) { // 빈칸이 GOAL 이면 GOAL 을 입력한다. if(GoalInfo.arrLocateOfGoal[i].X == x && GoalInfo.arrLocateOfGoal[i].Y == y) { g_TableOfMap[y][x] = GOAL; break; } } } void CMoveMap::MarkObject(int x, int y, e_Object t) { // 맵핑 테이블에 전달받은 타입의 오브젝트를 입력한다. switch(t) { case EMPTY: g_TableOfMap[y][x] = EMPTY; break; case BOX: g_TableOfMap[y][x] = BOX; break; case PLAYER: g_TableOfMap[y][x] = PLAYER; break; case WALL: g_TableOfMap[y][x] = WALL; break; case GOAL: g_TableOfMap[y][x] = GOAL; break; default: break; } } //--------------------------------------------- // CMoveRec.cpp //--------------------------------------------- CMoveRec::CMoveRec() { // 이동 정보를 초기화 한다. MoveIdx = 0; for(int i = 0; i < SZ_HISTORY; ++i) { MoveInfo[i].pos.X = 0; MoveInfo[i].pos.Y = 0; MoveInfo[i].bWithBox = FALSE; } } void CMoveRec::SavePath(int x, int y, BOOL bStatus) { MoveInfo[MoveIdx].pos.X = x; // 이동한 X 값. MoveInfo[MoveIdx].pos.Y = y; // 이동한 Y 값. MoveInfo[MoveIdx].bWithBox = bStatus; // 박스를 밀어서 이동했는지 여부. ++MoveIdx; // 인덱스 증가. } void CMoveRec::Undo(LPVOID lpParam) { CMoveMap *pThis = (CMoveMap*)lpParam; // CPushPush 의 this 포인터를 받는다. if(MoveIdx == 0) { // 기억된 이동 정보가 없으면 아무것도 하지 않고 리턴한다. return ; } // 박스와 같이 이동했으면 현재 박스를 지우고 이전 위치에 그린다. if(MoveInfo[--MoveIdx].bWithBox == TRUE) { pThis->RemoveObject(Env.currentPlayerPos.X + MoveInfo[MoveIdx].pos.X, Env.currentPlayerPos.Y + MoveInfo[MoveIdx].pos.Y); pThis->MarkObject(Env.currentPlayerPos.X, Env.currentPlayerPos.Y, BOX); } // 플레이어만 이동했으면 플레이어를 지운다. else { pThis->RemoveObject(Env.currentPlayerPos.X, Env.currentPlayerPos.Y); } Env.currentPlayerPos.X -= MoveInfo[MoveIdx].pos.X; // 플레이어의 위치를 이동시킨다. Env.currentPlayerPos.Y -= MoveInfo[MoveIdx].pos.Y; // 플레이어의 위치를 이동시킨다. ++(Env.step); // 스텝 증가. pThis->MarkObject(Env.currentPlayerPos.X, Env.currentPlayerPos.Y, PLAYER); } //--------------------------------------------- // ThreadProc.cpp //--------------------------------------------- DWORD WINAPI ThreadProc(LPVOID lpParam) { CPushPush *pThis = (CPushPush*)lpParam; // CPushPush 의 this 포인터를 받는다. int onTime = 0; int pauseStartTm = 0; int pauseOnTm = 0; int pauseTm = 0; WaitForSingleObject(hEvent, INFINITE); // 게임이 시작될 때 까지 기다린다. /* 경과시간을 초 단위로 저장하며, 1초에 한 번씩 DrawScoreTable()을 호출한다. */ Env.tmStart = GetTickCount(); // 시작 시간을 저장한다. while(1) { // 만약 현재 상태가 PAUSE 라면 PAUSE 시간을 저장하고 동작을 멈춘다. if(Env.pause) { pauseStartTm = GetTickCount(); // PAUSE 가 시작한 시간을 저장한다. while(Env.pause) // PAUSE 가 해제될 때까지 루프한다. { if(Env.restart) { // 다시 시작할 때까지 기다린다. ResetEvent(hEvent); // 시간설정을 초기화하고 다시 시작한다. onTime = 0; pauseStartTm = 0; pauseOnTm = 0; pauseTm = 0; Env.tmStart = GetTickCount(); Env.restart = FALSE; break; } if(Env.quit) { // 이벤트를 보낸다. SetEvent(hThread); // 스레드를 종료한다. ExitThread(0); } pauseOnTm = GetTickCount() - pauseStartTm; // PAUSE 의 경과시간을 구한다. } pauseTm += pauseOnTm; // 현재 멈춘 시간을 총 멈춘 시간에 더한다. } // 1초 단위로 경과 시간을 올린다. Env.tmSec = static_cast<int>((GetTickCount() - pauseTm - Env.tmStart) / 1000.0); if(Env.tmSec > onTime){ // 시간에 변화가 생기면 pThis->DrawScreen.DrawScoreTable(); // 스코어 테이블을 다시 그린다. ++onTime; // 다음 1초를 설정한다. } } return 0; } //--------------------------------------------- // main.cpp //--------------------------------------------- int main() { CPushPush PushPush; PushPush.InitializeGame(); PushPush.StartMenu(); return 0; } void gotoxy(int x, int y) { COORD Pos = {x-1, y-1}; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), Pos); }