C++easyX实现烟花
程序员文章站
2022-04-07 12:37:30
...
/* *************** HeadFiles *************** */
#include <easyx.h>
#include <cmath>
#include <ctime>
#include <list>
/* *************** GlobleValue *************** */
long ret_useless = 0; // Capture the _getwch() return value to eliminate warnings
const int GW = 640; // Screen width屏幕的宽度
const int GH = 480; // Screen height屏幕的高度
const double g = 9.8; // Acceleration of gravity重力
const double PI = 3.1415926;
const int len_max = 80; // The maximum length of LightLine光线的最大长度
const int h_max = GH - len_max; // The maximum height that LightLine can reach光线能到达的最高高度
const double v_max = sqrt(2 * g * h_max/10 ); // Maximum initial velocity of LightLine最大初速度 mgh=mv^2 重力势能=动能
//除以10的缘故是公式是用m做单位,1m代表10个像素点
const int n_max = 5; // Maximum number of fireworks on the screen烟花在屏幕上同时存在最多的数量
/* *************** LightLine *************** */
class LightLine
{
public:
LightLine(int, double);
void Draw() const;
void Move();
bool Stopped() const { return v == 0; }
bool OverLine() const { return py < h_max* n_max / (n_max + 1); } // Necessary condition for the next fireworks to rise
int GetX() const { return px; }
int GetY() const { return py; }
private:
int px; // Position_x
int py; // Position_y
int len; // Length
double v; // Velocity (The -y axis is positiva)速度(Y轴是正的)
clock_t ct = 0; // Recording time
};
LightLine::LightLine(int x = rand() % (GW - 80) + 40, double vv = (rand() % 20 + 80.0)/100 * v_max ) :px(x), py(h_max)
{
v = vv; // The initial velocity determines the height can be reached初始速度决定了可以达到的高度
len = int(v / v_max * len_max); // v : v_max = len : len_max速度快的,尾影会拖得长一些
}
void LightLine::Draw() const
{
srand((unsigned)(time)(NULL));
int Light_color = rand() % 360;
//绘制上升曲线,是一列圆的绘制,第一个圆形亮度最高,后面的亮度逐渐减少,达到渐变的效果
for (int j = py; j < py + len; ++j)
{
float hsv_v = 0.8f * (len - (j - py)) / len + 0.2f; // Gradient color这是亮度
setfillcolor(HSVtoRGB(float(Light_color), 1.0f, hsv_v));
solidcircle(px, j, 1);
}
}
void LightLine::Move()
{
if (v == 0)
return;
if (ct == 0)
{
ct = clock();
Draw();
return;
}
clock_t t = clock() - ct;
ct = clock();
double v_cur = v - g * t / 1000.0; // The -y axis is positiva for the velocity
//除以1000的原因是,公式是以s做单位,程序里是ms作为单位,1s=1000ms
if (v_cur > 0)
{
py += int(10 * (v_cur * v_cur - v * v) / 2 / g);//上升运动高度 vt^2-v0=2gh
v = v_cur;
}
else
{
//如果v_cur<0,则表示可以到顶点了。
py -= int(10 * v * v / 2 / g);//*落体的高度 0-vt^2=2gh
v = 0;//因为顶点烟花爆炸
}
len = int(v / v_max * len_max);
Draw();
}
/* *************** ParticleSwarm *************** */
class ParticleSwarm
{
struct Particle
{
int x; //表示粒子的运动过程的x坐标
int y; //表示粒子的运动过程的y坐标
int z = 0; // Z axis vertical screen inword Z轴垂直屏幕输入
double vy; // The y axis is positiva for the velocity结构体里面的vy是每个粒子的y方向速度
Particle(int xx, int yy, double vv) :x(xx), y(yy), vy(vv) {}
};
public:
ParticleSwarm(int, int, float);
void Draw() const;
void Move();
bool Finish() const { return vec.size() <= 1; }
private:
double vx;
double vz = 0;
float hsv_h; // Color parameter
clock_t ct = 0;
std::list<Particle> vec; // For saving particles
};
ParticleSwarm::ParticleSwarm(int x, int y, float colorh = float(rand() % 256))
{
// Cylindrical coordinate to xyz (parameters: len, radian_xz, radian_yx)
hsv_h = colorh + rand() % 20;
hsv_h = hsv_h > 255 ? hsv_h - 256 : hsv_h;
//Z轴的负向对着人,即人对着屏幕的方向为Z轴的正向
double vm = v_max / 2 * (rand() % 5 + 15.0) / 25.0;
double radian_xz = (rand() % 360) * PI / 180;//X轴偏向Z轴的角度0--2*PI
double radian_xy = (rand() % 90) * PI / 180 + PI / 2;//X轴偏向Y轴的角度PI/2--PI
vx = vm * cos(radian_xy) * cos(radian_xz);//向量在X轴的投影
vz = vm * cos(radian_xy) * sin(radian_xz);//向量在Z轴的投影
double vy = vm * sin(radian_xy); //向量在Y轴的投影
//len表示粒子运动轨迹的长度,也可以认为是装填粒子的数量
int len = rand() % 30 + 50;//rand() % 30 + 50这个是源代码的数值,数值越大,烟花爆炸的范围,散开的范围就越大。
//这一段刻画的是爆炸花束粒子中的其中一条线
while (len)
{
// Use len as time parameter
//目标像素位置=初始像素位置+偏移米×10
int xx = x + int(10 * vx * len / 200.0);
//int zz = int(10 * vz * len / 200.0);
double cvy = vy - g * len / 200.0;
int yy = y + int(10 * (cvy * cvy - vy * vy) / 2 / g);
vec.push_back(Particle(xx, yy, cvy));
--len;
}
}
void ParticleSwarm::Draw() const
{
int n = 0;
auto size = vec.size();
for (auto &x : vec)
{
if (x.x >= 0 && x.x < GW && x.y >= 0 && x.y < GH)
{
//烟花线条的尾端亮度最低,反之首端是比较亮的
float cv = 0.2f + 0.8f * (size - n) / size - x.z ; //原来的float cv = 0.2f + 0.8f * (size - n) / size - x.z / 40 * 0.1f
auto color = HSVtoRGB(hsv_h, 1.0f, cv > 0 ? cv : 0);
if (x.z < 0) // Z axis vertical screen inword如果烟花是往屏幕外扩散的话,就把像素点变大
{
setfillcolor(color);
solidcircle(x.x, x.y, abs(x.z) / 80 > 1 ? 2 : 1);
}
else
putpixel(x.x, x.y, color);
}
++n;
}
}
void ParticleSwarm::Move()
{
if (ct == 0)
{
ct = clock();
Draw();
return;
}
for (int i = 0; i < 3 && vec.size() > 1; i++)//
vec.pop_back(); // Delete particles for shortening length画面每次刷新删除3个末尾粒子
clock_t t = clock() - ct;
ct = clock();
for (auto& x : vec)//爆炸花束之中一条光纤的粒子持续运动
{
double vy_cur = x.vy - g * t / 1000.0;
x.x += int(10 * vx * t / 1000.0);
x.y += int(10 * (vy_cur * vy_cur - x.vy * x.vy) / 2 / g);
x.z += int(10 * vz * t / 1000.0);
x.vy = vy_cur;
}
Draw();
}
/* *************** Fireworks *************** */
class Fireworks
{
public:
Fireworks(int, int);
void Move();
bool Empty() const { return vec.empty(); }
private:
std::list<ParticleSwarm> vec;
};
Fireworks::Fireworks(int x, int y)
{
bool colorful = rand() % 100 < 20 ? true : false;//1/5的几率判断
float h = float(rand() % 256);
int n = rand() % 5 + 45;//烟花升到顶点后,爆炸出来的光线量
for (int i = 0; i < n; i++)
{
if (colorful)//决定烟花的爆炸光线,每一条是否是同一颜色的。1/5的几率判断
vec.push_back(ParticleSwarm(x, y));
else
vec.push_back(ParticleSwarm(x, y, h));
}
}
void Fireworks::Move()
{
std::list<decltype(vec.begin())> toDel;
for (auto it = vec.begin(); it != vec.end(); ++it)
{
if (it->Finish())//如果该粒子群里的粒子数只剩下一个,则跳过
{
toDel.push_back(it);
continue;
}
it->Move();//如果粒子群里的粒子数不只是剩下一个,则继续描画它的轨迹
}
for (auto& x : toDel)
vec.erase(x);
}
/* *************** main *************** */
int main()
{
initgraph(GW, GH);
setrop2(R2_MERGEPEN);
srand((unsigned)time(nullptr));
// Refresh once in 50ms
clock_t ct = clock();
// LightLine list
std::list<LightLine> vec;
vec.push_back(LightLine());
// Fireworks list
std::list<Fireworks> vec2;
BeginBatchDraw();
while (!(GetAsyncKeyState(VK_ESCAPE) & 0x8000))
{
if (clock() - ct > 50)
{
cleardevice();
ct = clock();
// LightLine list
std::list<decltype(vec.begin())> toDel;
if (vec.size() == 0)
vec.push_back(LightLine());
else if (vec.size() < n_max && rand() % 100 < 20 && (--vec.end())->OverLine())
vec.push_back(LightLine());
for (auto it = vec.begin(); it != vec.end(); ++it)
{
if (it->Stopped())
{
vec2.push_back(Fireworks(it->GetX(), it->GetY()));
toDel.push_back(it);
continue;
}
it->Move();
}
for (auto& it : toDel)
vec.erase(it);
// Fireworks list
std::list<decltype(vec2.begin())> toDel2;
for (auto it = vec2.begin(); it != vec2.end(); ++it)
{
if (it->Empty())
{
toDel2.push_back(it);
continue;
}
it->Move();
}
for (auto& it : toDel2)
vec2.erase(it);
FlushBatchDraw();
}
Sleep(1);
}
EndBatchDraw();
closegraph();
return 0;
}