
此处主要讲:如何开发一个连续的烟花弹上升并进行烟花爆炸的程序.
第一步自然是创建窗口,设置窗口宽度为1200,高度为800(#include
initgraph(1200, 800);//创建窗口,宽度1200,高度800
第二步我们需要为这个窗口添加背景音乐,这里我们添加了一首"周杰伦的告白气球.mp3",打开并且播放.因为我们并不需要暂停或者关闭,所以便注释掉对应的程序.
注意,添加音乐的时候要记住添加头文件
- #include
//媒体头文件 -
- #pragma comment(lib,"winmm.lib")//媒体库文件
- mciSendString("open 告白气球.mp3",0,0,0);
- mciSendString("play 告白气球.mp3", 0, 0, 0);
- //mciSendString("pause 周杰伦 - 告白气球(Live).mp3", 0, 0, 0);
- //mciSendString("close 周杰伦 - 告白气球(Live).mp3", 0, 0, 0);
第三步我们开始在窗口上面添加文本,记不记得学习C++的第一个程序,"hello,world!"
这里类同,只不过是讲对应的文本输入到窗口对应的位置.程序如下:
- settextcolor(YELLOW);//设置字体的颜色
- settextstyle(25,0,"微软雅黑");//设置字体的字号
- //outtextxy(320,240,"赵哥真帅");//在创建的窗口,(320,240)的位置的输入赵哥真帅
- outtextxy(400, 200, "XXXXXXXXXX");
- outtextxy(400, 250, "XXXXXXXXXX");
- outtextxy(400, 300, "XXXXXXXXXX");
- outtextxy(400, 350, "XXXXXXXXXX");
- outtextxy(400, 400, "XXXXXXXXXX");
- outtextxy(600, 450, "------小子");
然后在显示的时候,我们可以给这段文本延时5s
Sleep(5000);
文本结束后,就开始进入烟花弹和烟花的爆炸现场.
第四步,定义烟花弹和烟花
- //烟花弹
- struct Jet//烟花弹属性
- {
- int x, y;//当前坐标
- int hx, hy;//最高点的坐标
- unsigned long t1, t2, dt;//时间来控制速度
- IMAGE img;//保存烟花弹的图片
- bool isshoot;//烟花弹是否属于正在上升的状态
- }jet;
-
- //烟花
- struct Fire//从中心往外面进行拓展
- {
- int r;//当前半径
- int maxr;//最大半径
- int x, y;//中心点的左边//窗口
- int cx, cy;//中心点左边,坐标系不同//图片
- int xy[240][240]; //保存图片当中每一个像素点
- bool isboom;//是否开始爆炸
- bool isdram;//是否开始显示
- unsigned long t1, t2, dt; //爆炸的速度
- }fire;
简单解释:烟花弹属性包括了他的当前坐标和最高坐标,因为烟花弹是垂直向上的,所以速度,采用时间t1,t2和dt来进行描述,IMAGE用来保存烟花弹的图片.并定义一个布尔类型isshoot,判断烟花弹是否处于上升状态
关于IMAGE函数的有关解释,可以参考:EasyX 文档 - IMAGE
烟花的基本属性在于:要明白烟花爆炸是从一个中心点,向外速度越来越慢地拓展开来的,因此我们定义烟花的当前半径和最大半径,烟花爆炸的点相对于窗口的坐标,和烟花爆炸上面点相对于图片的坐标,及其设定一个二维数组用于保存图片当中每一个像素点.最后定义两个布尔类型,判断烟花是否爆炸和显示.
第五步 初始化烟花弹和烟花
- //初始化烟花弹
- void startjet()
- {
- jet.x = rand() % (1200 - 20);
- jet.y = 750;
- jet.hx = jet.x;//垂直上升
- jet.hy = rand() % 400;//0-399爆炸
- jet.t1 = GetTickCount(); //获取系统时间
- jet.dt = 10;//dt 10ms
- jet.isshoot = true;
- loadimage(&jet.img, "jet.jpg", 20, 50);
- putimage(jet.x, jet.y, &jet.img, SRCINVERT);
- }
-
- //初始化烟花
- void startfire() {
- fire.r=0;//当前半径
- fire.maxr=120;//最大半径
- fire.x=0, fire.y=0;//中心点的左边//窗口//等于烟花弹最高点的坐标
- fire.cx=120, fire.cy=120;//中心点左边,坐标系不同//图片
- fire.isboom=false;//是否开始爆炸
- fire.isdram=false;//是否开始显示
- fire.t1 = GetTickCount();
- fire.dt = 5; //爆炸的速度
- fire.xy[239][239]; //保存图片当中每一个像素点
- IMAGE fimg;
- loadimage(&fimg, "flower.jpg", 240,240);
-
- SetWorkingImage(&fimg);//获取fimg地址的图片
- for (int a = 0; a < 240; a++)//把图片的所有像素点定义在一个数组当中
- {
- for (int b = 0; b < 240; b++)
- {
- fire.xy[a][b]=getpixel(a, b);//窗口获取(a,b)像素点
-
- }
- }
- SetWorkingImage();//放开对应的图片
- }
其次对应上面定义的烟花和烟花弹的结构,对其进行初始状态定义.此处要注意,烟花和烟花弹图片显示的方式不同,烟花弹是作为一个整体,垂直上升的,而烟花是从一个点,爆炸开来的,显示的是每一个范围的像素图.
第六步 发射烟花弹
- void shootjet()
- {
- //biubiubiu
- jet.t2 = GetTickCount();//t2
- if (jet.t2 - jet.t1 > jet.dt && jet.isshoot == true)
- {
- putimage(jet.x, jet.y, &jet.img, SRCINVERT);
-
- if (jet.y > jet.hy)//停止在这个烟花的最高位置附近,然后要准备进行爆炸
- jet.y -= 5;
- putimage(jet.x, jet.y, &jet.img, SRCINVERT);//y-5之后再演示烟花弹下一个位置
-
- if (jet.y <= jet.hy)//到达最高点了
- {
- //擦除烟花弹
- putimage(jet.x, jet.y, &jet.img, SRCINVERT);
- fire.isboom = true;//开始爆炸烟花
- jet.isshoot = false;
- fire.x = jet.x;
- fire.y = jet.y+10;
- //showfire(pMem);
-
- }
- if (jet.isshoot == false)
- {
- //再次初始化烟花弹
- startjet();
- //fire.isdram = false;
- }
- jet.t1 = jet.t2;
- }
- }
当到达最高点的时候,要将烟花弹擦除,并且记录下此时的坐标,将其赋给烟花相对于窗口的中心点坐标,开始爆炸烟花,如果烟花弹的上升状态转换为false了,那么便再设定一个新的发射烟花弹函数,也就是回溯的调用,最后要记住循环完,将t2的数值赋值给t1;确保能够一直循环运行下去.
运行到这里,就可以看到一个又一个烟花弹上升并擦除的界面了.
第七步 烟花爆炸
先设定一个时间数组,用来控制自内向外爆炸的时间长度,越往外面速度越慢
- //爆炸烟花
- void showfire(DWORD *pMem)
- {
- //dt数组
- //爆炸速度:在半径小的时候爆炸特别快
- //半径越大,爆炸越来越慢
- int drt[12] = { 5,5,5,6,6,15,25,25,25,55,55,65 };//12个为什么?120%10
- fire.t2 = GetTickCount();
- if (fire.t2 - fire.t1 > fire.dt && fire.isboom == true)
- {
- if (fire.r < fire.maxr)
- {
- fire.r++;//半径不断地变大
- fire.dt = drt[fire.r / 10];
- fire.isdram = true;
- }
- if (fire.r >= fire.maxr - 1)
- {
- fire.isdram = false;
- fire.isboom = false;
- fire.dt = 5;
- fire.t1 = GetTickCount();
- fire.r = 0;
- }
- fire.t1 = fire.t2;
- }
- if (fire.isdram == true)//可以开始绘制
- {
- //628次 2Π
- for (double a = 0; a <= 6.28; a += 0.01)
- {
- //相对于图片左上角的坐标
- int x1 = fire.cx + fire.r * cos(a);
- int y1 = fire.cy - fire.r * sin(a);
- //x1 y1 628个来自于图片的像素点坐标<240
- fire.xy[x1][y1];//存储上面的像素点坐标
-
- if (x1 > 0 && x1 < 240 && y1>0 && y1 < 240 ) //图片像素是240*240,所以只输出像素内的点
- {
- int b = fire.xy[x1][y1] & 0xff;
- int g = (fire.xy[x1][y1] >> 8) & 0xff;
- int r = (fire.xy[x1][y1] >> 16);
- //烟花像素点在窗口上的坐标
- int xx = fire.x + fire.r * cos(a);
- int yy = fire.y - fire.r * sin(a);
- // 较暗的像素点不输出、防止越界
- if (r > 0x20 && g > 0x20 && b > 0x20 && xx > 0 && xx < 1200 && yy > 0 && yy < 800)
- pMem[yy * 1200 + xx] = BGR(fire.xy[x1][y1]); // 显存操作绘制烟花
-
- }
-
- }
- }
-
- fire.isdram = false;
- }
如果fire.isdram==true的时候,也就是可以开始绘制爆炸烟花了.循环当中采用了数学函数,可以定义到每一个像素点的坐标,然后再根据前面烟花弹停止上升的最高点,来确定烟花像素点在窗口上的坐标.设定一个条件判断,如果像素点较暗,则不输出,其他像素点输出.
最后在每一次循环结束的时候要将isdram设置成false
最后,运行主函数
- int main()
- {
- srand(time(0));
- initgraph(1200, 800);//创建窗口,宽度1200,高度800
-
- mciSendString("open 告白气球.mp3",0,0,0);
- mciSendString("play 告白气球.mp3", 0, 0, 0);
- //mciSendString("pause 周杰伦 - 告白气球(Live).mp3", 0, 0, 0);
- //mciSendString("close 周杰伦 - 告白气球(Live).mp3", 0, 0, 0);
- //mci send string
-
- settextcolor(YELLOW);//设置字体的颜色
- settextstyle(25,0,"微软雅黑");//设置字体的字号
- //outtextxy(320,240,"赵哥真帅");//在创建的窗口,(320,240)的位置的输入赵哥真帅
- outtextxy(400, 200, "XXXXXXXXXX");
- outtextxy(400, 250, "XXXXXXXXXX");
- outtextxy(400, 300, "XXXXXXXXXX");
- outtextxy(400, 350, "XXXXXXXXXX");
- outtextxy(400, 400, "XXXXXXXXXX");
- outtextxy(600, 450, "------小子");
-
- int uuu=getchar();//按回车键继续
- Sleep(5000);
- //DWORD st1 = timeGetTime(); // 播放花样计时
- DWORD* pMem = GetImageBuffer(); // 获取窗口显存指针
- //初始化烟花弹
- startjet();
- //初始化烟花
- startfire();
- while (1)
- {
- Sleep(1);
- // 随机选择 4000 个像素点擦除
- for (int clr = 0; clr < 2000; clr++)
- {
-
- for (int j = 0; j < 2; j++)
- {
- int px1 = rand() % 1200;
- int py1 = rand() % 800;
-
- if (py1 < 799) // 防止越界
- pMem[py1 * 1200 + px1] = pMem[py1 * 1200 + px1 + 1] = BLACK; // 对显存赋值擦出像素点
-
- //Style(st1);
- }
- shootjet();
-
- showfire(pMem);
-
- }
- //Style(st1);
- FlushBatchDraw(); // 显示前面的所有绘图操作
-
- }
-
-
- while (1);
- return 0;
- }
总代码和相关素材如下:
- #pragma warning(disable:4996)//忽略4996错误提示
- #pragma comment(linker,"/entry:mainCRTStartup /subsystem:windows")
- #include <graphics.h>
- #include <stdio.h>
- #include <conio.h>
- #include <easyx.h>//图形界面库
- #include<time.h>
- #include<math.h>
- #include<string.h>
- //歌曲需要的头文件
- #include<mmsystem.h>//媒体头文件
-
- #pragma comment(lib,"winmm.lib")//媒体库文件
- //烟花弹
- struct Jet//烟花弹属性
- {
- int x, y;//当前坐标
- int hx, hy;//最高点的坐标
- unsigned long t1, t2, dt;//时间来控制速度
- IMAGE img;//保存烟花弹的图片
- bool isshoot;//烟花弹是否属于正在上升的状态
- }jet;
-
- //烟花
- struct Fire//从中心往外面进行拓展
- {
- int r;//当前半径
- int maxr;//最大半径
- int x, y;//中心点的左边//窗口
- int cx, cy;//中心点左边,坐标系不同//图片
- int xy[240][240]; //保存图片当中每一个像素点
- bool isboom;//是否开始爆炸
- bool isdram;//是否开始显示
- unsigned long t1, t2, dt; //爆炸的速度
- }fire;
-
- //初始化烟花弹
- void startjet()
- {
- jet.x = rand() % (1200 - 20);
- jet.y = 750;
- jet.hx = jet.x;//垂直上升
- jet.hy = rand() % 400;//0-399爆炸
- jet.t1 = GetTickCount(); //获取系统时间
- jet.dt = 10;//dt 10ms
- jet.isshoot = true;
- loadimage(&jet.img, "jet.jpg", 20, 50);
- putimage(jet.x, jet.y, &jet.img, SRCINVERT);
- }
-
- //初始化烟花
- void startfire() {
- fire.r=0;//当前半径
- fire.maxr=120;//最大半径
- fire.x=0, fire.y=0;//中心点的左边//窗口//等于烟花弹最高点的坐标
- fire.cx=120, fire.cy=120;//中心点左边,坐标系不同//图片
- fire.isboom=false;//是否开始爆炸
- fire.isdram=false;//是否开始显示
- fire.t1 = GetTickCount();
- fire.dt = 5; //爆炸的速度
- fire.xy[239][239]; //保存图片当中每一个像素点
- IMAGE fimg;
- loadimage(&fimg, "flower.jpg", 240,240);
-
- SetWorkingImage(&fimg);//获取fimg地址的图片
- for (int a = 0; a < 240; a++)//把图片的所有像素点定义在一个数组当中
- {
- for (int b = 0; b < 240; b++)
- {
- fire.xy[a][b]=getpixel(a, b);//窗口获取(a,b)像素点
-
- }
- }
- SetWorkingImage();//放开对应的图片
- }
-
- //发射烟花弹
- void shootjet()
- {
- //biubiubiu
- jet.t2 = GetTickCount();//t2
- if (jet.t2 - jet.t1 > jet.dt && jet.isshoot == true)
- {
- putimage(jet.x, jet.y, &jet.img, SRCINVERT);
-
- if (jet.y > jet.hy)//停止在这个烟花的最高位置附近,然后要准备进行爆炸
- jet.y -= 5;
- putimage(jet.x, jet.y, &jet.img, SRCINVERT);//y-5之后再演示烟花弹下一个位置
-
- if (jet.y <= jet.hy)//到达最高点了
- {
- //擦除烟花弹
- putimage(jet.x, jet.y, &jet.img, SRCINVERT);
- fire.isboom = true;//开始爆炸烟花
- jet.isshoot = false;
- fire.x = jet.x;
- fire.y = jet.y+10;
- //showfire(pMem);
-
- }
- if (jet.isshoot == false)
- {
- //再次初始化烟花弹
- startjet();
- //fire.isdram = false;
- }
- jet.t1 = jet.t2;
- }
- }
- //爆炸烟花
- void showfire(DWORD *pMem)
- {
- //dt数组
- //爆炸速度:在半径小的时候爆炸特别快
- //半径越大,爆炸越来越慢
- int drt[12] = { 5,5,5,6,6,15,25,25,25,55,55,65 };//12个为什么?120%10
- fire.t2 = GetTickCount();
- if (fire.t2 - fire.t1 > fire.dt && fire.isboom == true)
- {
- if (fire.r < fire.maxr)
- {
- fire.r++;//半径不断地变大
- fire.dt = drt[fire.r / 10];
- fire.isdram = true;
- }
- if (fire.r >= fire.maxr - 1)
- {
- fire.isdram = false;
- fire.isboom = false;
- fire.dt = 5;
- fire.t1 = GetTickCount();
- fire.r = 0;
- }
- fire.t1 = fire.t2;
- }
- if (fire.isdram == true)//可以开始绘制
- {
- //628次 2Π
- for (double a = 0; a <= 6.28; a += 0.01)
- {
- //相对于图片左上角的坐标
- int x1 = fire.cx + fire.r * cos(a);
- int y1 = fire.cy - fire.r * sin(a);
- //x1 y1 628个来自于图片的像素点坐标<240
- fire.xy[x1][y1];//存储上面的像素点坐标
-
- if (x1 > 0 && x1 < 240 && y1>0 && y1 < 240 ) //图片像素是240*240,所以只输出像素内的点
- {
- int b = fire.xy[x1][y1] & 0xff;
- int g = (fire.xy[x1][y1] >> 8) & 0xff;
- int r = (fire.xy[x1][y1] >> 16);
- //烟花像素点在窗口上的坐标
- int xx = fire.x + fire.r * cos(a);
- int yy = fire.y - fire.r * sin(a);
- // 较暗的像素点不输出、防止越界
- if (r > 0x20 && g > 0x20 && b > 0x20 && xx > 0 && xx < 1200 && yy > 0 && yy < 800)
- pMem[yy * 1200 + xx] = BGR(fire.xy[x1][y1]); // 显存操作绘制烟花
-
- }
-
- }
- }
-
- fire.isdram = false;
- }
-
-
- // 显示花样 //本程序不显示
- void Style(DWORD& st1)
- {
- DWORD st2 = timeGetTime();
-
- if (st2 - st1 > 2000) // 一首歌的时间
- {
- // 心形坐标
- int x[13] = { 60, 75, 91, 100, 95, 75, 60, 45, 25, 15, 25, 41, 60 };
- int y[13] = { 65, 53, 40, 22, 5, 4, 20, 4, 5, 22, 40, 53, 65 };
- for (int i = 0; i < 13; i++)
- {
- //cleardevice();
- /**** 规律分布烟花弹 ***/
- jet.x = x[i] * 10;
- jet.y = (y[i] + 75) * 10;
- jet.hx = jet.x;
- jet.hy = y[i] * 10;
- jet.isshoot = true;
- jet.dt = 5;
- putimage(jet.x, jet.y, &jet.img, SRCINVERT); // 显示烟花弹
-
- /**** 设置烟花参数 ***/
- fire.x = jet.x + 10;
- fire.y = jet.hy;
- //fire.isdram = false;
- //fire.r = 0;
-
- /**** 播放发射声音 ***/
- /*char c1[50], c2[30], c3[30];
- sprintf(c1, "open ./fire/shoot.mp3 alias s%d", i);
- sprintf(c2, "play s%d", i);
- sprintf(c3, "close n%d", i);
-
- mciSendString(c3, 0, 0, 0);
- mciSendString(c1, 0, 0, 0);
- mciSendString(c2, 0, 0, 0);*/
- }
- st1 = st2;
- }
- }
-
-
-
-
- int main()
- {
- srand(time(0));
- initgraph(1200, 800);//创建窗口,宽度1200,高度800
-
- mciSendString("open 告白气球.mp3",0,0,0);
- mciSendString("play 告白气球.mp3", 0, 0, 0);
- //mciSendString("pause 周杰伦 - 告白气球(Live).mp3", 0, 0, 0);
- //mciSendString("close 周杰伦 - 告白气球(Live).mp3", 0, 0, 0);
- //mci send string
-
- settextcolor(YELLOW);//设置字体的颜色
- settextstyle(25,0,"微软雅黑");//设置字体的字号
- //outtextxy(320,240,"赵哥真帅");//在创建的窗口,(320,240)的位置的输入赵哥真帅
- outtextxy(400, 200, "XXXXXXXXXX");
- outtextxy(400, 250, "XXXXXXXXXX");
- outtextxy(400, 300, "XXXXXXXXXX");
- outtextxy(400, 350, "XXXXXXXXXX");
- outtextxy(400, 400, "XXXXXXXXXX");
- outtextxy(600, 450, "------小子");
-
- int uuu=getchar();//按回车键继续
- Sleep(5000);
- //DWORD st1 = timeGetTime(); // 播放花样计时
- DWORD* pMem = GetImageBuffer(); // 获取窗口显存指针
- //初始化烟花弹
- startjet();
- //初始化烟花
- startfire();
- while (1)
- {
- Sleep(1);
- // 随机选择 4000 个像素点擦除
- for (int clr = 0; clr < 2000; clr++)
- {
-
- for (int j = 0; j < 2; j++)
- {
- int px1 = rand() % 1200;
- int py1 = rand() % 800;
-
- if (py1 < 799) // 防止越界
- pMem[py1 * 1200 + px1] = pMem[py1 * 1200 + px1 + 1] = BLACK; // 对显存赋值擦出像素点
-
- //Style(st1);
- }
- shootjet();
-
- showfire(pMem);
-
- }
- //Style(st1);
- FlushBatchDraw(); // 显示前面的所有绘图操作
-
- }
-
-
- while (1);
- return 0;
- }

