前言:
飞机大作战游戏是一种非常受欢迎的射击类游戏,玩家需要控制一架战斗机在屏幕上移动,击落敌机以获得分数。本游戏使用C语言编写,旨在帮助初学者了解游戏开发的基本概念和技巧。
在开始编写代码之前,我们需要先了解一下游戏的基本规则和功能:
-
游戏界面:游戏界面是一个矩形区域,玩家可以在这个区域内控制战斗机移动和射击。
-
战斗机:玩家控制的战斗机可以在游戏界面内*移动,按下特定键可以发射子弹和开启技能击落敌机。
-
敌机:敌机会从屏幕的一侧出现,并沿着直线路径向另一侧移动。玩家需要击落敌机以获得分数。
-
分数:玩家每击落一架敌机,分数会增加。
-
游戏结束:当玩家飞机被敌机撞到到或者得分为0时,游戏结束。
接下来,我们将通过以下几个步骤来实现这个游戏:
-
初始化游戏界面和战斗机。
-
处理键盘输入,实现战斗机的移动和射击。
-
生成敌机,并控制其移动。
-
检测战斗机与敌机之间的碰撞,更新分数和技能充能值。
-
判断游戏是否结束。
通过学习这个游戏的开发过程,初学者将能够掌握C语言编程的基本技巧。
1. 打印菜单:
void menu()
{
printf("--------------飞机大作战--------------\n");
printf("| |\n");
printf("| 1.开始游戏 |\n");
printf("| 0.退出游戏 |\n");
printf("| W/A/S/D移动 |\n");
printf("| 空格射击 E/R技能 |\n");
printf("| |\n");
printf("--------------------------------------\n");
}
int main()
{
system("color b");
int input = 0;
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入有误,请重新输入:\n");
break;
}
return 0;
}
2. 写一个存放信息的数组
int arr[Col][Row] = { 0 };
3. 写一个打印数组信息的函数
飞机大作战的要素无非就是路、围墙、玩家飞机、敌机、子弹这五个元素,要想在打印出这几个元素,我们只需在写一个函数在二维数组中存放这个五个元素的信息(用0-4代替),然后再写一个打印信息的函数分别出二维数组中对应的信息。
void DisPlay(int arr[Col][Row])
{
gotoxy(0, 0);
for(int i=0;i<Col;i++)
{
for (int j = 0; j < Row; j++)
{
if (arr[i][j] == 0)
{
printf(" ");
}
if (arr[i][j] == 1)
{
printf("█");
}
if (arr[i][j] == 2)
{
printf("*");
}
if (arr[i][j] == 3)
{
printf("$");
}
if (arr[i][j] == 4)
{
printf("|");
}
}
printf("\n");
}
printf("得分:%d ", score);
printf("EnemySleep=%d ", EnemySleep);
printf("skill1(20)=%d ", skill1);
printf("skill2(5)=%d ", skill2);
Sleep(20);
}
4. 写一个函数来初始化数组的信息
void InSet(int arr[Col][Row])
{
//路--0
//墙--1
//玩家飞机--2
//敌机--3
//子弹--4
}
5. 初始化围墙
数组最边边的位置我们都让它等于1,这样就可以把游戏界面围起来了。
//墙--1
for (int i = 0; i < Col; i++)
{
arr[i][0] = 1;
arr[i][Row - 1] = 1;
}
for (int i = 0; i < Row; i++)
{
arr[0][i] = 1;
arr[Col - 1][i] = 1;
}
6. 初始化玩家飞机
玩家飞机刚开始应该出现在下侧的*位置,所以将这个位置的坐标对应的值初始化为2。
//玩家飞机--2
arr[PlayerPlane_y][PlayerPlane_x] = 2;
7. 玩家操作飞机
通过按下A/W/S/D键来控制飞机的移动,空格键来控制飞机发射子弹,要想实现这个功能我们需要用到getch()函数,这个函数用于从标准输入(通常是键盘)获取一个字符,而不需要用户按下回车键。这个函数在Windows操作系统下的
conio.h
头文件中定义,而在Unix/Linux系统下,通常使用termios.h
和unistd.h
头文件中的函数来实现类似的功能。
char ch = getch();
if (ch == 'w' && arr[PlayerPlane_y - 1][PlayerPlane_x] == 0)
{
arr[PlayerPlane_y][PlayerPlane_x] = 0;
PlayerPlane_y--;
arr[PlayerPlane_y][PlayerPlane_x] = 2;
}
if (ch == 'a' && arr[PlayerPlane_y][PlayerPlane_x - 1] == 0)
{
arr[PlayerPlane_y][PlayerPlane_x] = 0;
PlayerPlane_x--;
arr[PlayerPlane_y][PlayerPlane_x] = 2;
}
if (ch == 's' && arr[PlayerPlane_y + 1][PlayerPlane_x] == 0)
{
arr[PlayerPlane_y][PlayerPlane_x] = 0;
PlayerPlane_y++;
arr[PlayerPlane_y][PlayerPlane_x] = 2;
}
if (ch == 'd' && arr[PlayerPlane_y][PlayerPlane_x + 1] == 0)
{
arr[PlayerPlane_y][PlayerPlane_x] = 0;
PlayerPlane_x++;
arr[PlayerPlane_y][PlayerPlane_x] = 2;
}
if (ch == ' ')
{
Bullet_y = PlayerPlane_y - 1;
Bullet_x = PlayerPlane_x;
arr[Bullet_y][Bullet_x] = 4;
}
}
8. 子弹上移
子弹上移一格就调用一次DisPlay函数打印内容这样就实现了子弹上移的效果,这时需要用到到我们的while循环来循环子弹上移的函数和打印的函数,而子弹是由玩家按键产生的,所以这个while还需要循环上玩家操作飞机的函数,但是getch()会让while停止下来,玩家按键后才会继续,导致玩家按一次键盘按键,子弹才会上移一次,样就子弹就出现不了的这连续上移的效果了,所以我们需要在玩家操作的函数加上if(_kbhit())的判断语句函数,它会判断是否有按键按下来,如果没有就不执行getch()函数,继续循环。
_kbhit()
是一个C语言中的函数,用于检测键盘是否有按键被按下。它通常在Windows操作系统下的conio.h
头文件中定义。该函数不接受任何参数,并返回一个整数值。如果键盘上有按键被按下,则返回非零值(通常是1),否则返回0。
while (1)
{
//玩家操作
PlayerPlay(arr);
//打印棋盘
DisPlay(arr);
//子弹与敌机的操作
BulletEnemy(arr);
}
在循环打印的时候它会框框打印根本就看不清游戏界面,所以在这里还需要使用到光标的知识 :
在C语言中,光标通常指的是控制台或终端中的文本输入位置指示器。它显示了用户当前输入或输出的位置。在编写C程序时,有时需要对光标的显示和位置进行控制,以便创建更复杂的用户界面或处理特定的输入输出需求。
在Windows操作系统下,可以使用
conio.h
头文件中提供的函数来控制光标。
因为它会在光标的位置输出信息,所以我们在每次打印前将光标的位置置为(0,0)就可以了。
但是光标还会在那框框的闪,非常的碍眼,所以我们还需要使用函数将光标隐藏。
void HideCursor()
{
CONSOLE_CURSOR_INFO cursor_info = { 1,0 }; //第二个值为0,表示隐藏光标
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
9. 初始化敌机
在这里我们需要随机生成5架敌机出现在游戏界面上侧1-3格的位置,所以需要使用一个大小为5的数组接受随机数的值。
//敌机--3
for (int i = 0; i < Count; i++)
{
Enemy_y[i] = rand() % 3 + 1;
Enemy_x[i] = rand() % Row + 1;
//防止敌机生成在围墙上
if (Enemy_x[i] >= Row - 1)
Enemy_x[i] -= 2;
arr[Enemy_y[i]][Enemy_x[i]] = 3;
}
10. 敌机的下移
如果敌机也像子弹一样直接进去循环的话,那么敌机会移动得非常快,所以我们需要定义两个全局变量sleep和EnemySleep来控制敌机的移动速度,例如给EnemySleep赋值一个20,然后sleep赋值一个0,随后sleep在while循环中循环一次就加1,当sleep等于EnemySleep时我们才给敌机进行一次下移的操作,这样就相当于while每循环20次,敌机才下移一次。
在敌机下的过程中我们可能无法打中敌机,而使敌机下降到了最下侧,这时就得让敌机在触碰到围墙前一格消失,然后重新在上侧生成新的敌机。
在敌机下移的过程中可能会碰到玩家的飞机,这时游戏就得结束了,然后告诉玩家游戏结束。控制游戏结束需要用到system("pause")和exit()这两个函数。
system("pause")
的作用是在Windows系统的命令行窗口中暂停程序的执行,等待用户按下任意键后继续执行。这通常用于调试程序时,以便查看程序运行过程中的输出结果。
exit(0);
是C/C++语言中用于终止程序执行的语句。其中,参数0表示程序正常退出,非零值表示程序异常退出。在程序执行到该语句时,程序会立即停止运行,并返回给操作系统一个退出状态码。
//控制敌机的速度
if (sleep < EnemySleep)
{
sleep++;
}
else if (sleep > EnemySleep)
{
sleep = 0;
}
for (int i = 0; i < Count; i++)
{
//敌机击中玩家飞机的处理
if (PlayerPlane_y == Enemy_y[i] && PlayerPlane_x == Enemy_x[i] || score < 0)
{
printf(" /\\_/\\ \n");
printf(" ( o.o ) \n");
printf(" > ^ < \n");
printf("游戏失败!\n");
printf("\a");
system("pause");
exit(0);
}
//敌机到达最底面的处理
if (Enemy_y[i] >= Col - 2)
{
score -= 100;
arr[Enemy_y[i]][Enemy_x[i]] = 0;
Enemy_y[i] = rand() % 3 + 1;
Enemy_x[i] = rand() % Row + 1;
if (Enemy_x[i] >= Row - 1)
Enemy_x[i] -= 2;
arr[Enemy_y[i]][Enemy_x[i]] = 3;
}
//敌机下移的处理
if (sleep == EnemySleep)
{
for (int j = 0; j < Count; j++)
{
arr[Enemy_y[j]][Enemy_x[j]] = 0;
sleep = 0;
Enemy_y[j]++;
arr[Enemy_y[j]][Enemy_x[j]] = 3;
}
}
}
11. 游戏数值的设定
(1)敌机加速的分数阈值
- 当玩家得分达到 2000 分时,敌机的移动速度增加 2
- 当玩家得分再次增加 2000 分,即达到 4000 分时,敌机的移动速度再增加 2
- 以此类推,每当玩家得分增加 2000 分,敌机的速度增加 2。
(2)打爆一架敌机的得分
- 打爆一架普通敌机,玩家得到 100 分。
(3)一架敌机到底部消失的失分
- 如果一架敌机到达屏幕底部而未被击落,玩家失去 100 分。
12. 游戏的优化
为了使游戏更具可玩性,我们可以给玩家飞机加上一些技能,在这里的我设定了E/R是释放技能的指令,这个加在玩家操作飞机的函数哪里就可以了,这两个技能都是可发射范围更广的子弹,当然了这两个技能也不是无限释放的,是需要达到一定的值才能释放的,这里我们可以设定一个变量来存放一个技能的充能值,打爆一架敌机就给充能值加1,当这个值达到的我们所规定的满能值时才能接受释放技能的指令。
//技能指令
if (ch == 'r')
{
if (skill1 == 20)
{
for (int i = 1; i < Row - 1; i++)
{
skill1 = 0;
Bullet_y = PlayerPlane_y - 1;
Bullet_x = i;
arr[Bullet_y][Bullet_x] = 4;
}
}
}
if (ch == 'e')
{
int left = PlayerPlane_x - 3;
int right = PlayerPlane_x + 3;
if (skill2 == 5)
{
for (int i = left; i < right; i++)
{
if (i > 0 && i < Row - 1)
{
skill2 = 0;
Bullet_y = PlayerPlane_y - 1;
Bullet_x = i;
arr[Bullet_y][Bullet_x] = 4;
}
}
}
}
源码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<Windows.h>
#include<time.h>
#define Count 5
#define Col 40//列
#define Row 40//行
//玩家飞机坐标
int PlayerPlane_y = Col - 2;
int PlayerPlane_x = Row / 2 - 1;
//子弹坐标
int Bullet_y;
int Bullet_x;
//敌机坐标
int Enemy_y[Count] = { 0 };
int Enemy_x[Count] = { 0 };
//敌机的移动速度
int EnemySleep = 20;
int sleep = 0;
//分数
int score = 0;
//技能充能
int skill1 = 20;
int skill2 = 5;
void menu()
{
printf("--------------飞机大作战--------------\n");
printf("| |\n");
printf("| 1.开始游戏 |\n");
printf("| 0.退出游戏 |\n");
printf("| W/A/S/D移动 |\n");
printf("| 空格射击 E/R技能 |\n");
printf("| |\n");
printf("--------------------------------------\n");
}
//隐藏光标
void HideCursor()
{
CONSOLE_CURSOR_INFO cursor_info = { 1,0 }; //第二个值为0,表示隐藏光标
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
// 光标移到(X, Y)位置
void gotoxy(int x, int y)
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle, pos);
}
void DisPlay(int arr[Col][Row])
{
gotoxy(0, 0);
for(int i=0;i<Col;i++)
{
for (int j = 0; j < Row; j++)
{
if (arr[i][j] == 0)
{
printf(" ");
}
if (arr[i][j] == 1)
{
printf("█");
}
if (arr[i][j] == 2)
{
printf("*");
}
if (arr[i][j] == 3)
{
printf("$");
}
if (arr[i][j] == 4)
{
printf("|");
}
}
printf("\n");
}
printf("得分:%d ", score);
printf("EnemySleep=%d ", EnemySleep);
printf("skill1(20)=%d ", skill1);
printf("skill2(5)=%d ", skill2);
Sleep(20);
}
void InSet(int arr[Col][Row])
{
srand(time(NULL));
//路--0
//墙--1
for (int i = 0; i < Col; i++)
{
arr[i][0] = 1;
arr[i][Row - 1] = 1;
}
for (int i = 0; i < Row; i++)
{
arr[0][i] = 1;
arr[Col - 1][i] = 1;
}
//玩家飞机--2
arr[PlayerPlane_y][PlayerPlane_x] = 2;
//敌机--3
for (int i = 0; i < Count; i++)
{
Enemy_y[i] = rand() % 3 + 1;
Enemy_x[i] = rand() % Row + 1;
if (Enemy_x[i] >= Row - 1)
Enemy_x[i] -= 2;
arr[Enemy_y[i]][Enemy_x[i]] = 3;
}
//子弹--4
}
void PlayerPlay(int arr[Col][Row])
{
if (_kbhit())//判断是否有按键按下
{
char ch = getch();
if (ch == 'w' && arr[PlayerPlane_y - 1][PlayerPlane_x] == 0)
{
arr[PlayerPlane_y][PlayerPlane_x] = 0;
PlayerPlane_y--;
arr[PlayerPlane_y][PlayerPlane_x] = 2;
}
if (ch == 'a' && arr[PlayerPlane_y][PlayerPlane_x - 1] == 0)
{
arr[PlayerPlane_y][PlayerPlane_x] = 0;
PlayerPlane_x--;
arr[PlayerPlane_y][PlayerPlane_x] = 2;
}
if (ch == 's' && arr[PlayerPlane_y + 1][PlayerPlane_x] == 0)
{
arr[PlayerPlane_y][PlayerPlane_x] = 0;
PlayerPlane_y++;
arr[PlayerPlane_y][PlayerPlane_x] = 2;
}
if (ch == 'd' && arr[PlayerPlane_y][PlayerPlane_x + 1] == 0)
{
arr[PlayerPlane_y][PlayerPlane_x] = 0;
PlayerPlane_x++;
arr[PlayerPlane_y][PlayerPlane_x] = 2;
}
if (ch == ' ')
{
Bullet_y = PlayerPlane_y - 1;
Bullet_x = PlayerPlane_x;
arr[Bullet_y][Bullet_x] = 4;
}
if (ch == 'r')
{
if (skill1 == 20)
{
for (int i = 1; i < Row - 1; i++)
{
skill1 = 0;
Bullet_y = PlayerPlane_y - 1;
Bullet_x = i;
arr[Bullet_y][Bullet_x] = 4;
}
}
}
if (ch == 'e')
{
int left = PlayerPlane_x - 3;
int right = PlayerPlane_x + 3;
if (skill2 == 5)
{
for (int i = left; i < right; i++)
{
if (i > 0 && i < Row - 1)
{
skill2 = 0;
Bullet_y = PlayerPlane_y - 1;
Bullet_x = i;
arr[Bullet_y][Bullet_x] = 4;
}
}
}
}
}
}
void BulletEnemy(int arr[Col][Row])
{
for (int i = 0; i < Col; i++)
{
for (int j = 0; j < Row; j++)
{
if (arr[i][j] == 4)
{
for (int k = 0; k < Count; k++)
{
//子弹击中敌机的处理
if (i == Enemy_y[k] && j == Enemy_x[k])
{
if (skill1 < 20)
{
skill1++;
}
if (skill2 < 5)
{
skill2++;
}
score += 100;
arr[Enemy_y[k]][Enemy_x[k]] = 0;
Enemy_y[k] = rand() % 3 + 1;
Enemy_x[k] = rand() % Row + 1;
if (Enemy_x[k] >= Row - 1)
Enemy_x[k] -= 2;
arr[Enemy_y[k]][Enemy_x[k]] = 3;
//每2000分敌机加速
if (score % 2000 == 0 && EnemySleep > 4)
{
EnemySleep -= 2;
}
}
}
//子弹的移动
if (arr[i][j] == 4)
{
arr[i][j] = 0;
if (i > 1)
{
arr[i - 1][j] = 4;
}
}
}
}
}
//敌机的移动
if (sleep < EnemySleep)
{
sleep++;
}
else if (sleep > EnemySleep)
{
sleep = 0;
}
for (int i = 0; i < Count; i++)
{
if (PlayerPlane_y == Enemy_y[i] && PlayerPlane_x == Enemy_x[i] || score < 0)
{
printf(" /\\_/\\ \n");//敌机击中玩家飞机的处理
printf(" ( o.o ) \n");
printf(" > ^ < \n");
printf("游戏失败!\n");
printf("\a");
system("pause");
exit(0);
}
//敌机到达最底面的处理
if (Enemy_y[i] >= Col - 2)
{
score -= 100;
arr[Enemy_y[i]][Enemy_x[i]] = 0;
Enemy_y[i] = rand() % 3 + 1;
Enemy_x[i] = rand() % Row + 1;
if (Enemy_x[i] >= Row - 1)
Enemy_x[i] -= 2;
arr[Enemy_y[i]][Enemy_x[i]] = 3;
}
//敌机下移的处理
if (sleep == EnemySleep)
{
for (int j = 0; j < Count; j++)
{
arr[Enemy_y[j]][Enemy_x[j]] = 0;
sleep = 0;
Enemy_y[j]++;
arr[Enemy_y[j]][Enemy_x[j]] = 3;
}
}
}
}
void game()
{
//设置一个存放信息的数组
int arr[Col][Row] = { 0 };
//隐藏光标
HideCursor();
//放置信息
InSet(arr);
//打印游戏界面
DisPlay(arr);
//玩家移动
while (1)
{
//玩家操作
PlayerPlay(arr);
//打印棋盘
DisPlay(arr);
//子弹与敌机的操作
BulletEnemy(arr);
}
}
int main()
{
system("color b");
int input = 0;
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入有误,请重新输入:\n");
break;
}
return 0;
}
最终效果
飞机大作战