본문 바로가기

programming/내가 만들고 싶어서 만든 것!

C++을 활용한 게임 만들기

반응형

1. 개요

 

 행렬 Class를 사용해서 게임을 만들어 보았습니다 헤헿. 왜냐면 과제였기 때문이죠ㅎ. 사실 과제는 행렬을 사용한 프로그램 만들기였는데, 저는 그냥 게임을 만들었습니다. (사실 너무 허접해서 게임 같지도 않습니다......) 이차원 배열로 평면을 표현할 수 있다고 생각하고 전체적인 게임판은을 만들고 그 위에서 연산을 통해 캐릭터를 이동시키는...... 방법을 생각해보았습니다 ㅎㅎ 물론 완성도는 매우 낮으므로 욕만 하지 말아주세요 ㅎㅎ 굳이 행렬을 왜 Class로 만드느냐! 그냥 C++문법 연습도 하고 나름 자료구조 처럼 이용해보고 싶었습니다......

 

2. 코드 구성

 

- 행렬 Class 구현

 

 다음은 제가 작성한 행렬 Class입니다. 그냥 이 게임에서 필요한 기능만을 만들어서 제가 작성한 Class만을 보고 "아, 이게 행렬 자료구조구나, 행렬 Class를 이렇게 짜면 되겠구나!" 하시고서 그대로 복붙해가시면 진짜 큰일 날 수 있습니다 ㅎㅎ 다시 한번 말씀드리지만 이건 정말 그냥 제가 만들고 싶어서 만든겁니다!

 

#include <cstdio>
#define MAX_SIZE 100

class Polynomial {
private:

	char poly[MAX_SIZE][MAX_SIZE];
	int max_row;
	int max_col;

public:

	Polynomial() { max_row = 0; max_col = 0; }	//초기화


	void read() {		//최댓값 입력 메소드

		printf("행의 최댓값을 입력하세요 (1 ~ 100): ");
		scanf("%d", &max_row);
		printf("열의 최댓값을 입력하세요 (1 ~ 100): ");
		scanf("%d", &max_col);

		printf("\n");

		for (int i = 0; i < max_row; i++) {
			for (int j = 0; j < max_col; j++) {
				poly[i][j] = 0;
			}
		}
	}

	void show_max() {		//최댓값을 작성하는 매소드
		printf("행의 최댓값 : %d\n열의 최댓값 : %d", max_row, max_col);
	}

	bool isEmpty() {				//행렬이 비어 있다면 1출력
		if (max_row == 0 && max_col == 0)return 1;
		else return 0;
	}

	bool isOver(int row, int col) {		//범위를 초가하면 1 반환
		if (row > max_row || col > max_col) return 1;
		else return 0;
	}

	void display() {			//행렬을 출력하는 함수

		if (isEmpty()) {
			printf("행렬이 비어있습니다.");
			return;
		}
	
		printf("\n");

		for (int i = 0; i < max_row; i++) {
			for (int j = 0; j < max_col; j++) 
				printf("%5c", poly[i][j]);
			printf("\n");
		}
		printf("\n");
	}

	void change(int row, int col, int value) {		//행렬의 값을 변환하는 함수
		if (isOver(row, col)) {
			printf("인덱스 범위가 맞지 않습니다.");
			return;
		}
		poly[row][col] = value;
	}

	int getRow() {		//행의 최댓값 반환
		return max_row;
	}

	int getCol() {		//열의 최댓값 반환
		return  max_col;
	}

	int getValue(int i, int j) {		// 해당 행렬에 해당하는 값 반환
		return poly[i][j];
	}
};

 

 그대로 갖출 메소드는 거의 갖췄다고 나름 생각하고 있습니다 ㅎㅎ 최댓값을 돌려주거나, 주어진 인덱스에 해당하는 값을 반환하거나, 해당 인댁스의 값을 바꿔는 메소드가 주로 구성되어있습니다. 주석이 달려있으니 한 번 살펴보셔도 나쁘지 않을 듯 합니다. 다시 한번 말씀드리지만 제가 게임을 만들 때 필요한 메소드를 기준으로 작성하였기에 이게 정답은 절대 아닙니다!

 

- main() 함수 구현

 

 그래도 그럴듯한 게임을 만들어야 한다고 생각하기에, 게임 시작, 게임 설명, 게임 종료를 선택할 수 있는 시작 화면을 만들어야겠다고 생각했습니다.

 

int main(void) {

	srand((unsigned int)time(0));
	Polynomial map;
	int input;

	while (input = main_menu()) {
		if (input == 1) {
			reset_map(&map);
			int now_row = 0;
			int now_col = 0;
			do {
				show_map(map);
			} while (move(&map, &now_row, &now_col));
		}
		else if(input == 2) tutorial();
		else reset_message("올바른 키를 입력해주세요!");
	}
	reset_message("이용해주셔서 감사합니다!");
	return 0;
}

 

아마 첫 실행 화면을 보면 위에 코드가 이해될 것입니다.

 

첫 실행 화면

 위의 실행결과를 보면 알겠지만 1을 입력하면 게임을 시작하고 2를 입력하면 게임 방법에 대해 설명하고 3을 입력하면 게임을 종료시키는 기능을 구성하였습니다. 2을 누르면 tutorial() 함수를 실행하게 되는데 이 함수의 코드는 아래와 같습니다.

 

- tutorial() 함수 구현

 

//튜토리얼 출력 함수
void tutorial() {

	system("cls");
	printf("----------------------------------------------\n\n");

	printf("방향키 입력 방법\n\n");
	printf("w: 위로이동\n");
	printf("a: 왼쪽으로 이동\n");
	printf("s: 아래로 이동\n");
	printf("d: 오른쪽으로 이동\n\n");

	printf("게임 방법\n\n");
	printf("몬스터를 피해 목표까지 도달하세요!\n");
	printf("몬스터 위치 표시 : M\n");
	printf("목표의 위치 표시 : 0\n");
	printf("자신의 위치 표시 : H\n");

	printf("\n인지하셨다면 아무 키나 눌러주세요!");
	
	printf("\n\n----------------------------------------------");

	char trash = getche();
	return;


}

 

 어려운 문법 없이 그저 단순히 출력만을 구현한 함수입니다. 조금 특별한 점이 있다면 아무 키를 받아, 바로 return해줌으로써 함수를 종료시킨다는 점입니다.

 

- 게임에 관한 함수 구현

 

 다음은 말 그대로 게임 진행에 필요한 알고리즘입니다. 원리를 설명하자면, 입력받은 키에 따라서 위치를 바꿔주고 한번 입력받을 때마다 게임 판 전체를 다시 출력하여 마치 키보드에 따라 게임 캐릭터의 위치가 바뀌는 것처럼 보이도록 구현하였습니다. 다음은 게임에 관한 함수들 입니다.

 

//게임판 출력
void show_map(Polynomial map) {

	printf("\n\n");
	system("cls");
	for (int i = 0; i < map.getRow() * 5; i++)
		printf("-");

	map.display();
	for (int i = 0; i < map.getRow() * 5; i++)
		printf("-");
}

//위치를 바꿔주는 알고리즘
void position(Polynomial *map, int row, int col) {
	(*map).change(row,col,'H');
}

//이동하려는 곳이 게임판의 범위를 넘어가면 1반환
bool is_overRange(Polynomial map, int row, int col) {
	if (map.getRow() > row && map.getCol() > col && row > -1 && col > -1) return 0;
	else return 1;
}

//방향키를 입력받아 이동하는 함수
bool move(Polynomial *map, int *now_row, int *now_col) {

	char dirction;
	dirction = getche();

	int buff_row = *now_row;
	int buff_col = *now_col;

	switch (dirction) {

	case 'w': buff_row--;
		break;

	case 'a': buff_col--;
		break;

	case 's': buff_row++;
		break;

	case 'd': buff_col++;
		break;

	default: show_message(*map, "올바른 키를 입력해주세요!"); return 1;
		break;
	}

 

 하나의 Class로 정의된 행렬 안에서 진행하기 때문에 포인터적으로 어려운 일은 없었습니다. 조금 신경써야 할 부분은 맵을 벗어나는 방향키를 입력하였거나 올바르지 않은 방향키를 입력하였을 때의 오류 처리 였습니다.

 

- 전체 코드

 

 다음은 제가 만든 게임의 전체 코드입니다.

 

#include "Polynomial.h"
#include <cstdio>
#include <windows.h>
#include <ctime>
#include <conio.h>

using namespace std;

bool move(Polynomial *map, int *now_row, int *now_col);
void reset_map(Polynomial *map);
bool is_End(Polynomial map, int x, int y);
bool is_Monster(Polynomial map, int x, int y);
void show_map(Polynomial map);
void position(Polynomial *map, int row, int col);
bool is_overRange(Polynomial map, int row, int col);
void show_message(Polynomial map, const char *mes);
int main_menu();
void tutorial();
void reset_message(const char *mes);

int main(void) {

	srand((unsigned int)time(0));
	Polynomial map;
	int input;

	while (input = main_menu()) {
		if (input == 1) {
			reset_map(&map);
			int now_row = 0;
			int now_col = 0;
			do {
				show_map(map);
			} while (move(&map, &now_row, &now_col));
		}
		else if(input == 2) tutorial();
		else reset_message("올바른 키를 입력해주세요!");
	}
	reset_message("이용해주셔서 감사합니다!");
	return 0;
}

//이동하려는 자리에 monser가 있을 시 1반환
bool is_Monster(Polynomial map, int x, int y) {
	return map.getValue(x,y) == 'M';
}

//이동하려는 자리가 목적지면 1 반환
bool is_End(Polynomial map, int x, int y) {
	return map.getRow() - 1 == x && map.getCol() - 1 == y;
}

//게임판 설정
void reset_map(Polynomial* map) {

	int max_monster;

	system("cls");
	(*map).read();

	for (int i = 0; i < (*map).getRow(); i++) {
		for (int j = 0; j < (*map).getCol(); j++) 
			(*map).change(i, j, '.');
	}

	//몬스터의 갯수를 입력받고 랜덤으로 자리를 설정
	printf("난이도를 입력해주세요 (0 ~ %d)",(*map).getRow() + (*map).getCol());
	scanf("%d", &max_monster);

	for (int i = 0; i < max_monster; i++) {

		int x = rand() % (*map).getRow();
		int y = rand() % (*map).getCol();

		if (!is_Monster((*map), x, y) && !is_End((*map), x, y))(*map).change(x, y, 'M');
		else i--;
	}
	(*map).change((*map).getRow() - 1, (*map).getCol() - 1, '0');
	position(map, 0, 0);
}

//게임판 출력
void show_map(Polynomial map) {

	printf("\n\n");
	system("cls");
	for (int i = 0; i < map.getRow() * 5; i++)
		printf("-");

	map.display();
	for (int i = 0; i < map.getRow() * 5; i++)
		printf("-");
}

//위치를 바꿔주는 알고리즘
void position(Polynomial *map, int row, int col) {
	(*map).change(row,col,'H');
}

//이동하려는 곳이 게임판의 범위를 넘어가면 1반환
bool is_overRange(Polynomial map, int row, int col) {
	if (map.getRow() > row && map.getCol() > col && row > -1 && col > -1) return 0;
	else return 1;
}

//방향키를 입력받아 이동하는 함수
bool move(Polynomial *map, int *now_row, int *now_col) {

	char dirction;
	dirction = getche();

	int buff_row = *now_row;
	int buff_col = *now_col;

	switch (dirction) {

	case 'w': buff_row--;
		break;

	case 'a': buff_col--;
		break;

	case 's': buff_row++;
		break;

	case 'd': buff_col++;
		break;

	default: show_message(*map, "올바른 키를 입력해주세요!"); return 1;
		break;
	}

	if (is_Monster(*map, buff_row, buff_col)) {
		show_message(*map, "Game Over");
		return 0;
	}

	if (is_End(*map, buff_row, buff_col)) {
		show_message(*map, "성공!");
		return 0;
	}

	if (!is_overRange(*map, buff_row, buff_col)) {
		(*map).change(*now_row, *now_col, '.');
		*now_row = buff_row;
		*now_col = buff_col;
		(*map).change(*now_row, *now_col, 'H');
	}
	else show_message(*map,"범위를 벗어났습니다.");

	return 1;

}

//입력받은 메세지를 출력하는 함수
void show_message(Polynomial map, const char *mes) {

	system("cls");

	for (int i = 0; i < map.getRow() * 5; i++)
		printf("-");

	printf("\n\n");
	printf("%s",mes);
	printf("\n\n");

	for (int i = 0; i < map.getRow() * 5; i++)
		printf("-");

	Sleep(3000);
}

//시작 화면을 출력하고 메뉴를 입력받아 해당 값을 반환하는 함수
int main_menu() {

	while (1) {

		system("cls");
		printf("----------------------------------------------\n\n");
		printf("1. 게임 시작");
		printf("\t2. 게임 방법");
		printf("\t3. 게임 종료");
		printf("\n\n----------------------------------------------");

		char input = getche();
		//int buff = static_cast<int>(input);

		switch (input) {

		case '1': return 1;
			break;

		case '2': return 2;
			break;

		case '3': return 0;
			break;

		default: return 4;
		}
	}
}

//튜토리얼 출력 함수
void tutorial() {

	system("cls");
	printf("----------------------------------------------\n\n");

	printf("방향키 입력 방법\n\n");
	printf("w: 위로이동\n");
	printf("a: 왼쪽으로 이동\n");
	printf("s: 아래로 이동\n");
	printf("d: 오른쪽으로 이동\n\n");

	printf("게임 방법\n\n");
	printf("몬스터를 피해 목표까지 도달하세요!\n");
	printf("몬스터 위치 표시 : M\n");
	printf("목표의 위치 표시 : 0\n");
	printf("자신의 위치 표시 : H\n");

	printf("\n인지하셨다면 아무 키나 눌러주세요!");
	
	printf("\n\n----------------------------------------------");

	char trash = getche();
	return;


}

//최댓값이 없을 때 메세지를 출력할 수 있는 함수
void reset_message(const char *mes) {

	system("cls");
	printf("----------------------------------------------\n\n");
	printf("%s",mes);
	printf("\n\n----------------------------------------------\n\n");
	Sleep(3000);
}

 

3. 결어 및 보완할점

 

 물론 그런 사람은 없겠지만...... 게임을 혹시 진행해보신 분들은 아시겠지만, 판의 입력 범위에 따라 cmd창을 초과하여 게임 판 전체가 깨지는 단점이 있습니다. 이 부분은 조금더 찾아서 연구를 해봐야 할 것같고..... 모든 움직임이 연산에 따라 진행되기 때문에 결국 컴퓨터의 기능에 따라 출력하는 화면이 그대로 보여지는 현상도 발생하였습니다....... (사실 cmd로 게임을 만드는 것이 말이 안될 수도 ㅠㅠ)

 

 코드를 쓰실 분들은 댓글이라도 남겨주세요 ㅠㅠ 저에게 큰 힘이 될 것같습니다. 어디가 잘못되어있는지 지적해주시는 댓글도 언제나 환영입니다!

반응형