新的开始——实现简易的弹幕游戏 Part 1

博主,打砖块的Part 3让你给吃了?????

非也,等介绍完easyX会把之前介绍的游戏一并重构的。

(图文无关,呃,这张是灵梦,你要说有关也行)


前置知识:在前面文章的基础上再加一个静态变量(C语言)

(请叫我重构狂人)(雾

说到弹幕游戏,相信很多人不会陌生。从小时候玩的雷电,到现在的东方project,等等,弹幕游戏一直都很经典。这次我要写一些关于弹幕游戏的讲解和笔记了。

·初始化二维数组画布以及画一个能动的飞机

我们设定游戏画面的高度是25,宽度是30(仅仅举个例子,实际上弹幕游戏的宽高度通常要比这大得多)

这样我们就会得到一个初始的程序:

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
60
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
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>

#define High 25
#define Width 30

int pos_x, pos_y;
int canvas[High][Width];

void gotoxy(int x, int y) {
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle, pos);
}

void HideCursor() {
CONSOLE_CURSOR_INFO cursor_info = { 1,0 };
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

void startup() {
memset(canvas, 0, sizeof(canvas));
pos_x = High / 2;
pos_y = Width / 2;
canvas[pos_x][pos_y] = 1;
for (int i = 0; i < High; i++) {
for (int j = 0; j < Width; j++) {
if (i == 0 || i == High - 1)
canvas[i][j] = -2;
else if (j == 0 || j == Width - 1)
canvas[i][j] = -1;
}
}
}

void show() {
gotoxy(0, 0);
for (int i = 0; i < High; i++) {
for (int j = 0; j < Width; j++) {
if (canvas[i][j] == 0)
printf(" ");
else if (canvas[i][j] == 1)
printf("*");
else if (canvas[i][j] == -1)
printf("|");
else if (canvas[i][j] == -2)
printf("-");
}
printf("\n");
}
}

void updateWithInput() {
char input;
if (_kbhit()) {
input = _getch();
if (input == 'a') {
canvas[pos_x][pos_y] = 0;
pos_y--;
canvas[pos_x][pos_y] = 1;
}
if (input == 's') {
canvas[pos_x][pos_y] = 0;
pos_x++;
canvas[pos_x][pos_y] = 1;
}
if (input == 'd') {
canvas[pos_x][pos_y] = 0;
pos_y++;
canvas[pos_x][pos_y] = 1;
}
if (input == 'w') {
canvas[pos_x][pos_y] = 0;
pos_x--;
canvas[pos_x][pos_y] = 1;
}

}
}
void updateWithoutInput() {


}

int main() {
startup();
HideCursor();
while (1) {
show();
updateWithInput();
updateWithoutInput();
}
return 0;
}

运行程序,发现有一个bug,飞机可以跑到边框之外。显然,我们没有加入范围判定的代码,现在加上。

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
void updateWithInput() {
char input;
if (_kbhit()) {
input = _getch();
if ((input == 'a') && (pos_y - 1 > 0)) {
canvas[pos_x][pos_y] = 0;
pos_y--;
canvas[pos_x][pos_y] = 1;
}
if ((input == 's') && (pos_x + 1 < High - 1)) {
canvas[pos_x][pos_y] = 0;
pos_x++;
canvas[pos_x][pos_y] = 1;
}
if ((input == 'd') && (pos_y + 1 < Width - 1)) {
canvas[pos_x][pos_y] = 0;
pos_y++;
canvas[pos_x][pos_y] = 1;
}
if ((input == 'w') && (pos_x - 1 > 0)) {
canvas[pos_x][pos_y] = 0;
pos_x--;
canvas[pos_x][pos_y] = 1;
}

}
}

这里尤其要注意,在飞机移动前一定要清楚原来位置上的飞机,不然会有重影bug,下边要说的子弹和敌机也要注意这个问题。

·发射子弹

只需要让canvas里某个格子的值为2,就可以认为是发射子弹。问题是在处理子弹的飞行问题上。

子弹怎么发射?

显然,应该显示子弹在飞机的正上方,然后考虑子弹的飞行。

子弹怎么飞?

这需要我们改动updateWithoutInput函数,因为子弹的飞行和用户的输入是无关的。

思路很简单,用一个二重循环扫一遍整个画布,如果检测到当前帧有子弹,那么把子弹上移一格,下一帧时就会显示一个已经移动的子弹。

1
2
3
4
5
6
7
8
9
10
11
12
	
void updateWithoutInput() {
for (int i = 0; i < High; i++) {
for (int j = 0; j < Width; j++) {
if (canvas[i][j] == 2) {
canvas[i][j] = 0;
if (i > 1) // 判断子弹是否出界
canvas[i - 1][j] = 2;
}
}
}
}

·设置敌机

略去只设置一个敌机的部分,我们直接设置大量敌机。

说下初期的设定:敌机会自动下落,击中敌机得1分,敌机下落至画面最底短扣1分,若敌机和自机相撞则游戏结束。

初始化敌机仍然是使用随机数。这里有一个小技巧,在updateWithoutInput函数里使用了静态变量speed控制敌机下落的速度,具体参见代码。

最后的代码是这样的:

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
60
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
	
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <time.h>

#define High 25
#define Width 30
#define Enemy_num 5

int pos_x, pos_y;
int canvas[High][Width];
int enemy_x[Enemy_num];
int enemy_y[Enemy_num];
int score;

void gotoxy(int x, int y) {
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle, pos);
}

void HideCursor() {
CONSOLE_CURSOR_INFO cursor_info = { 1,0 };
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

void startup() {
memset(canvas, 0, sizeof(canvas));
memset(enemy_y, 0, sizeof(enemy_y));
memset(enemy_x, 0, sizeof(enemy_x));
pos_x = High - 2;
pos_y = Width / 2;
canvas[pos_x][pos_y] = 1;
for (int i = 0; i < High; i++) {
for (int j = 0; j < Width; j++) {
if (i == 0 || i == High - 1)
canvas[i][j] = -2;
else if (j == 0 || j == Width - 1)
canvas[i][j] = -1;
}
}
srand((unsigned int)time(NULL));
score = 0;

for (int j = 0; j < Enemy_num; j++) {
enemy_x[j] = 1;
enemy_y[j] = (rand() % (Width - 1)) + 1;
}
}

void show() {
gotoxy(0, 0);
for (int i = 0; i < High; i++) {
for (int j = 0; j < Width; j++) {
if (canvas[i][j] == 0)
printf(" ");
else if (canvas[i][j] == 1)
printf("*");
else if (canvas[i][j] == -1)
printf("|");
else if (canvas[i][j] == -2)
printf("-");
else if (canvas[i][j] == 2)
printf("^");
else if (canvas[i][j] == 3)
printf("@");
}
printf("\n");
}
printf("score: %d\n", score);
}

void updateWithInput() {
char input;
if (_kbhit()) {
input = _getch();
if ((input == 'a') && (pos_y - 1 > 0)) {
canvas[pos_x][pos_y] = 0;
pos_y--;
canvas[pos_x][pos_y] = 1;
}
if ((input == 's') && (pos_x + 1 < High - 1)) {
canvas[pos_x][pos_y] = 0;
pos_x++;
canvas[pos_x][pos_y] = 1;
}
if ((input == 'd') && (pos_y + 1 < Width - 1)) {
canvas[pos_x][pos_y] = 0;
pos_y++;
canvas[pos_x][pos_y] = 1;
}
if ((input == 'w') && (pos_x - 1 > 0)) {
canvas[pos_x][pos_y] = 0;
pos_x--;
canvas[pos_x][pos_y] = 1;
}
if (input == ' ') {
canvas[pos_x - 1][pos_y] = 2;
}

}
}
void updateWithoutInput() {
for (int i = 0; i < High; i++) {
for (int j = 0; j < Width; j++) {
if (canvas[i][j] == 2) {

for (int k = 0; k < Enemy_num; k++) {
if ((i == enemy_x[k]) && (j == enemy_y[k])) {
score++; //更新得分
canvas[enemy_x[k]][enemy_y[k]] = 0; //消掉原敌机
enemy_x[k] = rand() % 2 + 1; //更新新敌机x坐标
enemy_y[k] = (rand() % (Width - 3)) + 2;//更新新敌机y坐标
canvas[enemy_x[k]][enemy_y[k]] = 3; //显示新敌机
canvas[i][j] = 0; //消掉子弹
}
}

canvas[i][j] = 0;
if (i > 1) // 判断子弹是否出界
canvas[i - 1][j] = 2;
}
}
}
static int speed = 0;
if (speed < 20)
speed++;
for (int k = 0; k < Enemy_num; k++) {
if ((pos_x == enemy_x[k]) && (pos_y == enemy_y[k])){
printf("GAME OVER\n");
Sleep(1000);
system("pause");
exit(0);
}
if (enemy_x[k] > High - 3) {
canvas[enemy_x[k]][enemy_y[k]] = 0;
enemy_x[k] = rand() % 2 + 1;
enemy_y[k] = (rand() % (Width - 3)) + 2;
canvas[enemy_x[k]][enemy_y[k]] = 3;
score--;
}
if (speed == 20) {
speed = 0;
for (int w = 0; w < Enemy_num; w++) {
canvas[enemy_x[w]][enemy_y[w]] = 0;
enemy_x[w]++;
canvas[enemy_x[w]][enemy_y[w]] = 3;
}
}
}
}

int main() {
startup();
HideCursor();
while (1) {
show();
updateWithInput();
updateWithoutInput();
}
return 0;
}

(说句题外话,博客刚改了主题,好多东西还没来得及弄,各位先将就着看吧,我慢慢搞。。。)


-------------本文结束,感谢您的阅读转载请注明原作者及出处-------------


本文标题:新的开始——实现简易的弹幕游戏 Part 1

文章作者:Shawn Zhou

发布时间:2018年08月06日 - 18:08

最后更新:2018年12月09日 - 13:12

原始链接:http://yoursite.com/2018/08/06/新的开始——实现简易的弹幕游戏-Part-1/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

知识无价,码字不易。对您有用,敬请打赏。金额随意,感谢关心。