C语言实现简单的打飞机小游戏
编译环境:vs2019
需求:
在同一个平面内,控制大炮发射的三个方向,空格发射炮弹,敌军和友军飞机会一直随机在天上飞过,击中天上飞行的敌军飞机加一分,击中友军飞机减一分
思路:
先初始化程序,再画出说明和地图,接着进入无限循环,循环中分别实现飞机不停地在天上飞、通过awd空格键控制游戏内容、符合游戏条件时加分三个主要步骤。
做法:
首先要实现飞机的动画效果,本质是要在控制台上输出三条x轴上输出一个字符串,并要让这三个字符串看起来在同时移动。这里我借助了写赌马大赛的经验:在第一次无限循环内分别循环输出第1、2、3条的字符串表示走出第一步,然后再自增三个字符串x轴坐标数值,第二次无限循环清除上一次字符串移动痕迹,并再分别输出三条字符串,表示走出第二步。由于内层for循环没有休眠函数,这样看起来就是三个字符串同时移动的效果。这样就可以通过无限循环表示三架飞机不停地移动了。
接下来是通过getch函数改变大炮炮口指向方向(一个字符),在炮口指向切换的同时,炮弹初始化的位置也同样会改变,当按下空格时,炮弹(一个字符)会从他的初始化位置沿设定好的路线不停移动,当横纵轴坐标与飞机坐标相匹配时判断是否为敌军飞机|—0>,是则得分加一,否则得分减一。
此外程序里还有很多细节,这里不一一赘述。
难点:
看起来让三个字符串同时移动。这是一种算法,学会了就应该应该像记住基础知识一样牢牢记住他。明白了原理之后,比较容易掌握。
说明:
这是一个简单的小程序,实现该程序中相同的效果有着很多种方法,而我则大量地使用了数组来存放、统计和使用各种不同的数据,其实类似效果还可以通过单链表来实现。在我目前的认知中使用数组和直接输出字符串的写法有着简洁明了的优点。但同时也导致出一个显示效果上比较致命的缺陷。
注意:
由于编译器原因程序中_kbhit()和_getch()函数可能在其他编译器上编译会出现错误,解决办法是去掉函数前面的 “_”。
运行效果:
代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <time.h>
#include <string.h>
void HideCursor(); //隐藏光标
void gotoxy(int x, int y); //光标定位
#define R 26 //飞机每次随机尾x坐标最大值
#define N 20 //通关分数
int ax[3] = { 0 }; //三个飞机尾坐标,全局
int y[3] = { 3,7,11 }; //三个y轴,全局,查表法确定飞机的y轴位置
//因为炮弹的飞行距离是一次加两个格子,为了奇偶对应这里的y轴必须都是偶数
int i; //循环用变量
int fd[3]; //控制飞机种类
void cshcxhs() //初始化函数
{
system("title 打飞机小游戏"); //标题
system("mode con cols=100 lines=30"); //宽100,高30
HideCursor(); //光标隐藏
gotoxy(46, 12); //介绍游戏规则
printf("游戏规则:");
gotoxy(36, 14);
printf("w a d键控制方向,空格键发射炮弹");
gotoxy(26, 16);
printf("击中敌军飞机 |---0> 加一分,击中友军飞机 >>>>>> 减一分");
gotoxy(40, 18);
printf("累计得分 %d 结束游戏",N);
Sleep(2800);
system("cls");
int k; //循环用变量
gotoxy(40, R); //初始化炮台
for (k = 0; k < 17; k++)
printf("_");
gotoxy(46, R);
printf("[_O_]");
gotoxy(48, 25); //中间x:48
printf("|");
int j;
srand((unsigned)time(NULL)); //初始化随机种子
for (j = 0; j < 3; j++) //初始化飞机的初始x坐标,写在循环之外
{
ax[j] = rand() % R;
fd[j] = rand() % 2;
}
gotoxy(0, R);
printf(" 得分:"); //分数x坐标9
}
void hcfjhs() //画出飞机函数
{
gotoxy(ax[i], y[i]); //首先在飞机尾处输出整架飞机
if (fd[i] == 1)
printf("|---0>");
else
printf(">>>>>>");
gotoxy(ax[i] - 1, y[i]); //清除飞机尾部留下的痕迹
printf(" ");
ax[i]++; //然后飞机尾坐标自增,下次自飞机尾输出整架飞机
}
void dhpdxshs() //画出飞机,并让飞机在飞过x94的时候消失
{
for (i = 0; i < 3; i++) //每架飞机都走一步
{
hcfjhs();
if (ax[i] + 6 >= 94) //判断每架飞机头有没有超过94
{
gotoxy(94, y[i]); //满足条件在每架飞机的94处输出空格
printf(" ");
if (ax[i]+6 > 100) //每架飞机的飞机头超过100,则重置一个随机数
{
ax[i] = rand() % R; //修改为小于R的随机数
fd[i] = rand() % 2;
}
}
}
}
int da = 2; //da = 1为最左 默认中间 炮台方向,全局变量
int yip = 0; //是否开炮,0不开,1,2,3三个方向
int rtde = 1; //是否发射完毕,必须等待炮弹发射完成
int shells_x; //炮弹坐标
int shells_y;
void ckkzhs()
{
char ch;
ch = _getch();
if (ch == 'd' || ch == 'D')
{
gotoxy(49, 25);
printf("/"); //炮台右转
gotoxy(47, 25);
printf(" ");
da = 3; //最右状态
}
if (ch == 'A' || ch == 'a')
{
gotoxy(47, 25);
printf("\\"); //转义转义字符
gotoxy(48, 25);
printf(" ");
da = 1; //最左状态
}
if (ch == 'W' || ch == 'w')
{
gotoxy(48, 25);
printf("|");
gotoxy(47, 25);
printf(" ");
gotoxy(49, 25);
printf(" ");
da = 2;
}
if (ch == ' '&& rtde == 0) //大炮处于发射完毕状态,且按下空格
{
if (da == 2) //中 炮台朝向
yip = 2;
if (da == 1) //左
{
yip = 1;
shells_x = 45; //初始化炮弹的坐标
}
if (da == 3) //右
{
yip = 3;
shells_x = 51;
}
shells_y = 23; //初始化炮弹的坐标
}
}
void dpfshs() //向三个不同方向发射炮弹
{
rtde = 1; //循环,炮弹移动时代表未开炮完毕
if (yip == 2)
{
shells_x = 48;
gotoxy(shells_x, shells_y-=2);
printf("o");
gotoxy(shells_x, shells_y+2);
printf(" ");
}
if (yip == 1)
{
gotoxy(shells_x-=2, shells_y-=2);
printf("o");
gotoxy(shells_x+2, shells_y+2);
printf(" ");
}
if (yip == 3)
{
gotoxy(shells_x+=2, shells_y-=2);
printf("o");
gotoxy(shells_x-2, shells_y+2);
printf(" ");
}
if (shells_y <= 1) //炮弹越界,消除炮弹
{
yip = 0; //炮弹越界,炮弹停下
gotoxy(48, 1); //并消除
printf(" ");
gotoxy(23, 1);
printf(" ");
gotoxy(73, 1);
printf(" ");
rtde = 0; //炮弹越界,开炮完毕,可进行下一轮开炮
}
}
int score; //得分
void pdfsjzhs() //判断是否击中和统计信息
{
for (i = 0; i < 3; i++)
{
if (shells_x >= ax[i] && shells_x <= ax[i] + 6 && shells_y == y[i])//击中时
{
if(fd[i]==1)
score++; //击中一次,得分加一
else
{
score--;
if (score <= 0)
score = 0;
}
rtde = 0; //击中时,开炮完成
gotoxy(ax[i]-1, y[i]); //击中后,在原飞机尾巴处消除飞机
printf(" ");
ax[i] = rand() % R; //修改为小于10的随机数
fd[i] = rand() % 2; //飞机种类发生变化
gotoxy(shells_x, shells_y); //在击中飞机的地方输出空格消除炮弹尸体
printf(" ");
shells_x = 0, shells_y = 0; //炮弹击中飞机,炮弹越界,下一次空格将自动初始化炮弹
}
}
gotoxy(9, R);
printf("%d",score);
if (score >= N) //游戏结束判断
{
system("cls"); //清屏
gotoxy(39,15);
printf("您通关了,最终得分:%d !", N);
gotoxy(0, 29);
exit(0);
}
}
void process() //流程
{
while (1)
{
dhpdxshs(); //飞机动画函数
if (_kbhit())
{
ckkzhs(); //操作函数
}
dpfshs(); //大炮效果函数
pdfsjzhs(); //判断是否击中和统计信息
Sleep(18);
}
}
int main()
{
cshcxhs(); //初始化程序
process(); //流程
return 0;
}
void HideCursor()
{
CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
void gotoxy(int x, int y)
{
COORD pos = { x,y };
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
不足之处:
我在程序中输出飞机时使用了在飞机尾直接输出整架飞机,飞机尾x坐标自增的写法,这是一种很笨但又很简单的做法。这种做法导致飞机在飞过控制台最大宽度的同时无法逐个消除飞机机身,飞机头一旦越过控制台最大宽度就会出现程序输出错误,所以我为了保留“让飞机逐节身体地飞出边界”这个让飞机消失时看起来比较自然的效果,只好用最后飞机所在y轴最后6个x轴格子作为遮挡飞机的板子,以此来实现类似的效果。
由于该程序出了创意之外,全是由本人靠自己写的,由于我能力有限,在写这个程序的同时隐约感觉到我的程序存在着大量代码复用性、结构严谨性、运行效率低下上的问题,同时很多函数和变量标识符我采用了瞎写、中文拼音组成的做法。
作为一名c语言新手,我对未知的知识始终抱有学习和谦卑的态度,如有贵人能够对我的程序提出复用性、严谨性、运行效率上的建议,我将不胜感激。
白家名 发布了1 篇原创文章 · 获赞 0 · 访问量 50 私信 关注