空战小游戏(控制台应用) c/c++实现
首发地址:https://www.cnblogs.com/saw96x/p/12465195.html
笔者经历了近半年的C++学习。过去一直专注于控制台环境下的编程练习。最近总觉得这样的练习略显平淡。于是决定尝试编写一个简单的游戏程序。通过练习如《打砖块》《Flappy Bird》等经典小游戏后并查阅相关书籍以及网络资源开发了一个较为完善的第一人称空战游戏的控制界面程序。以期帮助其他正在困惑于编程道路的新手找到一些灵感。
游戏的完成效果:

先看看游戏的主要框架:
#include<iostream>
#include<cstdlib>
#include<conio.h>
#include<windows.h>
using namespace std;
//宏定义
#define High 15 //游戏界面高度
#define Width 25 //游戏界面宽度
#define EnemyNum 6 //敌机数量
//全局变量
int pos_x,pos_y; //飞机坐标
int canvas[High][Width]={0}; //背景画布,用二维数组表示
int enemy_x[EnemyNum],enemy_y[EnemyNum]; //敌机的坐标,因为有多架敌机,用一维数组表示
int BulletWidth; //子弹宽度,实现散弹功能
int EnemySpeed; //敌机速度,实现动态难度
int score; //得分
复制代码
复制代码
int main(){
HideCursor();//隐藏光标
startup();//游戏参数初始化
while(1){//循环保持游戏进程
show();//输出游戏画面
UpdateWithoutInput();//与用户输入无关的数据更新
UpdateWithInput();//与用户输入有关的数据更新
}
return 0;
}
这类框架能够轻松实现大多数简单游戏的功能,并增添一个用于判断游戏是否进行的条件作为循环控制依据。例如让读者自行补充完成这些功能模块如重新启动游戏的相关逻辑等
让我们按顺序来看看这些函数的原理和实现方法吧
为了更好地处理游戏体验问题,在本节中我们将详细探讨如何实现这一功能。作为一个独立的功能模块,在本项目中我们特意设计了一个名为"隐藏光标"的功能来解决这一技术难题。该功能的主要目的是确保在操作过程中不会出现闪烁现象影响用户体验。通过深入分析当前技术方案并结合实际测试需求,在开发团队的共同努力下完成了这一关键模块的设计与实现工作
void HideCursor(){
CONSOLE_CURSOR_INFO cursor_info={1,0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}
关于这个函数的工作原理呢?我对它的功能和实现细节并不清楚,在网络上看到的相关资料中也没有深入解析的内容。因此目前还没有找到完整的解析内容。希望读者能够对这一问题有所了解会更加感激。
然后是游戏参数的初始化,这个函数也毫无理解难度:
void startup(){
pos_x=High-1; //x是纵坐标
pos_y=Width/2; //y是横坐标
canvas[pos_x][pos_y]=1; //在画布中飞机相应的位置设定值为1,表示这个位置是飞机
for(int i=0;i<EnemyNum;i++){ //给每架敌机设定随机的出现位置
enemy_y[i]=rand()%Width;
enemy_x[i]=rand()%2;
canvas[enemy_x[i]][enemy_y[i]]=3; //在画布中敌机相应的位置设定值为3,表示这个位置是敌机
}
score=0;
BulletWidth=0;
EnemySpeed=13; //速度其实是刷新的时间间隔,因此速度越小速度越快
}
接下来是输出游戏画面的函数了,算是重头戏:
void show(){
gotoxy(0,0); //让输出内容的光标回到左上角
for(int i=0;i<High;i++){ //这两行是对画布元素值
for(int j=0;j<Width;j++){ //逐一扫描
if(canvas[i][j]==0) //如果值为0
printf(" "); //输出空格,表示空白
else if(canvas[i][j]==1) //如果值为1
printf("*"); //输出*,表示飞机
else if(canvas[i][j]==2) //如果值为2
printf("|"); //输出|,表示子弹
else if(canvas[i][j]==3) //如果值为3
printf("@"); //输出@,表示敌机
}
printf("\n"); //扫描完一行,换行开始下一行
}
printf("Score:%3d\n",score); //输出分数
Sleep(20); //停留2毫秒,作为刷新频率
}
简述输出画面的生成逻辑。首先定义一个二维数组作为画布背景,在该数组中的每个元素值代表画面上的游戏对象:若值为0则为空白区域;若值为1则表示为飞机;若值为2则表示为正在飞行中的子弹;若值为3则表示敌机存在。接着遍历该二维数组以确定输出内容。那么如何实现动画效果呢?在再次执行该函数时,在窗口左上角(x=0,y=0的位置)放置光标并显示相关内容即可实现元素间的移动效果(因为gotoxy(0,0)并非标准库函数,在此需包含window.h头文件并自行实现)。
void gotoxy(int x,int y){
HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X=x;
pos.Y=y;
SetConsoleCursorPosition(handle,pos);
}
显然仅凭画面输出函数无法生成完整的游戏动画效果。为了生成高质量的画面表现还需完成画布中数据的更新操作。因此开发团队必须先实现两个核心组件:首先需要实现的是与用户输入无关的数据更新功能UpdateWithoutInput();接着则是与用户输入相关的数据更新功能UpdateWithInput()。让我们先探讨一下与用户无关的数据更新模块UpdateWithoutInput()的具体实现细节。
void UpdateWithoutInput(){
for(int i=0;i<High;i++){ //这两个循环
for(int j=0;j<Width;j++){//是逐一扫描画布元素
if(canvas[i][j]==2){ //如果元素值是2(即子弹),会发生如下的数据改变
for(int k=0;k<EnemyNum;k++){ //对每架敌机的相关数据进行遍历
if(i==enemy_x[k]&&j==enemy_y[k]){ //如果子弹横纵坐标和敌机的相等
score++; //加分
if(score<7&&EnemySpeed>2) EnemySpeed=13;//实现不同分数段难度不同
else if(score>=7&&score<14) EnemySpeed=11;
else if(score>=14&&score<21) EnemySpeed=9;
else if(score>=21&&score<40) EnemySpeed=7;
else if(score>=40&&score<60) EnemySpeed=5;
else if(score>=60) EnemySpeed=3;
if(score<15) BulletWidth=0; //如果分数小于15,子弹宽度为0,那么只有单发子弹
else if(score<30&&score>=15) BulletWidth=1;//分数大于15小于30,宽度为1,那么有3发子弹
else if(score<45&&score>=30) BulletWidth=2;//分数大于30小于45,宽度为2,那么有5发子弹
canvas[enemy_x[k]][enemy_y[k]]=0; //既然子弹和敌机的坐标重合,那么敌机被消灭,相应的位置也就变为了空白
enemy_x[k]=rand()%2; //随机出新的敌机的纵坐标
enemy_y[k]=rand()%Width; //随机出新的敌机的横坐标
canvas[enemy_x[k]][enemy_y[k]]=3; //生成新的敌机
canvas[i][j]=0; //子弹消失(实际上子弹并未消失,笔者没搞出原因)
}
}
canvas[i][j]=0;//令子弹的原位置为空白
if(i>0)
canvas[i-1][j]=2;//生成子弹的新位置数据,配合输出画面函数实现动画效果
}
}
}
static int speed=0; //这俩句可能比较难懂,设置成静态变量是为了防止超出作用域后speed直接消失导致无法更新数据
if(speed<EnemySpeed) speed++; //设置speed的目的是只有当speed加到等于EnemySpeed时才更新在画布中移动的敌机的位置数据,从而实现敌机移动较慢的动画效果
for(int k=0;k<EnemyNum;k++){ //对每架敌机都进行处理
if(pos_x==enemy_x[k]&&pos_y==enemy_y[k]){ //敌机与飞机位置重合,游戏失败
printf("You Lose!\n");
Sleep(3000);
system("pause");
exit(0);
}
if(enemy_x[k]>High){ //敌机飞出了范围
canvas[enemy_x[k]][enemy_y[k]]=0; //将原位置置为空白
enemy_y[k]=rand()%Width; //生成新的敌机坐标
enemy_x[k]=rand()%2;
canvas[enemy_x[k]][enemy_y[k]]=3; //生成新的敌机
score--; //扣分
}
if(speed==EnemySpeed){ //speed等于EnemySpeed,可以更新敌机的位置数据了
for(int i=0;i<EnemyNum;i++){
canvas[enemy_x[i]][enemy_y[i]]=0;
enemy_x[i]++; //纵坐标加1,向前移动
speed=0;
canvas[enemy_x[i]][enemy_y[i]]=3;
}
}
}
}
这个核心模块可以说是整个程序的关键部分,在结合显示画面功能并配合数据更新后就可以轻松实现简单的动画效果了。通过这种方法我们可以构建出多种多样的实例来演示效果并掌握基本的技术手段。如果能够深入理解这一整体框架并合理运用注释说明那么配合实际操作应该能够更加容易地完成后续开发工作。因此建议大家动手实践一遍以加深对该技术路径的理解
然后是与用户有关的操作了:
void UpdateWithInput(){
char input;//输入字符
if(_kbhit()){ //如果按动了键盘
input=_getch();//读取键盘输入的字符
if(input=='a'){ //如果输入的字符是a,那么向左移动,以下几个分支类同
canvas[pos_x][pos_y]=0;
pos_y--;
canvas[pos_x][pos_y]=1;
}
else if(input=='w'){
canvas[pos_x][pos_y]=0;
pos_x--;
canvas[pos_x][pos_y]=1;
}
else if(input=='s'){
canvas[pos_x][pos_y]=0;
pos_x++;
canvas[pos_x][pos_y]=1;
}
else if(input=='d'){
canvas[pos_x][pos_y]=0;
pos_y++;
canvas[pos_x][pos_y]=1;
}
else if(input==' '){ //如果读入的是空格键
int l=pos_y-BulletWidth; //子弹流的左端
int r=pos_y+BulletWidth; //子弹流的右端for(int k=l;k<=r;k++) //输出一排子弹
canvas[pos_x-1][k]=2;
}
}
}
这段也很简单,不多解释了
以下是完整代码:
#include<iostream>
#include<cstdlib>
#include<conio.h>
#include<windows.h>
using namespace std;
#define High 15
#define Width 25
#define EnemyNum 6
int pos_x,pos_y;
int canvas[High][Width]={0};
int enemy_x[EnemyNum],enemy_y[EnemyNum];
int BulletWidth;
int EnemySpeed;
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(){
pos_x=High-1;
pos_y=Width/2;
canvas[pos_x][pos_y]=1;
for(int i=0;i<EnemyNum;i++){
enemy_y[i]=rand()%Width;
enemy_x[i]=rand()%2;
canvas[enemy_x[i]][enemy_y[i]]=3;
}
score=0;
BulletWidth=0;
EnemySpeed=13;
}
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]==2)
printf("|");
else if(canvas[i][j]==3)
printf("@");
}
printf("\n");
}
printf("Score:%3d\n",score);
Sleep(20);
}
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<EnemyNum;k++){
if(i==enemy_x[k]&&j==enemy_y[k]){
score++;
if(score<7&&EnemySpeed>2) EnemySpeed=13;
else if(score>=7&&score<14) EnemySpeed=11;
else if(score>=14&&score<21) EnemySpeed=9;
else if(score>=21&&score<40) EnemySpeed=7;
else if(score>=40&&score<60) EnemySpeed=5;
else if(score>=60) EnemySpeed=3;
if(score<15) BulletWidth=0;
else if(score<30&&score>=15) BulletWidth=1;
else if(score<45&&score>=30) BulletWidth=2;
canvas[enemy_x[k]][enemy_y[k]]=0;
enemy_x[k]=rand()%2;
enemy_y[k]=rand()%Width;
canvas[enemy_x[k]][enemy_y[k]]=3;
canvas[i][j]=0;
}
}
canvas[i][j]=0;
if(i>0)
canvas[i-1][j]=2;
}
}
}
static int speed=0;
if(speed<EnemySpeed) speed++;
for(int k=0;k<EnemyNum;k++){
if(pos_x==enemy_x[k]&&pos_y==enemy_y[k]){
printf("You Lose!\n");
Sleep(3000);
system("pause");
exit(0);
}
if(enemy_x[k]>High){
canvas[enemy_x[k]][enemy_y[k]]=0;
enemy_y[k]=rand()%Width;
enemy_x[k]=rand()%2;
canvas[enemy_x[k]][enemy_y[k]]=3;
score--;
}
if(speed==EnemySpeed){
for(int i=0;i<EnemyNum;i++){
canvas[enemy_x[i]][enemy_y[i]]=0;
enemy_x[i]++;
speed=0;
canvas[enemy_x[i]][enemy_y[i]]=3;
}
}
}
}
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;
}
else if(input=='w'){
canvas[pos_x][pos_y]=0;
pos_x--;
canvas[pos_x][pos_y]=1;
}
else if(input=='s'){
canvas[pos_x][pos_y]=0;
pos_x++;
canvas[pos_x][pos_y]=1;
}
else if(input=='d'){
canvas[pos_x][pos_y]=0;
pos_y++;
canvas[pos_x][pos_y]=1;
}
else if(input==' '){
int l=pos_y-BulletWidth;
int r=pos_y+BulletWidth;
for(int k=l;k<=r;k++)
canvas[pos_x-1][k]=2;
}
}
}
int main(){
HideCursor();
startup();
while(1){
show();
UpdateWithoutInput();
UpdateWithInput();
}
return 0;
}
以后应该还会更新一些别的小游戏,可能还会用图形库更新这个游戏
PS:值得参考《C语言课程设计与游戏开发实践教程》,如果想要了解C语言的作用和用途,请阅读这本书怎么样!
