首先我们写一下项目所需要的头文件
#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