0


C++烟花实现思路及源码

一.程序展示

二.程序简介

  1. 青春就像一场烟花,美得让人窒息,却短暂得让人流泪。

  2. 我们该如何用代码记录下现实中的烟花,让它成为永恒呢?

  3. 首先把烟花分成两个阶段:烟花弹升空和烟花绽放。

  4. 然后把烟花弹(jet)和烟花(flower)各作为一个对象,再思考它们各自的属性。

  5. 为了实现多个烟花的效果,创建烟花弹数组和烟花数组。

  6. 烟花弹和烟花各自的对象数组大小都为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烟花的属性

  1. 我们观察现实中的烟花,会发现它的绽放过程近似于以空间中的一点

作为圆心,半径从0开始不断变大,即以规则的圆形放大,直到最大半径,

然后缓慢消失。

  1. 那么它应该具有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这些运动的属性来让烟花变速绽放。

  1. 储存烟花弹上的像素点的操作
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烟花弹绽放的思路

  1. 我们声明一个指针存储绘图设备的显示缓冲区。
DWORD* Drawing_window = GetImageBuffer();
  1. 然后我们可以通过它来向窗口上输出像素点。

  2. 由上述讲解可知,在一个烟花弹上升到最高点消失再重置时,

它向一个烟花对象传来了它消失点的坐标,并且会让这个烟花对象的bloom为真,

我们就以这个坐标为烟花绽放的圆心。

flower[i].x = jet[i].x;
flower[i].y = jet[i].y;
flower[i].bloom = true;
ResetJet(i);
  1. 我们需要一个循环遍历烟花对象数组,如果r<max_r,并且bloom==true,

就令r++。

  1. 接下来是最关键的:将图片中的烟花的像素以圆形扩散的形式输出到窗口上。
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爱心烟花的初始化

  1. 首先我们在指定数量个烟花弹升空绽放后停止烟花弹升空。

  2. 此时13的烟花弹的shoot都为false,我们将其作为爱心烟花出现的条件.

  3. 然后我们给每个烟花弹都初始化一个坐标,再让它们的速度一致,

以此来达到爱心烟花的效果。

    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爱心烟花的切换

  1. 爱心烟花的实现与前面正常烟花的实现在切换时有冲突,我们需要加入一些操作来调节
static int success_times = 0, reset_t1, reset_t2;
static bool change_jet_num = true;
static bool Love = true;
  1. 我们引入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;
}
标签: c++ 开源 经验分享

本文转载自: https://blog.csdn.net/2301_76180784/article/details/128781623
版权归原作者 水镜Q 所有, 如有侵权,请联系我们删除。

“C++烟花实现思路及源码”的评论:

还没有评论