<토이프로젝트>

[C++ 게임] 리듬 게임 만들기 (1/4)

BlockDMask 2019. 12. 27. 11:31
반응형

안녕하세요. BlockDMask 입니다. (롤체 너무 재미있다.)

C++ 콘솔 게임 만들기 시리즈 그 두번째 게임은 리듬게임 입니다.

다섯개 정도 게임을 만들었었네요.

여러분이 생각하시는 그 위에서 아래로 떨어지는 리듬게임은 아니구요.

딱히 이름이 생각이 안나서 리듬게임이라고 지었습니다.

상, 하, 좌, 우 화살표가 나오면 해당 화살표에 맞게 입력을 하고 스페이스를 누르면 다음 탄으로 넘어가는 형태의 리듬게임입니다.

총 네단계로 이루어져있습니다.

1. 화면만들기, 화면전환, 커서 입력받기

2. 입력 출력, 문제 맞추기, 생명력

3. 레벨 세팅, 음악재생, 플레이 타입 계산


**모든 함수를 설명하는것은 불필요하다 생각해서, 중요하다 생각하는 함수와 로직만 설명하고 넘어가겠습니다.

혹시 이해가 안가는 부분은 댓글로 남겨주시면 답변드리겠습니다.


오늘은 그 첫번째 시간 입니다. C++ 리듬게임 만들기 시작하겠습니다.


<목차>

0. 게임 설명 및 이번시간 요약

1. 커서 이동을 위한 gotoxy 함수

2. 입력을 받는 _getch() 함수 사용방법

3. 위아래 이동 판단 로직

4. C++ 리듬게임 첫번째 시간 전체 소스코드


0. 게임 설명 및 이번시간 요약


- 이 게임은 윈도우기반 C++ 콘솔에서 실행되는 게임입니다.

- 상, 하, 좌, 우 화살표가 화면에 출력되면 유저가 똑같이 상, 하, 좌, 우 에 맞게 입력해서 스페이스를 눌러 맞았는지 틀렸는지 확인하고 다음 탄으로 넘어가는 게임 입니다.


- 문제 : ← ↑ ↑ ↓ →

문제가 이런식으로 나오면 우리는 똑같이 왼쪽, 위, 위, 아래, 오른쪽 순으로 키보드 입력을 하는 게임입니다.


- 이번시간은 여기까지 배웁니다.


1. gotoxy() 함수


▶ C/C++ gotoxy 함수

#include<Windows.h>

우리가 만든 프로젝트를 실행하게 되면 창이 하나 나오게 되는데요, 일반적으로 윈도우 창을 따로 실행시키지 않는이상 콘솔창이 나오게 됩니다.

콘솔 창에 깜빡깜빡 하는 " _ " 이렇게 생긴것을 커서라고 하는데요, 이 커서를 우리가 창 안에서 자유자재로 위치시키기 위한 함수 입니다.

정리 : gotoxy 함수는 커서를 특정 위치로 이동시키기 위한 함수 입니다.


void gotoxy(int x, int y)
{
    COORD Pos;        //x, y를 가지고 있는 구조체
    Pos.X = 2 * x;
    Pos.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), Pos);
}
cs


gotoxy 함수를 정의 할때 매개변수 x, y를 받도록 합니다. 우리가 사용할떄는 1,1로 가고싶다 이럴때 gotoxy(1,1) 이런식으로 사용할수 있도록 말이죠.

COORD 라는 구조체는 x, y 위치를 가지고 있는 구조체 입니다. 이 구조체 변수를 만들어서 x와 y를 채워 넣어 줍니다. 

2*x를 넣어주는 이유는 커서가 1씩 이동하는것보다 2씩 이동하는게 더 자연스러워 보이기 때문에 2*x를 넣어줍니다.

SetConsoleCursorPosition 함수는 이름에서 알수 있듯이 콘솔의 커서 위치를 알려주는 함수 입니다.

첫번째 매개변수로는 화면 출력 핸들러  GetStdHandle(STD_OUTPUT_HANDLE)를 넣어주고 

두번째 매개변수로는 위치를 나타내는 COORD 타입 변수인 Pos를 넣어줍니다.

첫번째 매개변수에는 을 넣습니다. STD_OUTPUT_HANDLE은 윈도우 베이스의 기본 출력을 뜻하는 매크로 이고, 그 매크로에 해당하는 핸들러를 GetStdHandle()로 받아옵니다. 이거는 그냥 "아 출력용 핸들러를 가지고 오는거구나" 하면 될것같습니다.

이렇게 커서를 이동시키는 함수를 완성했습니다.



2. 키보드 입력을 가지고 오는 _getch() 함수


▶ C/C++ _getch() 함수

#include<conio.h>

_getch() 함수는 사용자가 입력한 키에 대한 값을 아스키코드로 반환해주는 함수 입니다. (int 타입으로)

우리는 이걸 이용해서 사용자가 입력한 것에대한 것을 읽고 프로그램은 그에 맞는 동작을 하도록 할 것 입니다.


먼저 알아두어야 할 것은 아스키 코드 표를 보면 키보드 상하좌우에 대한 값이 나와있지 않습니다. (두둥.)

그럼 _getch()에서 키보드 화살표를 인식하지 못하는 걸까요?

아니겠죠..?


키보드에 입력이 →←↓↑ 키들이 오면 아스키 코드표에서는 표기할 수 없기 때문에 특별히 _getch()가 두번 출력을 하는데요.

224 를 뱉고 그 다음에 해당 키에 맞는 숫자를 뱉습니다.

정리하면 이렇습니다.

↑ : 224 -> 72

← : 224 -> 75

→ : 224 -> 77

↓ : 224 -> 80


코드에서 쉽게 사용하기 위해서 프로젝트에서는 enum과 메크로로 선언해두었습니다.

#define MAGIC_KEY 224    //상하좌우 화살표가 들어올때 선행되어 들어오는 숫자
#define SPACE 32        //스페이스 키 값
enum KEYBOARD
{
    UP = 72,
    LEFT = 75,
    RIGHT = 77,
    DOWN = 80
};
cs


3. 메뉴에서 위아래 이동 판단 로직


키보드 입력을 받는 _getch() 함수와 커서를 이동시키는 gotoxy 함수를 이용해서 메뉴판 위아래로 움직이는 로직을 짜보자.


MENU ReadyGame()
{
    int y = 0;                //커서의 y위치
    int input = 0;            //키보드 입력을 받을 변수
    while (true)            //게임 루프
    {
        DrawReadyGame();    //준비화면 그리기
        
        //-------------------
        //DrawUserCursor 함수
        if (y <= 0)        //커서가 위로 그만 올라가게 
        {
            y = 0;
        }
        else if (y >= 2//커서가 아래로 그만 내려가게
        {
            y = 2;
        }
 
        gotoxy(98 + y); //위치조정
        cout << ">";
        //-------------------
 
        input = _getch();    //키보드 입력을 받았다.
        //→←↑↓
        if (input == MAGIC_KEY)    //받았는데 224네?
        {
            switch (_getch())    //그럼 한번더 받아보자 상하좌우 일수 있으니.
            {
            case UP:            //위
                --y;
                break;
            case DOWN:            //아래
                ++y;
                break;
            }
        }
    }
}
cs


자 함수를 살펴보면 y가 이제 커서의 위치를 나타내는 곳이고, gotoxy로 위치를 계속 받고 있습니다.

그리고 _getch()를 이용해서 y의 위치를 ++, --하는걸 볼 수 있습니다.



4. C++ 리듬게임 첫번째 시간 전체 코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// [행맨 게임] by. BlockDMask
// [PART1] 화면만들기, 화면전환, 커서 입력.
#include<iostream>
#include<Windows.h>    //커서이동
#include<conio.h>    //_getch()
using namespace std;
 
#define MAGIC_KEY 224    //상하좌우 화살표가 들어올때 선행되어 들어오는 숫자
#define SPACE 32        //스페이스 키 값
 
enum MENU
{
    GAMESTART = 0,
    INFO,
    QUIT
};
 
enum KEYBOARD
{
    UP = 72,
    LEFT = 75,
    RIGHT = 77,
    DOWN = 80
};
 
//콘솔 커서 이동
void gotoxy(int x, int y)
{
    COORD Pos;        //x, y를 가지고 있는 구조체
    Pos.X = 2 * x;
    Pos.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), Pos);
}
 
//콘솔 크기, 타이틀
void SetConsoleView()
{
    system("mode con:cols=50 lines=20");        //가로 50, 세로20
    system("title DanceDance. By BlockDMask");    //타이틀 정하기
}
 
//-----------Draw-----------------
//준비 화면 그리기
void DrawReadyGame()
{
    system("cls");    //화면을 클리어 해주는 함수 입니다.
    gotoxy(52);
    cout << "******************************";
    gotoxy(53);
    cout << "*        Dance Dance         *";
    gotoxy(54);
    cout << "******************************";
    gotoxy(108);
    cout << "GameStart";
    gotoxy(109);
    cout << "GameInfo";
    gotoxy(1010);
    cout << "Quit" << endl;
}
cs
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
//정보 화면 그리기
void DrawInfoGame()
{
    system("cls");
    gotoxy(13);
    cout << "*******************************************";
    gotoxy(14);
    cout << "|Developer - BlockDMask.";
    gotoxy(15);
    cout << "|Blog - https://blockdmask.tistory.com/";
    gotoxy(16);
    cout << "|Insta - @BlockDMask";
    gotoxy(17);
    cout << "|follow my instagram. Thank you.";
    gotoxy(18);
    cout << "|블로그 많은 방문 부탁드립니다";
    gotoxy(19);
    cout << "*******************************************";
    gotoxy(110);
    cout << "|Music - https://www.youtube.com/HYPMUSIC";
}
 
//시작 화면 그리기
void DrawStartGame()
{
    system("cls");
    gotoxy(21);
    cout << "*******************************************";
    gotoxy(43);
    cout << "Life : ";
    gotoxy(44);
    cout << "Score : ";
    gotoxy(48);
    cout << "Q : ";
    gotoxy(410);
    cout << "A : ";
    gotoxy(412);
    cout << "SPACE!를 눌러야 답이 제출이 됩니다.";
    gotoxy(218);
    cout << "*******************************************" << endl;
 
    system("pause>null");
}
 
//게임 오버 그리기
void DrawGameOver()
{
    gotoxy(88);
    cout << "-------------------";
    gotoxy(89);
    cout << "| G A M E O V E R |";
    gotoxy(810);
    cout << "-------------------";
    system("pause>null");
}
 
//커서 움직이는것 출력
void DrawUserCursor(int& y)
{
    if (y <= 0)        //커서가 위로 그만 올라가게 
    {
        y = 0;
    }
    else if (y >= 2//커서가 아래로 그만 내려가게
    {
        y = 2;
    }
 
    gotoxy(98 + y); //위치조정
    cout << ">";
}
 
//-----------Func-----------------
MENU ReadyGame()
{
    int y = 0;                //커서의 y위치
    int input = 0;            //키보드 입력을 받을 변수
    while (true)            //게임 루프
    {
        DrawReadyGame();    //준비화면 그리기
        
        //-------------------
        //DrawUserCursor 함수
        if (y <= 0)        //커서가 위로 그만 올라가게 
        {
            y = 0;
        }
        else if (y >= 2//커서가 아래로 그만 내려가게
        {
            y = 2;
        }
 
        gotoxy(98 + y); //위치조정
        cout << ">";
        //-------------------
 
        input = _getch();    //키보드 입력을 받았다.
        //→←↑↓
        if (input == MAGIC_KEY)    //받았는데 224네?
        {
            switch (_getch())    //그럼 한번더 받아보자 상하좌우 일수 있으니.
            {
            case UP:            //위
                --y;
                break;
            case DOWN:            //아래
                ++y;
                break;
            }
        }
        else if (input == SPACE) //키보드 입력 받았는데 스페이스네?
        {
            switch (y)            //y위치에 따라 판단
            {
            case 0:
                return GAMESTART;
            case 1:
                return INFO;
            case 2:
                return QUIT;
            }
        }
    }
}
 
//게임 정보 뷰
void InfoGame()
{
    DrawInfoGame();
    system("pause>null");
}
 
//게임 시작 뷰
void StartGame()
{
    DrawStartGame();
    DrawGameOver();
}
 
//메인 루프
int main(void)
{
    SetConsoleView();    //프로그램 시작시에 콘솔 크기
    while (true)
    {
        switch (ReadyGame())    //리턴을 받아서 판단.
        {
        case GAMESTART:
            StartGame();
            break;
        case INFO:
            InfoGame();
            break;
        case QUIT:
            return 0;
        }
    }
    return 0;
}
cs


감사합니다. 이번시간에는 C++ 리듬게임 1탄으로 기본 화면을 세팅해봤습니다.

어렵진 않으시죠? 충분히 분석해보신 후에도 코드가 이해가지 않는다면 댓글로 질문 남겨주세요.

반응형