首先我们写一下项目所需要的头文件 #include<stdio.h>//标准输入输出头文件#include<math.h>//数学库头文件#include<graphics.h>//EasyX图形库头文件#include<mmsystem.h>//多媒体音乐库头文件#pragma comment(lib,"winmm.lib")//导入winmm库#pragma warning(disable : 4996)//4996警告杂注12345
接下来在涵盖几个宏定义进来 #define WIDTH 600//窗口宽度#define WEIGHT 400//窗口高度#define NUM 10//屏幕上所产生气球,子弹的最大数量12
然后我们开始打造一个漂亮的欢迎界面,这里需要二张图片,我把它贴出来。 1.jpg:
2.jpg:
图片有了,上代码,首先先在全局部分定义图片属性 IMAGE img[2];
void Welcome()//实现欢迎界面的函数{ loadimage(&img[0], "1.jpg",150,400);//加载图片 loadimage(&img[1], "2.jpg", 450, 400);//参数含义(图片地址,图片路径,图片宽,图片高) putimage(0,0,&img[0]);//显示图片 putimage(150, 0, &img[1]);//参数含义(显示坐标x,y+图片属性) while (1) { if (MouseHit())//判断是否有鼠标消息 { MOUSEMSG msg = GetMouseMsg();//获取当前鼠标消息 if (msg.uMsg == WM_LBUTTONDOWN)//如果鼠标左键按下 { return;//返回主函数 } } }}1234567891011121314151617
接下来我们开始准备音乐部分(音乐不方便贴出来,可以自己去网上找合适的) 我们为了不影响程序运行效率,决定使用多线程的方式实现音乐播放 背景音乐: DWORD WINAPI Music(LPVOID lpParamter){ mciSendString("open music.mp3 alias BGM", 0, 0, 0);//加载音乐 mciSendString("play BGM repeat", 0, 0, 0);//播放音乐 //repeat代表无限循环播放 while (1);}123456
气球爆炸音效: DWORD WINAPI MCC(LPVOID lpParamter){ mciSendString("open fill.mp3 alias BCC", 0, 0, 0); mciSendString("play BCC", 0, 0, 0); return 0;}12345
做游戏之前,我们先把游戏中的炮台,气球,子弹这3个元素用结构体抽象出来 struct Battery//炮台{//炮管二点的横纵坐标 int x; int y; int endx; int endy; int len; //炮管发射出来的子弹的速度 int speed; //炮管倾斜的弧度 double radian; //玩家的游戏得分 int score;}bat;struct bullet{//子弹的横纵坐标 int x; int y; //子弹的半径 int r; //子弹向炮管方向的位移量 int vx; int vy; //子弹是否存在的标志 bool falg; //炮管倾斜的弧度 double radian; //子弹的颜色 DWORD color;}ball[NUM];struct Balloon{//气球的横纵坐标 int x; int y; //气球的颜色 DWORD color; //气球是否存在的标志 bool falg;}boon[NUM];1234567891011121314151617181920212223242526272829303132333435363738394041
下面我们来初始化数据 void GameInit(){ srand(GetTickCount()); bat.x = WIDTH / 2; bat.y = WEIGHT - 10; bat.len = 50; bat.endx = bat.x; bat.endy = bat.y-bat.len; bat.speed = 7; bat.score = 0; for (int i = 0; i < NUM; i++) { ball[i].falg = false; }//因为一开始子弹不存在,子弹需要鼠标点击才生成一个,所以我们先初始化为不存在(false代表不存在) for (int i = 0; i < NUM; i++) { //随机坐标 boon[i].x = rand() % (WIDTH-30); boon[i].y = rand() % (WEIGHT-60); //随机颜色 boon[i].color = RGB(rand() % 256, rand() % 256, rand() % 256); boon[i].falg = true; }//因为气球是游戏一开始就要有的,所以我们把它全部初始化为true(存在),并且初始化坐标,颜色}1234567891011121314151617181920212223
接下来我们来把数据变得“看的见” void GameDraw(){ BeginBatchDraw();//开始双缓冲 setbkcolor(RGB(114, 191, 207));//设置窗口背景颜色 cleardevice();//清屏 for (int i = 0; i < NUM; i++)//遍历所有气球(所有包括存在和不存在) { if (boon[i].falg==true)//如果气球存在 { //画气球 setlinestyle(PS_SOLID, 0.1);//设置线条风格 PS_SOLID表示实线,0.1表示线条粗细程度 setlinecolor(WHITE);//设置线条颜色 setfillcolor(boon[i].color);//设置填充颜色 fillellipse(boon[i].x, boon[i].y, boon[i].x + 40, boon[i].y + 50);//画填充椭圆 //画气泡 arc(boon[i].x + 5, boon[i].y + 5, boon[i].x + 35, boon[i].y + 45, 0, 1.4);//画弧 //画绳子 arc(boon[i].x + 15, boon[i].y + 50, boon[i].x + 25, boon[i].y + 75, 5.2, 1.5); } } //画炮台 setlinestyle(PS_SOLID, 2); circle(bat.x,bat.y, 40);//画圆 setfillcolor(BLACK); solidcircle(bat.x, bat.y, 5);//画实心圆 //画炮管 setlinecolor(BLACK); setlinestyle(PS_SOLID, 5); line(bat.x, bat.y, bat.endx, bat.endy); //绘制子弹 for (int i = 0; i < NUM; i++)//遍历所有子弹(所有包括存在和不存在) { if (ball[i].falg == true)//如果子弹存在 { setfillcolor(ball[i].color); solidcircle(ball[i].x, ball[i].y, ball[i].r); } } char bar[80];//初始化字符串 sprintf(bar, "分数:%d", bat.score);//往这个字符串里写东西 setbkmode(0);//去掉文字背景 settextcolor(YELLOW);//设置文字颜色 settextstyle(40, 0, "楷体");//设置文字字体大小和字体样式(注:0表示编译器自适应) outtextxy(0, 0, bar);//设置文字显示位置,以及所需要显示的字符串 EndBatchDraw();//结束双缓冲}123456789101112131415161718192021222324252627282930313233343536373839404142434445
我们的炮管现在还是固定的,我们需要让它跟着鼠标动起来,走,现在我们去实现鼠标操作 void MouseEvent(){ if (MouseHit())//如果有鼠标消息 { MOUSEMSG msg = GetMouseMsg();//获取当前鼠标消息 bat.radian = atan2((double)bat.y - msg.y, (double)msg.x - bat.x);//使用数学的正切求出炮台的炮管的倾斜弧度 //同样使用数学知识求出二条直角边的长度 bat.endx = bat.x + cos(bat.radian) * bat.len;//正弦 bat.endy = bat.y - sin(bat.radian) * bat.len;//余弦 if (msg.uMsg == WM_LBUTTONDOWN)//如果鼠标左键按下 { CreatBullet();//产生子弹 } }}1234567891011121314
那么鼠标操作这一步又涉及到子弹的产生,所以我们一起去看看子弹是如何产生的 //生成子弹void CreatBullet(){ for (int i = 0; i < NUM; i++)//遍历所有子弹(所有包括存在和不存在) { if (ball[i].falg == false)//如果子弹不存在才生成 { ball[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);//颜色 //设置子弹坐标,让子弹从炮管出来 ball[i].x = bat.endx; ball[i].y = bat.endy; ball[i].radian = bat.radian;//让子弹运动方向的水平弧度等于当前炮管的水平弧度 //利用数学的正弦,余弦知识求出子弹的位移量,bat.speed是子弹的速度,一开始已经在炮台的结构体里面定义好了 ball[i].vx = cos(ball[i].radian) * bat.speed; ball[i].vy = sin(ball[i].radian) * bat.speed; //设置子弹的大小(圆的半径) ball[i].r = 5; //最后一定要把子弹改为存在,好让GameDraw函数绘制出来 ball[i].falg = true; //生成完一个子弹一定要break跳出for循环,否则程序会把所有子弹全部生成完,但我只要一个 break; } }}1234567891011121314151617181920212223
子弹生成完了我们也不能让它摆在那里不动,我们得让它飞,所以我们去实现一个函数让子弹移动 void BulletMove(){ for (int i = 0; i < NUM; i++) //遍历所有子弹(所有包括存在和不存在) { if (ball[i].falg==true)//子弹存在才移动 { //ball[i].vx,ball[i].vy我们在生成子弹里面就求出来了,这里我们就直接用 ball[i].x += ball[i].vx; ball[i].y -= ball[i].vy; } }}123456789101112
这时你会发现你打掉(宏定义)NUM个子弹后就没有了,这是什么原因呢?走,我们一起去升级一下这个函数 void BulletMove(){ for (int i = 0; i < NUM; i++) //遍历所有子弹(所有包括存在和不存在) { if (ball[i].falg==true)//子弹存在才移动 { //ball[i].vx,ball[i].vy我们在生成子弹里面就求出来了,这里我们就直接用 ball[i].x += ball[i].vx; ball[i].y -= ball[i].vy; if (ball[i].x<0 || ball[i].x>WIDTH || ball[i].y<0 || ball[i].y>WEIGHT) { ball[i].falg = false; }//如果子弹打出窗口了,就把该子弹设置为不存在,这样鼠标点击就可以继续生成子弹了(保证窗口一直可以容纳(宏定义)NUM个子弹) } }}12345678910111213141516
子弹移动了该气球了 //气球移动void BalloonMove(int speed){ for (int i = 0; i < NUM; i++)//遍历所有气球(所有涵盖存在和不存在) { if (boon[i].falg == true)//如果气球存在 { // %50的概率,让气球随风摇摆 if (rand() % 2 == 1) { boon[i].x++; } else { boon[i].x--; } //气球上升 boon[i].y -= speed; }}12345678910111213141516171819
然后我们会发现会产生与子弹移动同样的问题,气球一去不复返,所以我们升级一下这个函数 //气球移动void BalloonMove(int speed){ for (int i = 0; i < NUM; i++)//遍历所有气球(所有涵盖存在和不存在) { if (boon[i].falg == true)//如果气球存在 { // %50的概率,让气球随风摇摆 if (rand() % 2 == 1) { boon[i].x++; } else { boon[i].x--; } //气球上升 boon[i].y -= speed; if (boon[i].x-40<0 || boon[i].x>WIDTH+40 || boon[i].y+50<0 || boon[i].y>WEIGHT)//如果气球升到了窗口之外 { boon[i].falg = false;//把气球设置为不存在 CreatBall();//调用产生气球的函数(气球飞离窗口) } }}123456789101112131415161718192021222324
好,接下来我们来实现一下产生气球的函数 //产生气球void CreatBall(){ for (int i = 0; i < NUM; i++)//遍历所有气球(所有涵盖存在与不存在) { if (boon[i].falg == false)//如果气球不存在 { //随机气球坐标 boon[i].x = rand() % (WIDTH - 30); boon[i].y = (rand() % 100) + WEIGHT; //随机气球颜色 boon[i].color = RGB(rand() % 256, rand() % 256, rand() % 256); //生成完成把气球设置为存在 boon[i].falg = true; break;//同理,只需要一个,break } }}1234567891011121314151617
现在一切准备就绪,开始打气球 //开始游戏void PlayGame(){ for (int i = 0; i < NUM; i++)//遍历所有气球(所有涵盖存在和不存在) { if (boon[i].falg == false)//如果气球不存在 continue;//跳过此次循环剩余部分,执行下一次循环判定 for (int j = 0; j < NUM; j++) //遍历所有子弹(所有涵盖存在和不存在) { if (ball[j].falg == false)//如果子弹不存在 continue;//跳过此次循环剩余部分,执行下一次循环判定 if (ball[j].x > boon[i].x && ball[j].x<boon[i].x + 40 && ball[j].y>boon[i].y && ball[j].y < boon[i].y + 50) //如果子弹与气球重叠(打到了气球) { CreateThread(NULL, 0, MCC, NULL, 0, NULL);//执行多线程,播放气球爆炸音效 ball[j].falg = false;//子弹设置为不存在 boon[i].falg = false;//气球设置为不存在 bat.score++;//玩家分数加 1 CreatBall();//生成气球(气球被打) } } }}123456789101112131415161718192021222324
下面我们把主函数(程序的入口,程序的主干)写出来 int main(){ initgraph(WIDTH, WEIGHT);//创建窗口 CreateThread(NULL, 0, Music, NULL, 0, NULL);//调用多线程,循环播放背景音乐 Welcome();//欢迎界面 GameInit();//数据初始化 while (1)//死循环 { MouseEvent();//鼠标操作 GameDraw();//绘制(让游戏可视化) BulletMove();//子弹移动 BalloonMove(1);//气球移动 Sleep(1);//适当延时 PlayGame();//开始打气球 } getchar();//防止闪屏}12345678910111213141516
最后,博主送你源代码,感谢你认真的观看(因为我自己写代码很少注释,所以源代码注释少)但上面的详解可是注释慢慢,为了方便大家理解。 #include<stdio.h>#include<math.h>#include<graphics.h>#include<mmsystem.h>#pragma comment(lib,"winmm.lib")#pragma warning(disable : 4996)#define WIDTH 600#define WEIGHT 400#define NUM 10struct Battery{ int x; int y; int endx; int endy; int len; int speed; double radian; int score;}bat;struct bullet{ int x; int y; int r; int vx; int vy; bool falg; double radian; DWORD color;}ball[NUM];struct Balloon{ int x; int y; DWORD color; bool falg;}boon[NUM];IMAGE img[2];DWORD WINAPI MCC(LPVOID lpParamter){ mciSendString("open fill.mp3 alias BCC", 0, 0, 0); mciSendString("play BCC", 0, 0, 0); return 0;}DWORD WINAPI Music(LPVOID lpParamter){ mciSendString("open music.mp3 alias BGM", 0, 0, 0); mciSendString("play BGM repeat", 0, 0, 0); while (1);}void Welcome(){ loadimage(&img[0], "1.jpg",150,400); loadimage(&img[1], "2.jpg", 450, 400); putimage(0,0,&img[0]); putimage(150, 0, &img[1]); while (1) { if (MouseHit()) { MOUSEMSG msg = GetMouseMsg(); if (msg.uMsg == WM_LBUTTONDOWN) { return; } } }}void GameInit(){ srand(GetTickCount()); bat.x = WIDTH / 2; bat.y = WEIGHT - 10; bat.len = 50; bat.endx = bat.x; bat.endy = bat.y-bat.len; bat.speed = 7; bat.score = 0; for (int i = 0; i < NUM; i++) { ball[i].falg = false; } for (int i = 0; i < NUM; i++) { boon[i].x = rand() % (WIDTH-30); boon[i].y = rand() % (WEIGHT-60); boon[i].color = RGB(rand() % 256, rand() % 256, rand() % 256); boon[i].falg = true; }}void GameDraw(){ BeginBatchDraw(); setbkcolor(RGB(114, 191, 207)); cleardevice(); for (int i = 0; i < NUM; i++) { if (boon[i].falg==true) { //画气球 setlinestyle(PS_SOLID, 0.1); setlinecolor(WHITE); setfillcolor(boon[i].color); fillellipse(boon[i].x, boon[i].y, boon[i].x + 40, boon[i].y + 50); //画气泡 arc(boon[i].x + 5, boon[i].y + 5, boon[i].x + 35, boon[i].y + 45, 0, 1.4); //画绳子 arc(boon[i].x + 15, boon[i].y + 50, boon[i].x + 25, boon[i].y + 75, 5.2, 1.5); } } //画炮台 setlinestyle(PS_SOLID, 2); circle(bat.x,bat.y, 40); setfillcolor(BLACK); solidcircle(bat.x, bat.y, 5); //画炮管 setlinecolor(BLACK); setlinestyle(PS_SOLID, 5); line(bat.x, bat.y, bat.endx, bat.endy); //绘制子弹 for (int i = 0; i < NUM; i++) { if (ball[i].falg == true) { setfillcolor(ball[i].color); solidcircle(ball[i].x, ball[i].y, ball[i].r); } } char bar[80]; sprintf(bar, "分数:%d", bat.score); setbkmode(0); settextcolor(YELLOW); settextstyle(40, 0, "楷体"); outtextxy(0, 0, bar); EndBatchDraw();}//产生气球void CreatBall(){ for (int i = 0; i < NUM; i++) { if (boon[i].falg == false) { boon[i].x = rand() % (WIDTH - 30); boon[i].y = (rand() % 100) + WEIGHT; boon[i].color = RGB(rand() % 256, rand() % 256, rand() % 256); boon[i].falg = true; break; } }}//开始游戏void PlayGame(){ for (int i = 0; i < NUM; i++) { if (boon[i].falg == false) continue; for (int j = 0; j < NUM; j++) { if (ball[j].falg == false) continue; if (ball[j].x > boon[i].x && ball[j].x<boon[i].x + 40 && ball[j].y>boon[i].y && ball[j].y < boon[i].y + 50) { CreateThread(NULL, 0, MCC, NULL, 0, NULL); ball[j].falg = false; boon[i].falg = false; bat.score++; CreatBall(); } } }}//气球移动void BalloonMove(int speed){ for (int i = 0; i < NUM; i++) { if (boon[i].falg == true) { if (rand() % 2 == 1) { boon[i].x++; } else { boon[i].x--; } boon[i].y -= speed; if (boon[i].x-40<0 || boon[i].x>WIDTH+40 || boon[i].y+50<0 || boon[i].y>WEIGHT) { boon[i].falg = false; CreatBall(); } } }}//生成子弹void CreatBullet(){ for (int i = 0; i < NUM; i++) { if (ball[i].falg == false) { ball[i].color = RGB(rand() % 256, rand() % 256, rand() % 256); ball[i].x = bat.endx; ball[i].y = bat.endy; ball[i].radian = bat.radian; ball[i].vx = cos(ball[i].radian) * bat.speed; ball[i].vy = sin(ball[i].radian) * bat.speed; ball[i].r = 5; ball[i].falg = true; break; } }}//移动子弹void BulletMove(){ for (int i = 0; i < NUM; i++) { if (ball[i].falg==true) { ball[i].x += ball[i].vx; ball[i].y -= ball[i].vy; if (ball[i].x<0 || ball[i].x>WIDTH || ball[i].y<0 || ball[i].y>WEIGHT) { ball[i].falg = false; } } }}//鼠标操作void MouseEvent(){ if (MouseHit()) { MOUSEMSG msg = GetMouseMsg(); bat.radian = atan2((double)bat.y - msg.y, (double)msg.x - bat.x); bat.endx = bat.x + cos(bat.radian) * bat.len; bat.endy = bat.y - sin(bat.radian) * bat.len; if (msg.uMsg == WM_LBUTTONDOWN) { CreatBullet(); } }}int main(){ initgraph(WIDTH, WEIGHT); CreateThread(NULL, 0, Music, NULL, 0, NULL); Welcome(); GameInit(); while (1) { MouseEvent(); GameDraw(); BulletMove(); BalloonMove(1); Sleep(1); PlayGame(); } getchar();}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
|