
C语言雷霆战机
下载好这个.exe文件 然后点击,会自动识别电脑上可以安装的软件
然后安装就行了,这是打开VS2019就可以直接引入头文件 #include<graphics.h> 雷霆战机源码以及素材: 链接:https://pan.baidu.com/s/1hqFnO12nebMgGB2gQJeVSg 提取码:3ecs
发布日期:2021-05-06 10:51:32
浏览次数:25
分类:精选文章
本文共 8543 字,大约阅读时间需要 28 分钟。
C语言写雷霆战机(图形库)
这里我用的VS2019,图形库是EasyX(可以到官方上下载,超级简单几KB)
由于素材时间原因就大概,写到这但是思路已经打开,大家可以根据自己
的想法进行扩充 上图#include#include //图形库,需要下载easyX,#include #include #include #include //系统设备头文件#pragma comment(lib,"winmm.lib")//加载多媒体设备库//将所有素材,拷贝到当前项目的文件里面//然后我们在使用图片的时候,需要将图片加载到内存,(就像我们要使用字母a一样我们同样需要在//在内存中加载一个a,因为程序是在内存上运行的,故我们需要一个变量来储存这个图片//在graphics.h--图形库中提供了一个变量IMAGE(图片的意思),IMAGE backInit_Phone;//初始界面,一闪而过;IMAGE backGame_Phone;//关卡的背景IMAGE player_Phone[2];//游戏角色的图片,需要掩码图,以及原位图IMAGE ammunition_Phone[2];//弹药图片;IMAGE enemy_Phone[2][2];//两种敌机的图片IMAGE ok;//确定图片enum gameAttributes//游戏各种属性,这里我就不用宏定义了,使用枚举类,因为我对枚举类不熟悉{ X = 1200, Y = 750, ammunitionNumber = 300, //弹药数量 Big = 9, //用于标志大飞机 Small = 0, //用于标志小的飞机 enemyNumber = 15 //一次最多出现多少敌机 };//定义一个结构体用于描述,玩家战机的属性以及其他数据等,因为角色的好多属性都是变量;struct playerAttributes { int x, y; //玩家战机坐标 弹药坐标 bool isLive;//是否存活 弹药是否发出 int speed;//飞机速度 弹药速度 int HP; int killsNumber;}fighterPlane, ammunition[ammunitionNumber];//Fighter plane--战机//定义一个结构体,用于描述敌方战斗机的属性struct enemyAttributes { int x, y; int speed; bool isLive; int Type; int HP;}Plane[enemyNumber];//专门用于加载图片的方法,调用图形库中的loadimage()方法;,加载音乐void loadImage() { //这里会报错,因为字符集不对,我们图形库里面专门定义的字符集,识别错误..具体原因 //博客上有,解决方案,将当前项目的字符姐由Unicode -> 多字节字符集 loadimage(&player_Phone[0], "./image/player.jpg");//加载飞机掩位码图 loadimage(&player_Phone[1], "./image/player2.jpg");//加载飞机源图 loadimage(&backInit_Phone,"./image/初始化背景.jpg");//加载游戏欢迎以及介绍背景图 loadimage(&backGame_Phone,"./image/游戏背景--最终.jpg");//加载关卡图 loadimage(&ammunition_Phone[0], "./image/导弹.png"); loadimage(&ammunition_Phone[1], "./image/导弹2.png"); loadimage(&enemy_Phone[0][0], "./image/敌机.jpg"); loadimage(&enemy_Phone[0][1], "./image/敌机2.jpg"); loadimage(&enemy_Phone[1][0], "./image/大敌机.jpg"); loadimage(&enemy_Phone[1][1], "./image/大敌机2.jpg"); loadimage(&ok, "./image/2.png"); mciSendString("open ./image/初始背景音乐.mp3",0,0,0);//先打开音乐,然后后面三个擦书不用管; //mciSendString("open ./image/死亡音乐.mp3 alias deathBgm", 0, 0, 0);//先打开音乐,然后后面三个擦书不用管; //mciSendString("open ./image/导弹.mp3 alias missileBgm", 0, 0, 0);//先打开音乐,然后后面三个擦书不用管; //mciSendString("open ./image/boss.mp3", 0, 0, 0);//先打开音乐,然后后面三个擦书不用管; mciSendString("open ./image/Boss背景.mp3", 0, 0, 0);//先打开音乐,然后后面三个擦书不用管; mciSendString("open ./image/游戏音乐.mp3", 0, 0, 0);//先打开音乐,然后后面三个擦书不用管; //alias 起别名}//用于欢迎以及介绍界面dbool Welcome() { initgraph(X, Y); putimage(0, 0, &backInit_Phone);//将图片贴到窗口上,0.0是坐标,后是哪张图 putimage(500,400,&ok); mciSendString("play ./image/初始背景音乐.mp3", 0, 0, 0); while (1) { if (_getch() == 'b') { //_getch()这个函数就是getch(),不过vs2012以后getch()不知到为啥 //会报错,所以我在博客上搜出了这个函数_getch(); closegraph(); return true; } } return false;}//初始化玩家数据,以及地图,子弹void initData() { loadImage();//先将图片加载到内存里;由于图片只需要加载一次就可以了,所以我们就放在初始化数据的地方 fighterPlane.x = X / 2; fighterPlane.y = Y - 82; fighterPlane.speed = 2; fighterPlane.isLive = true; fighterPlane.HP = 20; fighterPlane.killsNumber = 0; //初始化弹药 for (int i = 0; i < ammunitionNumber;++i) { ammunition[i].x = 0; //我们这里是为了给个默认值,实际上弹药的初始坐标是根据飞机来定位的 ammunition[i].y = 0; ammunition[i].isLive = false; ammunition[i].speed = 1; } //初始化敌机 for (int i = 0; i < enemyNumber;++i) { Plane[i].isLive = false; Plane[i].speed = 1; Plane[i].Type = 0; Plane[i].HP = 1; Plane[i].x = 0; Plane[i].y = 0; }}//自动创建敌方战斗机,并完成数据赋值void createEnemyPlane() { mciSendString("play ./image/游戏音乐.mp3 repeat", 0, 0, 0); //mciSendString("play ./image/Boss背景.mp3 ", 0, 0, 0); static DWORD time1, time2 = 0; int r = rand() % 11; if (time2 - time1 > 1000) { if (r==0) { for (int i = 0; i < enemyNumber;++i) { if (!Plane[i].isLive) { Plane[i].isLive = true; Plane[i].HP = 3; Plane[i].speed = 1; Plane[i].Type = Big; Plane[i].x = rand() % (X - 192/2); Plane[i].y = rand() % 500 - 500; break; } } } else { for (int i = 0; i < enemyNumber; ++i) { if (!Plane[i].isLive) { Plane[i].isLive = true; Plane[i].HP = 1; Plane[i].speed = 1; Plane[i].Type = Small; Plane[i].x = rand() % (X - 90/2); Plane[i].y = rand() % 500 - 500; break; } } } time1 = time2; } time2 = GetTickCount(); }//如果玩家按下空格键,代表飞机发射了弹药,那就把该弹药标记位true,且根据当前飞机的位置定位弹药位置void createAmmunition() { //这里只需要对弹药数据进行修改就行了,真正按键实在其他地方 for (int i = 0; i < ammunitionNumber;++i) { if (!ammunition[i].isLive) { ammunition[i].isLive = true;//让子弹存活 ammunition[i].x = fighterPlane.x + 82 / 2;//这个数据就是战机的头部 ammunition[i].y = fighterPlane.y; break; } }}//打印游戏图片void printGameImage() { putimage(0, 0, &backGame_Phone);//贴图 putimage(fighterPlane.x, fighterPlane.y, &player_Phone[1], NOTSRCERASE);//先贴掩码图 //NOTSRCERASE使用OR运算符组合源位图,的反*与目标矩阵的颜色* //SRCINVERT使用XOR运算符,组合目标矩阵形与模板颜色*; putimage(fighterPlane.x, fighterPlane.y, &player_Phone[0], SRCINVERT);//后贴原位图 outtextxy(0,0,fighterPlane.killsNumber); //如果弹药不为false,说明弹药已经被发射出去了,那就绘制弹药 for (int i = 0; i < ammunitionNumber; ++i) { if (ammunition[i].isLive) { putimage(ammunition[i].x, ammunition[i].y, &ammunition_Phone[1], NOTSRCERASE);//后贴原位图 putimage(ammunition[i].x, ammunition[i].y, &ammunition_Phone[0], SRCINVERT);//后贴原位图 } } //绘制敌机 for (int i = 0; i < enemyNumber;++i) { if (Plane[i].isLive) { if (Plane[i].Type == Big) { putimage(Plane[i].x, Plane[i].y, &enemy_Phone[1][1], NOTSRCERASE); putimage(Plane[i].x,Plane[i].y,&enemy_Phone[1][0], SRCINVERT); } else { putimage(Plane[i].x, Plane[i].y, &enemy_Phone[0][1], NOTSRCERASE); putimage(Plane[i].x, Plane[i].y, &enemy_Phone[0][0], SRCINVERT); } } //printf("xy(%d ,%d) HP = %d isLive = %d\n",Plane[i].x, Plane[i].y, Plane[i].HP, Plane[i].isLive); //Sleep(10); }}//移动战机,移动子弹void moveFighterPlane() { //有两种方式,1._getch()从键盘读取一个字符,但是它是阻塞的,而且不是C语言的正规函数,这是windows里面 //的函数,包含头文件conio.h。可以借助_kbhit()函数来实现类似不阻塞,(该函数功能,检测键盘是否按下键,如果按下 //返回真,否则返回假2.另外方法,使用windows里面的函数,GetAsyncKeyState(键盘按键名字),获取键盘输入) //这里我们使用第二种; //为了防止飞机飞出边界,这里还需要判断一下; //如果用字母必须是大写,这样大小写才能检测到,否则都检测不到 if (GetAsyncKeyState(VK_UP)|| GetAsyncKeyState('W')) { //上,这里的VK_UP其实就是宏定义,代表键盘的上,类似还有VK_DOWN等 if(fighterPlane.y > 0) fighterPlane.y -= fighterPlane.speed; } //飞机也是右长度的,所以我们需要一尾部的点判断下移的情况; //当前y + 飞机长 = 尾y坐标,但是又有个问题,飞机发射的子弹是从中间发射的,而且走直线 //那如果敌机出现在窗口边缘,就打不到了,所以我们可以让飞机出去一半 if (GetAsyncKeyState(VK_DOWN)|| GetAsyncKeyState('S')) { //下 if (fighterPlane.y + 82 < Y) fighterPlane.y += fighterPlane.speed; } if (GetAsyncKeyState(VK_LEFT)|| GetAsyncKeyState('A')) { //左 if (fighterPlane.x + 82/2 > 0) fighterPlane.x -= fighterPlane.speed; } //右移同理 if (GetAsyncKeyState(VK_RIGHT)|| GetAsyncKeyState('D')) { //右 if (fighterPlane.x + 86/2 < X) fighterPlane.x += fighterPlane.speed; } //但是我们会发现,子弹好像只能发射一个,因为程序运行的太快了,你点一下空格,GetAsyncKeyState() //会检测很多次,以至于一次把子弹发射完了;所以你会看到有一个长长黑影,解决方案,使用局部休眠 // 这个在数字雨中使用过; static DWORD time1, time2 = 0; if (GetAsyncKeyState(VK_SPACE) && (time2 - time1 > 200) ) { //mciSendString("play missileBgm",0,0,0); createAmmunition(); time1 = time2; } time2 = GetTickCount(); //弹药的移动 for (int i = 0; i < ammunitionNumber;++i) { if (ammunition[i].isLive) { ammunition[i].y -= ammunition[i].speed; } } //敌机的移动 static DWORD T1, T2 = 0; if (T2- T1>10) { for (int i = 0; i < enemyNumber;++i) { if (Plane[i].isLive) { Plane[i].y += Plane[i].speed; } } T1 = T2; } T2 = GetTickCount();}//敌机阵亡void planeDeath() { for (int i = 0; i < enemyNumber;++i) { if (!Plane[i].isLive) continue; for (int j = 0; j < ammunitionNumber;++j) { if (!ammunition[j].isLive)continue; if ((Plane[i].Type == Big) && ((ammunition[j].x > Plane[i].x) && (ammunition[j].x < Plane[i].x + 192)) && ((ammunition[j].y > Plane[i].y) && (ammunition[j].y < Plane[i].y + 290))) { //当弹药坐标,在敌方飞机的体内;即打中了飞机 ammunition[j].isLive = false; Plane[i].HP--; } if ((Plane[i].Type == Small) && ((ammunition[j].x > Plane[i].x) && (ammunition[j].x < Plane[i].x + 90)) && ((ammunition[j].y > Plane[i].y) && (ammunition[j].y < Plane[i].y + 90))) { //当弹药坐标,在敌方飞机的体内;即打中了飞机 ammunition[j].isLive = false; Plane[i].isLive = false; fighterPlane.killsNumber++; //putimage(ammunition[i].x, ammunition[i].y, &ammunition_Phone[0], SRCINVERT);//后贴原位图); mciSendString("play deathBgm", 0, 0, 0); } } if (Plane[i].HP == 0) { Plane[i].isLive = false; fighterPlane.killsNumber++; } }}int main(){ srand(GetTickCount()); initData(); Welcome();//开始游戏界面 initgraph(X, Y,SHOWCONSOLE);//游戏界面,showconsole这是第三个参数,把控制台输出出来 //会看到一闪闪的;这是因为,在内存中加载的数据,我们的程序一直取,以至于速度过快,图片 //还没有在内存画好,就被取出,让我们看到了绘制的过程;解决方案,使用双缓冲,让图片在缓冲区 //绘制完毕后在取出 BeginBatchDraw(); while(1) { createEnemyPlane(); printGameImage(); FlushBatchDraw();//刷因缓冲区,让在缓冲区绘制好的图片输出出来; moveFighterPlane(); planeDeath(); } EndBatchDraw();//理论上实在这一步才将绘制好的图片输出出来; closegraph(); return 0;}
发表评论
最新留言
留言是一种美德,欢迎回访!
[***.207.175.100]2025年04月01日 14时37分28秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
爬取网易科技滚动新闻
2019-03-05
vuex modules
2019-03-05
phthon基本语法——温习
2019-03-05
sleep、wait、yield、join——简介
2019-03-05
web项目配置
2019-03-05
c语言(基本数据类型)实参与形参传值 用汇编理解
2019-03-05
基于单片机可控音乐流水灯控制设计-全套资料
2019-03-05
基于单片机简易信号误差分析设计-全套资料
2019-03-05
基于单片机简易脉搏测量仪系统设计-毕设课设资料
2019-03-05
并发框架下的“基础类型”——浅析基本类型、ThreadLocal、原子类的线程安全机制
2019-03-05
VHDL代码风格
2019-03-05
Object Clone
2019-03-05
Javascript中String支持使用正则表达式的四种方法
2019-03-05
【IoT】蓝牙BLE基础:CC254x通信系列之模拟SPI协议
2019-03-05
【IoT】TI BLE CC2541 串口控制蓝牙详解
2019-03-05
【产品】项目管理的五个过程和九大知识领域之二
2019-03-05
【Tool】如何使用 Uniflash 烧写 WIFI 芯片 CC3200
2019-03-05
copy_{to, from}_user()的思考
2019-03-05
Web前端安全策略之CSRF的攻击与防御
2019-03-05