祝大家2026快乐!2
2026-02-17 16:29:05
发布于:湖南
更新日志:
更新了:爆炸圆环
更新了:爆炸烟雾
更新了:爆炸粒子
改变了:拖尾特效 (线条拖尾 -> 粒子拖尾(模拟真实))
增强了:粒子性能(多发烟花无压力)
版本号:V1.1
代码(上次有人问是什么IDE,是Dev-C++[其实什么IDE都可以哦!gcc只要版本足够就行],以后不会变的):
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <vector>
#include <array>
#include <random>
#include <cmath>
#include <algorithm>
#include <chrono>
#include <iostream>
#include <ctime>
using namespace std;
const int DISPLAY_WIDTH = 800;
const int DISPLAY_HEIGHT = 800;
const COLORREF BG_COLOR = RGB(20, 20, 30);
float g_wind_x = 0.0f;
const int TRAIL_PARTICLE_COUNT = 8;
const float TRAIL_PARTICLE_RANDOM = 1.5f;
const float TRAIL_BASE_SIZE = 0.5f;
BYTE* g_pMemBits = nullptr;
BITMAPINFO g_bmi = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
class Vector2 {
public:
float x, y;
Vector2() : x(0.0f), y(0.0f) {}
Vector2(float x_, float y_) : x(x_), y(y_) {}
Vector2& operator+=(const Vector2& other) {
x += other.x;
y += other.y;
return *this;
}
Vector2& operator-=(const Vector2& other) {
x -= other.x;
y -= other.y;
return *this;
}
Vector2& operator*=(float scalar) {
x *= scalar;
y *= scalar;
return *this;
}
Vector2& operator/=(float scalar) {
x /= scalar;
y /= scalar;
return *this;
}
Vector2 operator+(const Vector2& other) const {
return Vector2(x + other.x, y + other.y);
}
Vector2 operator-(const Vector2& other) const {
return Vector2(x - other.x, y - other.y);
}
Vector2 operator*(float scalar) const {
return Vector2(x * scalar, y * scalar);
}
Vector2 operator/(float scalar) const {
return Vector2(x / scalar, y / scalar);
}
float length() const {
return std::sqrt(x * x + y * y);
}
float length_squared() const {
return x * x + y * y;
}
Vector2 normalized() const {
float len = length();
return len > 0 ? Vector2(x / len, y / len) : Vector2();
}
float distance_to(const Vector2& other) const {
float dx = x - other.x;
float dy = y - other.y;
return std::sqrt(dx * dx + dy * dy);
}
};
std::random_device rd;
std::mt19937 gen(rd());
float uniform(float min, float max) {
std::uniform_real_distribution<> dist(min, max);
return static_cast<float>(dist(gen));
}
struct ExplosionFlash {
Vector2 pos;
float radius;
float max_radius;
float alpha;
float decay_speed;
bool active;
ExplosionFlash(float x, float y) {
pos = Vector2(x, y);
max_radius = uniform(30.0f, 60.0f);
radius = 0.0f;
alpha = 1.0f;
decay_speed = uniform(0.08f, 0.16f);
active = true;
}
bool update() {
if (!active) return false;
if (radius < max_radius) {
radius += max_radius * 0.1f;
if (radius > max_radius) radius = max_radius;
} else {
alpha -= decay_speed;
if (alpha <= 0.0f) {
alpha = 0.0f;
active = false;
return false;
}
}
return true;
}
void draw() {
if (!active || alpha <= 0.0f) return;
int r = 255, g = 255, b = 255;
int int_radius = static_cast<int>(radius);
for (int dy = -int_radius; dy <= int_radius; dy++) {
for (int dx = -int_radius; dx <= int_radius; dx++) {
if (dx * dx + dy * dy <= int_radius * int_radius) {
float local_alpha = alpha;
if (local_alpha <= 0.0f) continue;
int rx = static_cast<int>(pos.x + dx);
int ry = static_cast<int>(pos.y + dy);
if (rx < 0 || rx >= DISPLAY_WIDTH || ry < 0 || ry >= DISPLAY_HEIGHT) continue;
int idx = (ry * DISPLAY_WIDTH + rx) * 4;
g_pMemBits[idx] = static_cast<BYTE>(GetBValue(BG_COLOR) * (1 - local_alpha) + b * local_alpha);
g_pMemBits[idx + 1] = static_cast<BYTE>(GetGValue(BG_COLOR) * (1 - local_alpha) + g * local_alpha);
g_pMemBits[idx + 2] = static_cast<BYTE>(GetRValue(BG_COLOR) * (1 - local_alpha) + r * local_alpha);
g_pMemBits[idx + 3] = 255;
}
}
}
}
};
const Vector2 gravity(0.0f, 0.15f);
const float air_drag = 0.001f;
const float wind_turbulence = 0.003f;
const int dynamic_offset = 1;
const int static_offset = 5;
enum FireworkType {
TYPE_SPHERE,
TYPE_HEART,
TYPE_STAR,
TYPE_LINE,
TYPE_SPARKLE,
TYPE_2026,
TYPE_CIRCLE_RING,
TYPE_FLOWER,
TYPE_SPIRAL,
TYPE_DOUBLE_HEART,
TYPE_RANDOM_BURST,
TYPE_NULL
};
int randint(int min, int max) {
std::uniform_int_distribution<> dist(min, max);
return dist(gen);
}
template <typename T>
T choice(const std::vector<T>& vec) {
if (vec.empty()) return T();
return vec[randint(0, static_cast<int>(vec.size()) - 1)];
}
inline void DrawPixelMem(int x, int y, COLORREF col) {
if (x < 0 || x >= DISPLAY_WIDTH || y < 0 || y >= DISPLAY_HEIGHT) return;
int idx = (y * DISPLAY_WIDTH + x) * 4;
g_pMemBits[idx] = GetBValue(col);
g_pMemBits[idx + 1] = GetGValue(col);
g_pMemBits[idx + 2] = GetRValue(col);
g_pMemBits[idx + 3] = 255;
}
inline void DrawEllipseMem(int x, int y, int radius, COLORREF col) {
if (radius <= 0) return;
for (int dy = -radius; dy <= radius; dy++) {
for (int dx = -radius; dx <= radius; dx++) {
if (dx * dx + dy * dy <= radius * radius) {
DrawPixelMem(x + dx, y + dy, col);
}
}
}
}
class Trail {
public:
int pos_in_line;
Vector2 pos;
Vector2 prev_pos;
bool dynamic;
float base_size;
float life_ratio;
Trail(int n, int size_, bool dynamic_)
: pos_in_line(n), pos(-10.0f, -10.0f), prev_pos(-10.0f, -10.0f),
dynamic(dynamic_), life_ratio(1.0f) {
if (dynamic)
base_size = max(static_cast<float>(size_) - n * 0.4f, TRAIL_BASE_SIZE);
else
base_size = max(static_cast<float>(size_) - 1.0f, TRAIL_BASE_SIZE);
}
void get_pos(int x, int y) {
prev_pos = pos;
pos = Vector2(static_cast<float>(x), static_cast<float>(y));
}
void show(HDC hdc, COLORREF base_colour, float parent_life_ratio) {
(void)hdc;
life_ratio = 1.0f - pow(static_cast<float>(pos_in_line) / 5.0f, 0.0125f);
float total_alpha = life_ratio * parent_life_ratio;
if (total_alpha <= 0.0004f || pos.x == -10.0f) return;
int r = static_cast<int>(GetRValue(base_colour) * total_alpha + GetRValue(BG_COLOR) * (1 - total_alpha));
int g = static_cast<int>(GetGValue(base_colour) * total_alpha + GetGValue(BG_COLOR) * (1 - total_alpha));
int b = static_cast<int>(GetBValue(base_colour) * total_alpha + GetBValue(BG_COLOR) * (1 - total_alpha));
COLORREF particle_col = RGB(r, g, b);
DrawPixelMem(static_cast<int>(round(pos.x)), static_cast<int>(round(pos.y)), particle_col);
if (prev_pos.x != -10.0f) {
Vector2 dir = pos - prev_pos;
float segment_len = dir.length();
if (segment_len > 0.1f) {
Vector2 step = dir / (TRAIL_PARTICLE_COUNT + 1);
for (int i = 1; i <= TRAIL_PARTICLE_COUNT; i++) {
float t = static_cast<float>(i) / (TRAIL_PARTICLE_COUNT + 1);
float sub_alpha = total_alpha * (1.0f - t * 0.01f);
float jitter_t = t + uniform(-0.1f, 0.1f);
Vector2 sub_pos = prev_pos + step * (jitter_t * (TRAIL_PARTICLE_COUNT + 1));
sub_pos.x += uniform(-TRAIL_PARTICLE_RANDOM, TRAIL_PARTICLE_RANDOM);
sub_pos.y += uniform(-TRAIL_PARTICLE_RANDOM, TRAIL_PARTICLE_RANDOM);
int sr = static_cast<int>(GetRValue(base_colour) * sub_alpha + GetRValue(BG_COLOR) * (1 - sub_alpha));
int sg = static_cast<int>(GetGValue(base_colour) * sub_alpha + GetGValue(BG_COLOR) * (1 - sub_alpha));
int sb = static_cast<int>(GetBValue(base_colour) * sub_alpha + GetBValue(BG_COLOR) * (1 - sub_alpha));
COLORREF sub_col = RGB(sr, sg, sb);
DrawPixelMem(static_cast<int>(round(sub_pos.x)), static_cast<int>(round(sub_pos.y)), sub_col);
}
}
}
}
};
class SmokeParticle {
public:
Vector2 pos;
Vector2 vel;
Vector2 acc;
int life;
int max_life;
float size;
bool remove;
COLORREF base_smoke_color;
SmokeParticle(float x, float y, COLORREF firework_colour)
: pos(x, y), remove(false) {
float angle = uniform(3.0f, 6.0f);
float speed = uniform(1.0f, 3.0f);
vel = Vector2(cos(angle) * speed, sin(angle) * speed * 0.8f);
life = 0;
max_life = randint(80, 120);
size = uniform(2.0f, 5.0f);
int r = (GetRValue(firework_colour) + 100) / 2;
int g = (GetGValue(firework_colour) + 100) / 2;
int b = (GetBValue(firework_colour) + 100) / 2;
base_smoke_color = RGB(r, g, b);
}
void apply_force(const Vector2& force) {
acc += force;
}
void update() {
apply_force(Vector2(0, -0.06f));
apply_force(Vector2(g_wind_x * 0.3f, 0));
apply_force(Vector2(uniform(-0.008f, 0.008f), 0));
Vector2 drag = vel.normalized() * (-0.003f * vel.length_squared());
apply_force(drag);
vel += acc;
pos += vel;
acc = Vector2(0, 0);
life++;
remove = (life >= max_life);
size += 0.03f;
}
void show(HDC hdc) {
(void)hdc;
float life_ratio = 1.0f - (static_cast<float>(life) / max_life);
if (life_ratio <= 0.01f) return;
float alpha = 0.4f * life_ratio;
int gray = randint(80, 140);
int r = static_cast<int>((GetRValue(base_smoke_color) * 0.6f + gray * 0.4f) * alpha + GetRValue(BG_COLOR) * (1 - alpha));
int g = static_cast<int>((GetGValue(base_smoke_color) * 0.6f + gray * 0.4f) * alpha + GetGValue(BG_COLOR) * (1 - alpha));
int b = static_cast<int>((GetBValue(base_smoke_color) * 0.6f + gray * 0.4f) * alpha + GetBValue(BG_COLOR) * (1 - alpha));
COLORREF smoke_color = RGB(r, g, b);
float current_size = size * (0.8f + 0.4f * life_ratio);
DrawEllipseMem(static_cast<int>(pos.x), static_cast<int>(pos.y), static_cast<int>(current_size), smoke_color);
}
};
class Particle {
public:
bool firework;
Vector2 pos;
Vector2 origin;
bool remove;
int explosion_radius;
int life;
int max_life;
Vector2 acc;
std::vector<Trail> trails;
std::array<int, 10> prev_posx;
std::array<int, 10> prev_posy;
Vector2 vel;
float size;
COLORREF colour;
COLORREF original_colour;
bool is_sparkle;
int sparkle_timer;
bool can_explode;
int secondary_explosion_time;
std::vector<COLORREF> secondary_colours;
float mass;
float sparkle_phase;
float thermal_strength;
Particle(float x, float y, bool firework_, const std::vector<COLORREF>& colour_list, bool sparkle_ = false)
: firework(firework_), pos(x, y), origin(x, y), remove(false),
explosion_radius(randint(10, 25)), life(0), max_life(120), acc(0.0f, 0.0f),
is_sparkle(sparkle_), sparkle_timer(0),
can_explode(false), secondary_explosion_time(0),
mass(1.0f), sparkle_phase(uniform(0, 6.28f)), thermal_strength(uniform(0.5f, 1.5f)) {
prev_posx.fill(-10);
prev_posy.fill(-10);
int trail_size = firework ? 5 : randint(3, 4);
for (int i = 0; i < 5; i++) trails.emplace_back(i, trail_size, firework);
if (firework) {
vel = Vector2(uniform(-0.5f, 0.5f), static_cast<float>(-randint(16, 19)));
size = 4.0f;
original_colour = colour_list[0];
mass = 2.0f;
} else {
vel = Vector2(uniform(-1.0f, 1.0f), uniform(-1.0f, 1.0f)).normalized() * static_cast<float>(randint(8, explosion_radius + 8));
size = static_cast<float>(randint(2, 4));
original_colour = choice(colour_list);
max_life = explosion_radius >= 150 ? 300 : 100;
mass = uniform(0.5f, 1.5f);
}
colour = original_colour;
}
Particle(float x, float y, bool firework_, COLORREF colour_, bool sparkle_ = false, Vector2 initial_vel = Vector2(0, 0))
: firework(firework_), pos(x, y), origin(x, y), remove(false),
explosion_radius(randint(10, 25)), life(0), max_life(120), acc(0.0f, 0.0f),
is_sparkle(sparkle_), sparkle_timer(0),
can_explode(false), secondary_explosion_time(0),
mass(1.0f), sparkle_phase(uniform(0, 6.28f)), thermal_strength(uniform(0.5f, 1.5f)) {
prev_posx.fill(-10);
prev_posy.fill(-10);
int trail_size = firework ? 5 : randint(3, 4);
for (int i = 0; i < 5; i++) trails.emplace_back(i, trail_size, firework);
if (firework) {
vel = (initial_vel.x != 0 || initial_vel.y != 0) ? initial_vel : Vector2(uniform(-0.5f, 0.5f), static_cast<float>(-randint(16, 19)));
size = 4.0f;
original_colour = colour_;
mass = 2.0f;
} else {
vel = Vector2(uniform(-1.0f, 1.0f), uniform(-1.0f, 1.0f)).normalized() * static_cast<float>(randint(8, explosion_radius + 8));
size = static_cast<float>(randint(2, 4));
original_colour = colour_;
max_life = 100;
mass = uniform(0.5f, 1.5f);
}
colour = original_colour;
}
void apply_force(const Vector2& force) {
acc += force / mass;
}
void move() {
if (vel.length_squared() > 0) {
Vector2 drag = vel.normalized() * (-air_drag * vel.length_squared() * mass);
apply_force(drag);
}
apply_force(Vector2(g_wind_x, 0.0f));
float life_ratio = 1.0f - (static_cast<float>(life) / max_life);
apply_force(Vector2(0, -0.03f * thermal_strength * life_ratio));
vel += acc;
vel *= 0.92f;
pos += vel;
acc = Vector2(0, 0);
if (life == 0 && !firework) {
if (pos.distance_to(origin) > explosion_radius) remove = true;
}
decay();
trail_update();
life++;
float fade_start = 0.4f;
float color_ratio = (life_ratio < fade_start) ? (life_ratio / fade_start) : 1.0f;
int r = static_cast<int>(GetRValue(original_colour) * color_ratio + GetRValue(BG_COLOR) * (1 - color_ratio));
int g = static_cast<int>((GetGValue(original_colour) + 10) * color_ratio + GetGValue(BG_COLOR) * (1 - color_ratio));
int b = static_cast<int>(GetBValue(original_colour) * color_ratio + GetBValue(BG_COLOR) * (1 - color_ratio));
if (is_sparkle) {
float sparkle = 0.7f + 0.3f * sin(sparkle_phase);
r = static_cast<int>(r * sparkle);
g = static_cast<int>(g * sparkle);
b = static_cast<int>(b * sparkle);
sparkle_phase += 0.3f;
}
colour = RGB(max(0, min(255, r)), max(0, min(255, g)), max(0, min(255, b)));
}
void show(HDC hdc) {
float life_ratio = 1.0f - (static_cast<float>(life) / max_life);
for (size_t i = 0; i < trails.size(); i++) trails[i].show(hdc, original_colour, life_ratio);
if (size > 0.1f && life_ratio > 0.05f) {
int glow_layers = 3;
for (int i = glow_layers; i >= 0; i--) {
float layer_size = size + 2.0f + i * 1.5f;
float layer_alpha = 0.2f * (1.0f - static_cast<float>(i) / glow_layers) * life_ratio;
if (layer_alpha <= 0) continue;
int r = static_cast<int>(GetRValue(colour) * layer_alpha + GetRValue(BG_COLOR) * (1 - layer_alpha));
int g = static_cast<int>(GetGValue(colour) * layer_alpha + GetGValue(BG_COLOR) * (1 - layer_alpha));
int b = static_cast<int>(GetBValue(colour) * layer_alpha + GetBValue(BG_COLOR) * (1 - layer_alpha));
COLORREF layer_col = RGB(r, g, b);
DrawEllipseMem(static_cast<int>(pos.x), static_cast<int>(pos.y), static_cast<int>(layer_size), layer_col);
}
DrawEllipseMem(static_cast<int>(pos.x), static_cast<int>(pos.y), static_cast<int>(size), colour);
}
}
void decay() {
if (life > max_life * 0.9f) if (randint(0, 4) == 0) remove = true;
}
void trail_update() {
for (int i = 9; i > 0; i--) {
prev_posx[i] = prev_posx[i - 1];
prev_posy[i] = prev_posy[i - 1];
}
prev_posx[0] = (int)pos.x;
prev_posy[0] = (int)pos.y;
for (size_t n = 0; n < trails.size(); n++) {
int idx = trails[n].dynamic ? (n + dynamic_offset) : (n + static_offset);
if (idx < 10) trails[n].get_pos(prev_posx[idx], prev_posy[idx]);
}
}
};
class Firework {
public:
COLORREF colour;
std::vector<COLORREF> colours;
Particle firework;
bool exploded;
std::vector<Particle> particles;
std::vector<Particle> secondary_particles;
std::vector<SmokeParticle> smoke_particles;
std::vector<Particle> embers;
Vector2 min_max_particles;
FireworkType type;
bool full_secondary_explosion;
std::vector<ExplosionFlash> explosion_flashes;
Firework(float x, float y)
: firework(x, y, true, RGB(255, 255, 255), false, Vector2(uniform(-1.0f, 1.0f), static_cast<float>(-randint(70, 80)))), exploded(false), min_max_particles(200, 300), full_secondary_explosion(randint(0, 4) == 0) {
std::vector<COLORREF> palette;
palette.push_back(RGB(255, 60, 60));
palette.push_back(RGB(255, 120, 60));
palette.push_back(RGB(255, 180, 80));
palette.push_back(RGB(255, 255, 120));
palette.push_back(RGB(120, 255, 120));
palette.push_back(RGB(80, 200, 255));
palette.push_back(RGB(100, 150, 255));
palette.push_back(RGB(180, 100, 255));
palette.push_back(RGB(200, 120, 255));
palette.push_back(RGB(255, 120, 180));
palette.push_back(RGB(255, 255, 255));
int type_rand = randint(0, 99);
if (type_rand < 50) type = TYPE_SPHERE;
else if (type_rand < 65) type = TYPE_NULL;
else type = static_cast<FireworkType>(randint(1, 10));
if (type == TYPE_2026) {
colour = RGB(255, 215, 0);
colours.clear();
colours.push_back(RGB(255, 50, 50));
colours.push_back(RGB(255, 215, 0));
colours.push_back(RGB(255, 255, 200));
} else {
colour = choice(palette);
colours.clear();
for (int i = 0; i < 3; i++) colours.push_back(choice(palette));
}
firework.original_colour = colour;
firework.colour = colour;
switch (type) {
case TYPE_STAR:
min_max_particles = Vector2(150, 250);
break;
case TYPE_HEART:
min_max_particles = Vector2(250, 350);
break;
case TYPE_2026:
min_max_particles = Vector2(500, 600);
break;
case TYPE_NULL:
min_max_particles = Vector2(0, 0);
break;
case TYPE_LINE:
min_max_particles = Vector2(100, 200);
break;
case TYPE_SPARKLE:
min_max_particles = Vector2(300, 400);
break;
case TYPE_CIRCLE_RING:
min_max_particles = Vector2(150, 250);
break;
case TYPE_FLOWER:
min_max_particles = Vector2(200, 300);
break;
case TYPE_SPIRAL:
min_max_particles = Vector2(250, 350);
break;
case TYPE_DOUBLE_HEART:
min_max_particles = Vector2(300, 400);
break;
case TYPE_RANDOM_BURST:
min_max_particles = Vector2(200, 400);
break;
default:
break;
}
}
void update(HDC hdc) {
if (!exploded) {
firework.apply_force(gravity);
firework.move();
firework.show(hdc);
if (firework.vel.y >= 0) {
exploded = true;
explode();
create_smoke(firework.pos.x, firework.pos.y);
explosion_flashes.emplace_back(firework.pos.x, firework.pos.y);
}
} else {
for (auto& flash : explosion_flashes) {
flash.update();
flash.draw();
}
explosion_flashes.erase(remove_if(explosion_flashes.begin(), explosion_flashes.end(),
[](const ExplosionFlash & f) {
return !f.active;
}), explosion_flashes.end());
update_smoke(hdc);
for (size_t i = 0; i < embers.size(); i++) {
embers[i].apply_force(Vector2(gravity.x, gravity.y * 1.2f));
embers[i].move();
embers[i].show(hdc);
}
embers.erase(remove_if(embers.begin(), embers.end(), [](Particle & p) {
return p.remove;
}), embers.end());
for (size_t i = 0; i < particles.size(); i++) {
Vector2 f(gravity.x, gravity.y * 0.6f);
particles[i].apply_force(f);
particles[i].move();
if (particles[i].can_explode && particles[i].life >= particles[i].secondary_explosion_time) {
trigger_secondary(particles[i]);
create_smoke(particles[i].pos.x, particles[i].pos.y, 1);
particles[i].remove = true;
}
particles[i].show(hdc);
}
for (size_t i = 0; i < secondary_particles.size(); i++) {
secondary_particles[i].apply_force(Vector2(0, gravity.y * 0.5f));
secondary_particles[i].move();
secondary_particles[i].show(hdc);
}
}
}
void create_smoke(float x, float y, int count = 20) {
for (int i = 0; i < count; i++) {
float offset_x = uniform(-10.0f, 10.0f);
float offset_y = uniform(-10.0f, 10.0f);
smoke_particles.push_back(SmokeParticle(x + offset_x, y + offset_y, colour));
}
}
void update_smoke(HDC hdc) {
for (size_t i = 0; i < smoke_particles.size(); i++) smoke_particles[i].update();
for (size_t i = 0; i < smoke_particles.size(); i++) if (!smoke_particles[i].remove) smoke_particles[i].show(hdc);
smoke_particles.erase(remove_if(smoke_particles.begin(), smoke_particles.end(),
[](const SmokeParticle & s) {
return s.remove;
}), smoke_particles.end());
}
void trigger_secondary(const Particle& parent) {
int cnt = full_secondary_explosion ? 2 : 5;
for (int i = 0; i < cnt; i++) {
Particle p(parent.pos.x, parent.pos.y, false, parent.secondary_colours);
p.vel = Vector2(uniform(-1, 1), uniform(-1, 1)).normalized() * static_cast<float>(randint(3, 8));
p.size = static_cast<float>(randint(1, 2));
p.max_life = 60;
secondary_particles.push_back(p);
}
}
void explode_2026(float ox, float oy, const std::vector<COLORREF>& cols, int fix_time) {
const int R = 8, C = 8, S = 6;
unsigned char n2[R][C] = {{0, 1, 1, 1, 1, 1, 1, 0}, {1, 1, 0, 0, 0, 0, 1, 1}, {0, 0, 0, 0, 0, 0, 1, 1}, {0, 0, 0, 0, 1, 1, 1, 0},
{0, 0, 1, 1, 1, 0, 0, 0}, {0, 1, 1, 0, 0, 0, 0, 0}, {1, 1, 0, 0, 0, 0, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}
};
unsigned char n0[R][C] = {{0, 1, 1, 1, 1, 1, 1, 0}, {1, 1, 0, 0, 0, 0, 1, 1}, {1, 1, 0, 0, 0, 0, 1, 1}, {1, 1, 0, 0, 0, 0, 1, 1},
{1, 1, 0, 0, 0, 0, 1, 1}, {1, 1, 0, 0, 0, 0, 1, 1}, {1, 1, 0, 0, 0, 0, 1, 1}, {0, 1, 1, 1, 1, 1, 1, 0}
};
unsigned char n6[R][C] = {{0, 1, 1, 1, 1, 1, 1, 0}, {1, 1, 0, 0, 0, 0, 0, 0}, {1, 1, 0, 0, 0, 0, 0, 0}, {1, 1, 1, 1, 1, 1, 1, 0},
{1, 1, 0, 0, 0, 0, 1, 1}, {1, 1, 0, 0, 0, 0, 1, 1}, {1, 1, 0, 0, 0, 0, 1, 1}, {0, 1, 1, 1, 1, 1, 1, 0}
};
unsigned char* seq[4] = {&n2[0][0], &n0[0][0], &n2[0][0], &n6[0][0]};
for (int n = 0; n < 4; n++) {
int off_x = n * (C + 2);
for (int r = 0; r < R; r++) {
for (int c = 0; c < C; c++) {
if (seq[n][r * C + c]) {
float x = (off_x + c - 19) * S + uniform(-1, 1);
float y = (r - 4) * S + uniform(-1, 1);
Particle p(ox, oy, false, cols);
p.vel = Vector2(x * 0.18f, y * 0.18f);
p.explosion_radius = 60;
p.max_life = 200;
if (full_secondary_explosion) {
p.can_explode = true;
p.secondary_explosion_time = fix_time;
p.secondary_colours = cols;
}
particles.push_back(p);
}
}
}
}
}
void explode() {
int amt = randint((int)min_max_particles.x, (int)min_max_particles.y);
float ox = firework.pos.x, oy = firework.pos.y;
int fix_t = randint(20, 28);
switch (type) {
case TYPE_2026:
explode_2026(ox, oy, colours, fix_t);
break;
case TYPE_SPHERE:
for (int i = 0; i < amt; i++) {
float a = uniform(0, 6.28f);
Particle p(ox, oy, false, colours);
p.vel = Vector2(cos(a), sin(a)) * uniform(8, 16);
if (full_secondary_explosion) {
p.can_explode = true;
p.secondary_explosion_time = fix_t;
p.secondary_colours = colours;
}
particles.push_back(p);
}
break;
case TYPE_HEART:
for (int i = 0; i < amt; i++) {
float t = uniform(0, 6.28f);
float x = 16 * pow(sin(t), 3);
float y = -(13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t));
float s = uniform(3, 6);
Particle p(ox, oy, false, colours);
p.vel = Vector2(x * s * 0.15f, y * s * 0.15f);
p.explosion_radius = 80;
particles.push_back(p);
}
break;
case TYPE_STAR: {
const int POINTS = 5;
for (int i = 0; i < amt; i++) {
float angle = uniform(0, 6.28f);
int point_idx = static_cast<int>(angle / (6.28f / POINTS));
float star_angle = point_idx * (6.28f / POINTS);
float inner_radius = uniform(4, 8), outer_radius = uniform(12, 20);
float radius = (fmod(angle - star_angle, 6.28f / POINTS) < 6.28f / (2 * POINTS)) ? outer_radius : inner_radius;
Particle p(ox, oy, false, colours);
p.vel = Vector2(cos(angle), sin(angle)) * radius;
p.explosion_radius = 100;
if (full_secondary_explosion) {
p.can_explode = true;
p.secondary_explosion_time = fix_t;
p.secondary_colours = colours;
}
particles.push_back(p);
}
break;
}
case TYPE_LINE: {
int line_type = randint(0, 3);
float angle = 0;
switch (line_type) {
case 0:
angle = 0;
break;
case 1:
angle = 1.57f;
break;
case 2:
angle = 0.785f;
break;
case 3:
angle = 2.356f;
break;
}
for (int i = 0; i < amt; i++) {
float offset = uniform(-20, 20);
float perp_angle = angle + 1.57f;
Vector2 dir(cos(angle), sin(angle)), perp_dir(cos(perp_angle), sin(perp_angle));
Particle p(ox, oy, false, colours);
p.vel = dir * uniform(10, 18) + perp_dir * offset * 0.3f;
p.explosion_radius = 120;
particles.push_back(p);
}
break;
}
case TYPE_SPARKLE: {
for (int i = 0; i < amt; i++) {
Particle p(ox, oy, false, colours, true);
float speed = uniform(5, 20);
float a = uniform(0, 6.28f);
p.vel = Vector2(cos(a), sin(a)) * speed;
p.max_life = randint(60, 100);
p.size = static_cast<float>(randint(1, 2));
if (randint(0, 3) == 0) {
p.can_explode = true;
p.secondary_explosion_time = randint(10, 30);
p.secondary_colours = colours;
}
particles.push_back(p);
}
break;
}
case TYPE_CIRCLE_RING: {
float ring_radius = uniform(15, 25);
for (int i = 0; i < amt; i++) {
float a = uniform(0, 6.28f);
Particle p(ox, oy, false, colours);
float offset = uniform(-2, 2);
p.vel = Vector2(cos(a), sin(a)) * (ring_radius + offset);
p.explosion_radius = 150;
if (full_secondary_explosion) {
p.can_explode = true;
p.secondary_explosion_time = fix_t;
p.secondary_colours = colours;
}
particles.push_back(p);
}
break;
}
case TYPE_FLOWER: {
const int PETALS = randint(6, 12);
const int LAYERS = 3;
for (int layer = 0; layer < LAYERS; layer++) {
float layer_radius = 8 + layer * 6;
for (int i = 0; i < amt / LAYERS; i++) {
int petal_idx = randint(0, PETALS - 1);
float base_angle = petal_idx * (6.28f / PETALS);
float angle = base_angle + uniform(-0.2f, 0.2f);
Particle p(ox, oy, false, colours);
p.vel = Vector2(cos(angle), sin(angle)) * uniform(layer_radius, layer_radius + 4);
p.explosion_radius = 100;
particles.push_back(p);
}
}
break;
}
case TYPE_SPIRAL: {
float turns = uniform(1, 3);
for (int i = 0; i < amt; i++) {
float t = uniform(0, turns * 6.28f);
float radius = t * 2;
float angle = t;
Particle p(ox, oy, false, colours);
p.vel = Vector2(cos(angle), sin(angle)) * uniform(radius * 0.5f, radius);
p.explosion_radius = 150;
p.max_life = 150;
particles.push_back(p);
}
break;
}
case TYPE_DOUBLE_HEART: {
for (int i = 0; i < amt / 2; i++) {
float t = uniform(0, 6.28f);
float x = 16 * pow(sin(t), 3);
float y = -(13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t));
float s = uniform(2, 5);
Particle p(ox - 20, oy, false, colours);
p.vel = Vector2(x * s * 0.12f, y * s * 0.12f);
p.explosion_radius = 80;
particles.push_back(p);
}
for (int i = 0; i < amt / 2; i++) {
float t = uniform(0, 6.28f);
float x = 16 * pow(sin(t), 3);
float y = -(13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t));
float s = uniform(2, 5);
Particle p(ox + 20, oy, false, colours);
p.vel = Vector2(x * s * 0.12f, y * s * 0.12f);
p.explosion_radius = 80;
particles.push_back(p);
}
break;
}
case TYPE_RANDOM_BURST: {
for (int i = 0; i < amt; i++) {
Particle p(ox, oy, false, colours);
float speed = uniform(5, 25);
float a = uniform(0, 6.28f);
p.vel = Vector2(cos(a), sin(a)) * speed;
p.max_life = randint(80, 150);
p.size = static_cast<float>(randint(1, 4));
p.explosion_radius = randint(50, 150);
if (randint(0, 5) == 0) {
p.can_explode = true;
p.secondary_explosion_time = randint(15, 40);
p.secondary_colours = colours;
}
particles.push_back(p);
}
break;
}
case TYPE_NULL:
break;
}
int ember_count = randint(20, 40);
for (int i = 0; i < ember_count; i++) {
std::vector<COLORREF> ember_col;
ember_col.push_back(firework.original_colour);
Particle e(ox, oy, false, ember_col, true);
e.vel = Vector2(uniform(-1.0f, 1.0f), uniform(-1.0f, 1.0f)).normalized() * uniform(2.0f, 6.0f);
e.size = uniform(0.5f, 1.5f);
e.max_life = randint(150, 250);
e.mass = 0.3f;
e.thermal_strength = 0.2f;
embers.push_back(e);
}
}
bool remove() {
particles.erase(remove_if(particles.begin(), particles.end(), [](Particle & p) {
return p.remove;
}), particles.end());
secondary_particles.erase(remove_if(secondary_particles.begin(), secondary_particles.end(), [](Particle & p) {
return p.remove;
}), secondary_particles.end());
smoke_particles.erase(remove_if(smoke_particles.begin(), smoke_particles.end(), [](const SmokeParticle & s) {
return s.remove;
}), smoke_particles.end());
embers.erase(remove_if(embers.begin(), embers.end(), [](Particle & p) {
return p.remove;
}), embers.end());
return exploded && particles.empty() && secondary_particles.empty() && smoke_particles.empty() && embers.empty() && explosion_flashes.empty();
}
};
HWND g_hWnd = nullptr;
HDC g_hMemDC = nullptr;
HBITMAP g_hMemBmp = nullptr;
std::vector<Firework> g_fireworks;
bool g_running = true;
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: {
HDC hdc = GetDC(hWnd);
g_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
g_bmi.bmiHeader.biWidth = DISPLAY_WIDTH;
g_bmi.bmiHeader.biHeight = -DISPLAY_HEIGHT;
g_bmi.bmiHeader.biPlanes = 1;
g_bmi.bmiHeader.biBitCount = 32;
g_bmi.bmiHeader.biCompression = BI_RGB;
g_hMemDC = CreateCompatibleDC(hdc);
g_hMemBmp = CreateDIBSection(g_hMemDC, &g_bmi, DIB_RGB_COLORS, (void**)&g_pMemBits, NULL, 0);
SelectObject(g_hMemDC, g_hMemBmp);
ReleaseDC(hWnd, hdc);
return 0;
}
case WM_LBUTTONDOWN: {
int x = LOWORD(lParam);
g_fireworks.push_back(Firework((float)x, (float)DISPLAY_HEIGHT));
return 0;
}
case WM_RBUTTONDOWN: {
int x = LOWORD(lParam);
Firework fw((float)x, (float)DISPLAY_HEIGHT);
fw.type = TYPE_2026;
fw.colours.clear();
fw.colours.push_back(RGB(255, 50, 50));
fw.colours.push_back(RGB(255, 215, 0));
fw.colours.push_back(RGB(255, 255, 200));
fw.colour = RGB(255, 215, 0);
fw.firework.original_colour = fw.colour;
fw.firework.colour = fw.colour;
g_fireworks.push_back(fw);
return 0;
}
case WM_KEYDOWN:
if (wParam == VK_ESCAPE) {
g_running = false;
DestroyWindow(hWnd);
}
return 0;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
DWORD bg_dword = (GetBValue(BG_COLOR)) | (GetGValue(BG_COLOR) << 8) | (GetRValue(BG_COLOR) << 16) | (0xFF << 24);
DWORD* pDwords = (DWORD*)g_pMemBits;
int pixel_count = DISPLAY_WIDTH * DISPLAY_HEIGHT;
for (int i = 0; i < pixel_count; i++) pDwords[i] = bg_dword;
for (size_t i = 0; i < g_fireworks.size(); i++) g_fireworks[i].update(g_hMemDC);
BitBlt(hdc, 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, g_hMemDC, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
return 0;
}
case WM_ERASEBKGND:
return 1;
case WM_CLOSE:
g_running = false;
DestroyWindow(hWnd);
return 0;
case WM_DESTROY:
DeleteObject(g_hMemBmp);
DeleteDC(g_hMemDC);
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) {
ShowWindow(GetConsoleWindow(), SW_HIDE);
WNDCLASSEX wc = {sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS, WndProc, 0, 0, hInstance, LoadIcon(NULL, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW), (HBRUSH)GetStockObject(BLACK_BRUSH), NULL, "FireworkReal", LoadIcon(NULL, IDI_APPLICATION)};
RegisterClassEx(&wc);
g_hWnd = CreateWindowEx(WS_EX_COMPOSITED, "真实烟花","真实烟花模拟(左键发射普通烟花,右键有惊喜)",WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME,CW_USEDEFAULT, CW_USEDEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,NULL, NULL, hInstance, NULL);
ShowWindow(g_hWnd, nCmdShow);
UpdateWindow(g_hWnd);
MSG msg;
LARGE_INTEGER freq, start, end;
QueryPerformanceFrequency(&freq);
double frameTime = 1.0 / 60.0;
while (g_running) {
QueryPerformanceCounter(&start);
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT) g_running = false;
}
g_wind_x += uniform(-0.0015f, 0.0015f);
g_wind_x = max(-0.08f, min(0.08f, g_wind_x));
g_fireworks.erase(remove_if(g_fireworks.begin(), g_fireworks.end(), [](Firework & fw) {
return fw.remove();
}), g_fireworks.end());
InvalidateRect(g_hWnd, NULL, FALSE);
UpdateWindow(g_hWnd);
QueryPerformanceCounter(&end);
double elapsed = (end.QuadPart - start.QuadPart) / (double)freq.QuadPart;
if (elapsed < frameTime) Sleep((DWORD)((frameTime - elapsed) * 1000));
}
UnregisterClass("FireworkReal", hInstance);
return 0;
}
运行效果(自己去看)TvT
(点个赞呗thank you)
新年快乐!
这里空空如也





















有帮助,赞一个