ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C++ - Mini Project - Snake game
    Project/Service 2020. 12. 21. 01:59

    Snake game

     

    Jungle 1주 차가 끝나고 2주 차도 절반가량이 지났다.

    다음 주 주말에 Doodle 점프 게임을 만들어 보기 위해서 더욱 간단한 Snake game을 만들면서 연습해 보았다.

    이번 주말에 만든 게임은 정말 간단한 형태이지만, 시도했고, 시작했다는 것에 큰 의미가 있는 것 같다.

     

    snake game

    콘솔 창에서 글씨를 출력하고, 삭제하고 다시 출력하는 방식으로 한 프레임 한 프레임을 구현했다.

     

    전형적인 Snake Game과 동일하게 '*'로 표시되는 아이템을 먹을 때 마다 뱀의 길이가 길어진다.

    w, a, d, s로 상하좌우 방향을 이동할 수 있으며, 뱀이 자기 자신에 부딪힐 때 게임이 종료된다.

     

    코드가 어떻게 구성되는지 알아보자.

     

     

    main

     

    main function은 아래와 같이 Setup(), 그리고 while문 내에 Draw(), Input(), Logi()으로 구성된다.

    int main() {
    	Setup();
    	while (!gameOver) {
    		Draw();
    		Input();
    		Logic();
    		//Sleep(10); // slow down
    	}
    	return 0;
    }

     

    Setup

     

    먼저 Setup에서는 초기 설정을 하게 된다.

    게임이 진행되는 맵의 가로, 세로 길이, 과일의 위치, 진행 방향등을 세팅한다. 

    사용자가 방향키를 누르기 이전까지는 뱀이 출발하지 않도록, 초기 진행방향은 STOP으로 설정해 두었다. 이에 대한 추가 설정은 Logic()에서 설명하도록 하겠다.

     

    void Setup() {
    	gameOver = false;
    	eDirection dir = STOP;
    	x = width / 2;
    	y = height / 2;
    	fruitX = rand() % width +1;
    	fruitY = rand() % height;
    	score = 0;
    }

     

    Draw

     

    Setup을 마쳤으면, while문 안으로 들어가 게임이 종료되기 전까지 반복하게 된다. while문을 한 번 돌 때마다, 화면을 지우고 새로운 뱀의 위치, 과일의 위치, 점수 등을 다시 출력한다.(그래서 게임을 실행할 때 깜빡임이 심하다.)

     

    #으로 위아래, 양옆 벽을 세우고, 뱀의 머리는 O, 꼬리들은 o로 표시한다. 아이템이 위치하는 곳은 *로 표시해준다.

     

    void Draw() {
    	system("cls"); // 화면에 출력된 정보들을 지운다.
        
    	for (int i = 0; i < width+2; i++)
    		cout << "#";
    	cout << "\n";
    
    	for (int i = 0; i < height; i++) {
    		for (int j = 0; j < width+2; j++) {
    			if (j == 0) cout << "#";
    			if (j == width + 1) cout << "#";
    			if (i == y && j == x)
    				cout << "O"; // 뱀의 머리
    			else if (i == fruitY && j == fruitX)
    				cout << "*";
    			else {
    				bool print = false;
    				for (int k = 0; k < nTail; k++) {
    					if (tailX[k] == j && tailY[k] == i) {
    						cout << "o"; // 뱀의 꼬리
    						print = true;
    					}
    				}
    				if (!print)
    					cout << " ";
    			}
    		}
    		cout << "\n";
    	}
    	for (int i = 0; i < width + 2; i++)
    		cout << "#";
    	cout << "\n";
    	cout << "\n";
    	cout << "Score : " << score << "\n";
    	cout << "fruit coordinate : " << fruitX << ", " << fruitY;
    }

     

    Input

     

    #include <conio.h>를 선언하고, _kbhit()과 _getch()를 사용해 사용자의 입력을 받아온다.

     

    • _kbhit() - 키보드 입력 여부를 체크한다.
    • _getch() - 사용자가 무슨 키를 눌렀는지 입력받는다. 

    w,s,a,d에 따라 dir변수에 상하좌우 값을 넣어주고, x를 눌렀을 때는 gameOver변수에 true를 저장해, while문을 탈출시키고 종료할 수 있도록 한다.

     

    void Input() {
    	if (_kbhit()) { // w, as, d
    		switch (_getch()) {
    		case 'a':
    			dir = LEFT;
    			break;
    		case 'd':
    			dir = RIGHT;
    			break;
    		case 'w':
    			dir = UP;
    			break;
    		case 's':
    			dir = DOWN;
    			break;
    		case 'x':
    			gameOver = true;
    			break;
    		}
    	}
    }

     

     

     

    Logic

     

    Input에서 처리한 dir방향으로 뱀을 이동시킨다.

    이때, 벽을 만나면 반대편 벽으로 다시 빠져나오도록 설정되어 있다.

    뱀의 머리가 꼬리에 닿으면, 게임이 종료되며, 과일을 먹으면 score가 올라가고 꼬리가 한 칸 길어진다.

     

    void Logic() {
    	//꼬리
    	int prevX = tailX[0];
    	int prevY = tailY[0];
    	int prev2X, prev2Y;
    	tailX[0] = x;
    	tailY[0] = y;
    	for (int i = 1; i < nTail; i++) {
    		prev2X = tailX[i];
    		prev2Y = tailY[i];
    		tailX[i] = prevX;
    		tailY[i] = prevY;
    		prevX = prev2X;
    		prevY = prev2Y;
    	}
    	//머리
    	switch (dir) {
    	case LEFT:
    		x--;
    		break;
    	case RIGHT:
    		x++;
    		break;
    	case UP:
    		y--;
    		break;
    	case DOWN:
    		y++;
    		break;
    	default:
    		break;
    	}
        //벽을 만나면 게임 종료
    	/*if (x > width || x < 0 || y > height || y < 0)
    		gameOver = true;*/
        //벽을 만나면 반대편 벽으로 통과되어 나온다.
    	if (x >= width+1) x = 0; else if (x < 0) x = width - 1;
    	if (y >= height) y = 0; else if (y < 0) y = height - 1;
    	//뱀의 꼬리에 머리가 닿으면 게임 종료
    	for (int i = 0; i < nTail; i++)
    		if (tailX[i] == x && tailY[i] == y)
    			gameOver = true;
        //과일을 먹으면 점수를 올리고, 꼬리가 길어지며, 과일을 다시 배치한다.
    	if (x == fruitX && y == fruitY) {
    		score += 10;
    		fruitX = rand() % width + 1;
    		fruitY = rand() % height;
    		nTail++;
    	}
    }

     

    Code

    #include <iostream>
    #include <conio.h>
    #include <Windows.h>
    using namespace std;
    bool gameOver;
    const int width = 20;
    const int height = 15;
    int x, y, fruitX, fruitY, score;
    int tailX[100], tailY[100];
    int nTail;
    enum eDirection {STOP = 0, LEFT, RIGHT, UP, DOWN};
    eDirection dir;
    
    
    void Setup() {
    	gameOver = false;
    	eDirection dir = STOP;
    	x = width / 2;
    	y = height / 2;
    	fruitX = rand() % width +1;
    	fruitY = rand() % height;
    	score = 0;
    }
    void Draw() {
    	system("cls");
    	for (int i = 0; i < width+2; i++)
    		cout << "#";
    	cout << "\n";
    
    	for (int i = 0; i < height; i++) {
    		for (int j = 0; j < width+2; j++) {
    			if (j == 0) cout << "#";
    			if (j == width + 1) cout << "#";
    			if (i == y && j == x)
    				cout << "O";
    			else if (i == fruitY && j == fruitX)
    				cout << "*";
    			else {
    				bool print = false;
    				for (int k = 0; k < nTail; k++) {
    					if (tailX[k] == j && tailY[k] == i) {
    						cout << "o";
    						print = true;
    					}
    				}
    				if (!print)
    					cout << " ";
    			}
    		}
    		cout << "\n";
    	}
    	for (int i = 0; i < width + 2; i++)
    		cout << "#";
    	cout << "\n";
    	cout << "\n";
    	cout << "Score : " << score << "\n";
    	cout << "fruit coordinate : " << fruitX << ", " << fruitY;
    }
    void Input() {
    	if (_kbhit()) { // w, as, d
    		switch (_getch()) {
    		case 'a':
    			dir = LEFT;
    			break;
    		case 'd':
    			dir = RIGHT;
    			break;
    		case 'w':
    			dir = UP;
    			break;
    		case 's':
    			dir = DOWN;
    			break;
    		case 'x':
    			gameOver = true;
    			break;
    		}
    	}
    }
    void Logic() {
    	int prevX = tailX[0];
    	int prevY = tailY[0];
    	int prev2X, prev2Y;
    	tailX[0] = x;
    	tailY[0] = y;
    	for (int i = 1; i < nTail; i++) {
    		prev2X = tailX[i];
    		prev2Y = tailY[i];
    		tailX[i] = prevX;
    		tailY[i] = prevY;
    		prevX = prev2X;
    		prevY = prev2Y;
    	}
    
    	switch (dir) {
    	case LEFT:
    		x--;
    		break;
    	case RIGHT:
    		x++;
    		break;
    	case UP:
    		y--;
    		break;
    	case DOWN:
    		y++;
    		break;
    	default:
    		break;
    	}
    	/*if (x > width || x < 0 || y > height || y < 0)
    		gameOver = true;*/
    	if (x >= width+1) x = 0; else if (x < 0) x = width - 1;
    	if (y >= height) y = 0; else if (y < 0) y = height - 1;
    
    	for (int i = 0; i < nTail; i++)
    		if (tailX[i] == x && tailY[i] == y)
    			gameOver = true;
    	if (x == fruitX && y == fruitY) {
    		score += 10;
    		fruitX = rand() % width + 1;
    		fruitY = rand() % height;
    		nTail++;
    	}
    }
    int main() {
    	Setup();
    	while (!gameOver) {
    		Draw();
    		Input();
    		Logic();
    		//Sleep(10); // slow down
    	}
    	return 0;
    }

     

Designed by Tistory.