一.程序展示
二.程序简介
青春就像一场烟花,美得让人窒息,却短暂得让人流泪。
我们该如何用代码记录下现实中的烟花,让它成为永恒呢?
首先把烟花分成两个阶段:烟花弹升空和烟花绽放。
然后把烟花弹(jet)和烟花(flower)各作为一个对象,再思考它们各自的属性。
为了实现多个烟花的效果,创建烟花弹数组和烟花数组。
烟花弹和烟花各自的对象数组大小都为13(为了实现爱心烟花的操作才取13)
三.烟花弹升空
3.1烟花弹的属性
1.我们用initgraph()创建一个窗口,烟花弹要有一个升空,再绽放的动态变化,
那么它应该具有int x,y,hy,t1,t2,dt这些代表着运动的属性,bool shoot这样的发射开关,
IMAGE jet_picture[2]这样储存图片的变量(为了达到闪烁的效果,我们准备一明一暗
两张烟花图片,交替变化,顺便引出byte n:1作为变化的枢纽)
2.储存烟花弹的操作
IMAGE JET_PICTURE;
loadimage(&JET_PICTURE, "./jet.jpg", 200, 50);
SetWorkingImage(&JET_PICTURE);
int choice_jet = rand() % 5;
getimage(&jet_picture[0], choice_jet * 20, 0, 20, 50);
getimage(&jet_picture[1], (choice_jet + 5) * 20, 0, 20, 50);
3.2烟花弹发射的思路
1.烟花弹一个接一个地升空,一个接一个地绽放,仔细一想,
好像是一个烟花弹升空再绽放作为一个线程,多个线程一起运行。
2.再仔细一想,如果我们创建一个烟花弹对象数组,然后再来一个
循环遍历这个数组,如果这个烟花弹可发射(shoot==true),那就令(y-=5),
再加个(t2-t1>=dt)限制速度,到达最高点hy后重置这个烟花弹,然后把此时的坐标x,y
传给烟花这个对象,让它开始绽放。再在这个循环外面套个循环,
每次都让可发射的烟花弹上升一点,这样整体看来就达到了并行的效果。
3.还有两个关键问题:每次该让多少烟花弹发射呢,这些发射的烟花弹
是在同一高度,以同一速度上升,这样太僵硬了,因此,我们需要设计一种算法。
4.我们声明两个静态变量static int shoot_need = 0, shoot_reality = 0,
由上面的讲解,我们知道外部是有一个循环的,每次该循环遍历时,
令shoot_need = rand() % 3 + 2,当 (shoot_reality < shoot_need)时,
声明一个局部变量int i = rand() % 13,然后随机访问一个烟花对象,
如果它原先没有发射,就令shoot=true,并把它输出在窗口上,然后shoot_reality++,
直到与需要的烟花弹数量相等时退出循环。
5.当然,烟花弹上升到最高点重置时,shoot_reality--。
shoot_need = rand() % 3 + 2;
while (shoot_reality < shoot_need)
{
int i = rand() % 13;
if (!jet[i].shoot)
{
jet[i].shoot = true;
shoot_reality++;
jet[i].t1 = GetTickCount();
putimage(jet[i].x, jet[i].y, &jet[i].jet_picture[jet[i].n], SRCINVERT);
}
}
6.我们在初始化烟花发射(shoot=true)时,已经把烟花弹的图片输出到了窗口上,
接下来判断(t2-t1>=dt),若为真则调用putimage()擦除上个位置的烟花,
令t1=t2重置时间;然后再判断(y>hy),若还为真,则上升(y-=5),再调用
putimage()在下个位置显示烟花弹。若为假,则把此时烟花弹的坐标传给
烟花,并重置烟花弹的属性。
四.烟花绽放
4.1烟花的属性
- 我们观察现实中的烟花,会发现它的绽放过程近似于以空间中的一点
作为圆心,半径从0开始不断变大,即以规则的圆形放大,直到最大半径,
然后缓慢消失。
- 那么它应该具有int x,y,r,cen_x,cen_y,max_r这些运动的属性,bool bloom
这样的开关, DWORD flower[240][240]这样储存烟花上的像素点数组。
(因为我们要把烟花上的像素点以圆形扩散的形式输出到窗口上,图片本身是有大小的
cen_x=cen_y=120,它们是图片中的圆心;x和y作为窗口上烟花绽放的圆心,
即烟花弹到最高点的坐标,另外max_r=120。)
3.当然你可以加入t1,t2,dt这些运动的属性来让烟花变速绽放。
- 储存烟花弹上的像素点的操作
IMAGE FLOWER, Flower;
loadimage(&FLOWER, "./flower.jpg", 3120, 240);
SetWorkingImage(&FLOWER);
static int choice_flower = 0;
getimage(&Flower, choice_flower * 240, 0, 240, 240);
choice_flower++;
SetWorkingImage(&Flower);
for (int y = 0; y < 240; y++)
for (int x = 0; x < 240; x++)
flower[y][x] = getpixel(y, x);
4.2烟花弹绽放的思路
- 我们声明一个指针存储绘图设备的显示缓冲区。
DWORD* Drawing_window = GetImageBuffer();
然后我们可以通过它来向窗口上输出像素点。
由上述讲解可知,在一个烟花弹上升到最高点消失再重置时,
它向一个烟花对象传来了它消失点的坐标,并且会让这个烟花对象的bloom为真,
我们就以这个坐标为烟花绽放的圆心。
flower[i].x = jet[i].x;
flower[i].y = jet[i].y;
flower[i].bloom = true;
ResetJet(i);
- 我们需要一个循环遍历烟花对象数组,如果r<max_r,并且bloom==true,
就令r++。
- 接下来是最关键的:将图片中的烟花的像素以圆形扩散的形式输出到窗口上。
for (double a = 0; a <= 2 * PI; a += 0.01)
{//图形上像素点的坐标
int img_x= (int) (flower[i].cen_x + flower[i].r * cos(a));
int img_y = (int)(flower[i].cen_y + flower[i].r * sin(a));
if (img_x > 0 && img_x < 240 && img_y>0 && img_y < 240)
{
//接近黑色的像素点不输出
int b = flower[i].flower[img_y][img_x] & 0xff;
int g = (flower[i].flower[img_y][img_x] >> 8) & 0xff;
int r = (flower[i].flower[img_y][img_x] >> 16);
//窗口上像素点的坐标
int win_x = (int)(flower[i].x + flower[i].r * cos(a));
int win_y = (int)(flower[i].y + flower[i].r * sin(a));
if (win_x > 0 && win_x < WIDTH && win_y>0 && win_y < HEIGHT && b>0x20 && g > 0x20 && r > 0x20)
Drawing_window[win_y * WIDTH + win_x] = BGR(flower[i].flower[img_y][img_x]);
}
}
6.我们声明两个局部变量img_x,img_y,借助圆的参数方程的知识,获取以cen_x,cen_y为圆心,
r为半径的圆在图片上的坐标,再加入一个判断防止它们越界。
7.我们再声明两个局部变量,win_x,win_y获取以x,y为圆心,r为半径的圆在窗口上的坐标,然后再加入一个判断防止它们越界。
8.最后我们利用 Drawing_window(存储了绘图设备的显示缓冲区),来实现烟花绽放。
9.Drawing_window里的参数是窗口上像素点的位置,BGR里的参数是图片中指定位置的像素点。
五.爱心烟花
5.1爱心烟花的初始化
首先我们在指定数量个烟花弹升空绽放后停止烟花弹升空。
此时13的烟花弹的shoot都为false,我们将其作为爱心烟花出现的条件.
然后我们给每个烟花弹都初始化一个坐标,再让它们的速度一致,
以此来达到爱心烟花的效果。
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 < JET_NUM; i++)
{//爱心坐标
jet[i].x = x[i] * 10;
jet[i].y = (y[i] + 75) * 10;
jet[i].hy = y[i] * 10;
jet[i].shoot = true;
jet[i].t1 = GetTickCount();
jet[i].dt = 10;
putimage(jet[i].x, jet[i].y, &jet[i].jet_picture[jet[i].n], SRCINVERT);
}
5.2爱心烟花的切换
- 爱心烟花的实现与前面正常烟花的实现在切换时有冲突,我们需要加入一些操作来调节
static int success_times = 0, reset_t1, reset_t2;
static bool change_jet_num = true;
static bool Love = true;
- 我们引入5个具有文件作用域的变量:success_times为成功绽放烟花的数量,我们通过让它达到指定值来引出爱心烟花;Love为了阻止爱心烟花一直运行(爱心烟花是同步上升的,13个烟花的shoot会同时重置为false,再一次满足了爱心烟花的条件);reset_t1获取爱心烟花开始时的时间,reset_t2获取当前的时间;我们声明一个局部变量count=0,当一个烟花的bloom=false和一个烟花弹的shoot=false时,count++,当它等于26时,并且已经过去6s,就重置回正常烟花;change_jet_num是为了更好地协调正常烟花和爱心烟花的切换而来的.
reset_t2 = GetTickCount();
int count = 0;
for (int i = 0; i < FLOWER_NUM; i++)
if (!flower[i].bloom)
count++;
for (int i = 0; i < JET_NUM; i++)
if (!jet[i].shoot)
count++;
if ((count == 26) && (reset_t2 - reset_t1 > 6000))
{//6秒后重置烟花
success_times = 0;
shoot_reality = 0;
Love = true;
}
六.源码展示
一些细节的地方没有说明,还有一些地方可以优化,大家可以照着源码再看看
#include<iostream>
#include<easyx.h>
#include<stdlib.h>
#define PI 3.1415826
#define WIDTH 1200
#define HEIGHT 700
#define JET_NUM 13
#define FLOWER_NUM 13
static int success_times = 0, reset_t1, reset_t2;
static bool change_jet_num = true;
static bool Love = true;
using namespace std;
inline void ResetJet(int);
inline void shoot();
inline void ResetFlower(int);
inline void bloom(DWORD*);
inline int love();
class JET
{
public:
int x, y;
int hy;
DWORD t1, t2, dt;
bool shoot;
IMAGE jet_picture[2];
byte n : 1;
JET()
{
x = rand() % 1160 + 40;
y = rand() % 100 + 600;
hy = rand() % 350 + 50;
if (rand() % 2)
dt = rand() % 2 + 1;
else
dt = rand() % 10 + 15;
shoot = false;
t1 = t2 = 0;
n = 0;
IMAGE JET_PICTURE;
loadimage(&JET_PICTURE, "./jet.jpg", 200, 50);
SetWorkingImage(&JET_PICTURE);
int choice_jet = rand() % 5;
getimage(&jet_picture[0], choice_jet * 20, 0, 20, 50);
getimage(&jet_picture[1], (choice_jet + 5) * 20, 0, 20, 50);
//SetWorkingImage();
}
}jet[JET_NUM];
class FLOWER
{
public:
int x, y;
int cen_x, cen_y;
int r;
int max_r;
DWORD flower[240][240];
bool bloom;
FLOWER()
{
x = y = 0;
cen_x = 120;
cen_y = 120;
max_r = 120;
r = 0;
bloom = false;
IMAGE FLOWER, Flower;
loadimage(&FLOWER, "./flower.jpg", 3120, 240);
SetWorkingImage(&FLOWER);
static int choice_flower = 0;
getimage(&Flower, choice_flower * 240, 0, 240, 240);
choice_flower++;
SetWorkingImage(&Flower);
for (int y = 0; y < 240; y++)
for (int x = 0; x < 240; x++)
flower[y][x] = getpixel(y, x);
}
}flower[FLOWER_NUM];
int main()
{
srand((unsigned)time(NULL));
initgraph(WIDTH, HEIGHT);
DWORD* Drawing_window = GetImageBuffer();
BeginBatchDraw();
while (1)
{
for (int i = 0; i < WIDTH; i++)
{
for (int k = 0; k < 4; k++)
{
int x = rand() % WIDTH;
int y = rand() % HEIGHT;
if (y < HEIGHT)
Drawing_window[y * WIDTH + x] = BLACK;
}
}
shoot();
bloom(Drawing_window);
love();
FlushBatchDraw();
}
EndBatchDraw();
}
inline void ResetJet(int i)
{
jet[i].x = rand() % 1120 + 40;
for (int j = 0; j < JET_NUM; j++)
if((jet[j].shoot) && ((jet[i].x - jet[j].x) <= 66))
jet[i].x = rand() % 1120 + 40;
jet[i].y = rand() % 30 + 600;
jet[i].hy = rand() % 350 + 50;
jet[i].shoot = false;
jet[i].t1 = jet[i].t2 = 0;
if (rand() % 2)
jet[i].dt = rand() % 2 + 1;
else
jet[i].dt = rand() % 10 + 15;
}
inline void shoot()
{
static int shoot_need = 1, shoot_reality = 0;
if (!change_jet_num)
{
reset_t2 = GetTickCount();
int count = 0;
for (int i = 0; i < FLOWER_NUM; i++)
if (!flower[i].bloom)
count++;
for (int i = 0; i < JET_NUM; i++)
if (!jet[i].shoot)
count++;
if ((count == 26) && (reset_t2 - reset_t1 > 6000))
{//6秒后重置烟花
success_times = 0;
shoot_reality = 0;
Love = true;
}
}
if (success_times <= 12)
change_jet_num = true;
else
change_jet_num = false;
if (change_jet_num)
{
shoot_need = rand() % 3 + 2;
while (shoot_reality < shoot_need)
{
int i = rand() % 13;
if (!jet[i].shoot)
{
jet[i].shoot = true;
shoot_reality++;
jet[i].t1 = GetTickCount();
putimage(jet[i].x, jet[i].y, &jet[i].jet_picture[jet[i].n], SRCINVERT);//显示烟花弹
}
}
}
for (int i = 0; i < JET_NUM; i++)
{
jet[i].t2 = GetTickCount();
if ((jet[i].shoot) && (jet[i].t2 - jet[i].t1 >= jet[i].dt))
{
putimage(jet[i].x, jet[i].y, &jet[i].jet_picture[jet[i].n], SRCINVERT);//擦除烟花弹
if (jet[i].y > jet[i].hy)
{
jet[i].y -= 5;
jet[i].n++;
jet[i].t1 = jet[i].t2;
putimage(jet[i].x, jet[i].y, &jet[i].jet_picture[jet[i].n], SRCINVERT);//显示烟花弹
}
else
{
if (change_jet_num)
{
success_times++;
shoot_reality--;
}
flower[i].x = jet[i].x;
flower[i].y = jet[i].y;
flower[i].bloom = true;
ResetJet(i);
}
}
}
}
inline void ResetFlower(int i)
{
flower[i].cen_x = 120;
flower[i].cen_y = 120;
flower[i].max_r = 120;
flower[i].r = 0;
flower[i].bloom = false;
}
inline void bloom(DWORD* Drawing_window)
{
for (int i = 0; i < FLOWER_NUM; i++)
{
if ((flower[i].bloom)&&(flower[i].r < flower[i].max_r))
{
flower[i].r++;
for (double a = 0; a <= 2 * PI; a += 0.01)
{//图形上像素点的坐标
int img_x= (int) (flower[i].cen_x + flower[i].r * cos(a));
int img_y = (int)(flower[i].cen_y + flower[i].r * sin(a));
if (img_x > 0 && img_x < 240 && img_y>0 && img_y < 240)
{
//接近黑色的像素点不输出
int b = flower[i].flower[img_y][img_x] & 0xff;
int g = (flower[i].flower[img_y][img_x] >> 8) & 0xff;
int r = (flower[i].flower[img_y][img_x] >> 16);
//窗口上像素点的坐标
int win_x = (int)(flower[i].x + flower[i].r * cos(a));
int win_y = (int)(flower[i].y + flower[i].r * sin(a));
if (win_x > 0 && win_x < WIDTH && win_y>0 && win_y < HEIGHT && b>0x20 && g > 0x20 && r > 0x20)
Drawing_window[win_y * WIDTH + win_x] = BGR(flower[i].flower[img_y][img_x]);
}
}
}
if (flower[i].r >= flower[i].max_r)
ResetFlower(i);
}
}
inline int love()
{//爱心烟花
int count = 0;
for (int i = 0; i < JET_NUM; i++)
if (!jet[i].shoot)
count++;
if ((!change_jet_num) && (!Love))
return 0;
if (count == 13)
{
reset_t1 = GetTickCount();
Love = false;
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 < JET_NUM; i++)
{//爱心坐标
jet[i].x = x[i] * 10;
jet[i].y = (y[i] + 75) * 10;
jet[i].hy = y[i] * 10;
jet[i].shoot = true;
jet[i].t1 = GetTickCount();
jet[i].dt = 10;
putimage(jet[i].x, jet[i].y, &jet[i].jet_picture[jet[i].n], SRCINVERT);
}
}
return 0;
}
版权归原作者 水镜Q 所有, 如有侵权,请联系我们删除。