C++easyX实现烟花

/* *************** 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;
}

在这里插入图片描述

  • 20
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 22
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值