c语言烟花(爱心烟花,碰撞烟花,跳动爱心)

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>

#include <stdlib.h>

#include<math.h>

#include<time.h>

#include<conio.h>

#include<windows.h>

#include<graphics.h>

#include<mmsystem.h>//播放音乐头文件

#pragma comment(lib,"winmm.lib")//多媒体设备接口库文件,音乐

#pragma comment(lib,"WINMM.LIB");//及这个预处理

#define PI 3.1415

#define NUM 13//烟花弹数量

#define WIDETH 1200

#define HEIGHT 800//对应窗口宽高

#define SIZE 9

#define CANVAS_WIDTH 1200

#define CANVAS_HEIGHT 800

#define CANVAS_CEBTER_X CANVAS_WIDTH / 2

#define CANVAS_CEBTER_Y CANVAS_HEIGHT / 2

struct Jet {//烟花弹

int x1, x2, x, y;//坐标

int hx, hy;//最高点坐标,炸的地方防过早炸

bool isLaunch;//烟花弹是否在发射中,bool就是0和1,表示真假

IMAGE img[2];//两个图片数组,一明一暗实现上升闪烁效果切换展示

byte n : 1;//位段 010101010,辅助上面图片闪烁第0第1

int height;

}jet[NUM];//数组

struct Fire

{

int r;//当前绽放半径

int max_r;//最大半径

int cen_x, cen_y;//中心点坐标

int x, y,x1,x2;//坐标

int width, height;//240 240

DWORD pixel[240][240];//dword就是无符号整型

bool isshow;//是否开始显示,烟花是否准备好

bool isdraw;//是否开始绘画

}fire[NUM];

void welcome()

{

mciSendString(L"open ./res/小幸运.mp3", 0, 0, 0);//要有空格

mciSendString(L"play ./res/小幸运.mp3", 0, 0, 0);

for (int i = 0; i < 50; i++) {

//画一次请除一次屏幕

cleardevice();//清除绘图,使得只有一行字

int x = 500 + 180 * sin(2 * PI / 60 * i);//2pi整圆,60等分相当于每次懂的角度

int y = 200 + 180 * cos(2 * PI / 60 * i);//实现转动,文字坐标

//设置字体格式

settextstyle(i, 0, L"楷体");

settextcolor(RGB(0, 202, 0));//255

setbkmode(TRANSPARENT);//文字背景,括号里面是模式,透明的意思

outtextxy(x, y, L"浪漫表白程序");//文字

Sleep(20);//延时,速度

}

//_getch();//按任意键继续

cleardevice();

settextstyle(25, 0, L"楷体");//字的格式

outtextxy(350, 100, L"亲爱的宝贝");//输出内容

outtextxy(350, 150, L"我爱你");

//outtextxy(450, 200, L"xxx");//稍微后一点名字

}

void InitDate(int i);//声明一下初始化

void loadImg() {//加载图片

//绽放烟花

IMAGE bloomImg1, tImg1;

loadimage(&bloomImg1, L"./rea/flower1.jpg", 240, 3120);

for (int i = 0; i < NUM; i++) {

InitDate(i);

SetWorkingImage(&bloomImg1);

getimage(&tImg1, 0, i * 240, 240, 240);

SetWorkingImage(&tImg1);

for (int a = 0; a < 240; a++) {

for (int b = 0; b < 240; b++) {

fire[i].pixel[a][b] = getpixel(a, b);//getpixel获取的是像素点的坐标

}

}

}

IMAGE jetImg1, jetImg2;//定义图片

loadimage(&jetImg1, L"./rea/left.jpg", 90, 200);

SetWorkingImage(&jetImg1);//此特定区域绘图

for (int i = 0; i < NUM; i++) {

int n = rand() % 5;//5张不同颜色

getimage(&jet[i].img[0], 0, 20 * n, 90, 20);//同色不同亮度,这玩意给image0

getimage(&jet[i].img[1], 0, 20 * (n + 5), 90, 20);//给image1

jet[i].isLaunch = false;//布尔型true,lalse就是0,1

}

SetWorkingImage();//恢复工作区

loadimage(&jetImg2, L"./rea/right.jpg", 90, 200);

SetWorkingImage(&jetImg2);//此特定区域绘图

for (int i = 0; i < NUM; i++) {

int n = rand() % 5;//5张不同颜色

getimage(&jet[i].img[0], 0, 20 * n, 90, 20);//同色不同亮度,这玩意给image0

getimage(&jet[i].img[1], 0, 20 * (n + 5), 90, 20);//给image1

jet[i].isLaunch = false;//布尔型true,lalse就是0,1

//putimage(900, (i + 1) * 20, &jet[i].img[0]);

}

SetWorkingImage();//恢复工作区

//绽放烟花

IMAGE bloomImg, tImg;

loadimage(&bloomImg, L"./res/flower.jpg", 3120, 240);

for (int i = 0; i < NUM; i++) {

SetWorkingImage(&bloomImg);

getimage(&tImg, i * 240, 0, 240, 240);

InitDate(i);

SetWorkingImage(&tImg);

for (int a = 0; a < 240; a++) {

for (int b = 0; b < 240; b++) {

fire[i].pixel[a][b] = getpixel(a, b);//getpixel获取的是像素点的坐标

}

}

}

//初始化烟花弹

IMAGE jetImg;//定义图片

loadimage(&jetImg, L"./res/launch.jpg", 200, 50);//加载图片,putimage是放出图片

SetWorkingImage(&jetImg);//此特定区域绘图

for (int i = 0; i < NUM; i++) {

// SetWorkingImage(&fireing);//操作图片

int n = rand() % 5;//5张不同颜色

getimage(&jet[i].img[0], 20 * n, 0, 20, 50);//同色不同亮度,这玩意给image0

getimage(&jet[i].img[1], 20 * (n + 5), 0, 20, 50);//给image1

jet[i].isLaunch = false;//布尔型true,lalse就是0,1

//putimage(i * 108,0,&jet[1].img[0]);//这里只是烟花按的图片,还没上天

}

SetWorkingImage();//恢复工作区

}

void InitDate(int i) {//所有初始化数据

int r[13] = { 120, 120, 155, 123, 130, 147, 138, 138, 130, 135, 140, 132, 155 };

int x[13] = { 120, 120, 110, 117, 110, 93,  102, 102, 110, 105, 100, 108, 110 };

int y[13] = { 120, 120, 85,  118, 120, 103, 105, 110, 110, 120, 120, 104, 85 };

fire[i].cen_x = 120;

fire[i].cen_y = 120;

fire[i].max_r = 120;

fire[i].r = 0;

fire[i].width = 240;

fire[i].height = 240;

fire[i].isdraw = false;

fire[i].isshow = false;

}

//产生烟花弹

void creatJet() {//初始化操作

int i = rand() % NUM;//0-13的数字

if (jet[i].isLaunch == false) {//没有发射

jet[i].x = rand() % (WIDETH - 20);//初始化防擦边,里面任意一个数

jet[i].y = rand() % 100 + HEIGHT;//本来+200,去掉之后不让从窗口里面出来,从下面出来

jet[i].hx = jet[i].x;//因为笔直走,不变

jet[i].hy = rand() % (HEIGHT / 3 * 2);//在三分之二处爆炸

jet[i].isLaunch = true;//让他发射

}

}

//发射烟花

void launch() {

for (int i = 0; i < NUM; i++) {

if (jet[i].isLaunch) {//在发射

putimage(jet[i].x, jet[i].y, &jet[i].img[jet[i].n], SRCINVERT);//图片和背景进行什么样的二进制操作,srcinver去除轨迹

//判断到最高点

if (jet[i].y > jet[i].hy) {//在下面还要上升

jet[i].y -= 5;//往上走是y减

jet[i].n++;//byte中n不是0就是1,对应image实现闪烁效果

}

putimage(jet[i].x, jet[i].y, &jet[i].img[jet[i].n], SRCINVERT);

if (jet[i].y <= jet[i].hy) {//到达最高点

putimage(jet[i].x, jet[i].y, &jet[i].img[jet[i].n], SRCINVERT);//让末尾的烟花弹消失

jet[i].isLaunch = false;//让烟花停止

//Sleep(1000);

//可以放烟花

for (int i = 0; i < NUM; i++) {

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

cleardevice();

jet[i].x = x[i] * 10;

jet[i].y = (y[i] + 75) * 10;

jet[i].hx = jet[i].x;

jet[i].hy = y[i] * 10;

jet[i].height = jet[i].y - jet[i].hy;

jet[i].isLaunch = true;

putimage(jet[i].x, jet[i].y, &jet[i].img[jet[i].n], SRCINVERT); // 显示烟花弹

fire[i].x = jet[i].x + 10;

fire[i].y = jet[i].hy;

fire[i].isshow = false;

fire[i].r = 0;

// fire[i].x = jet[i].x;

// fire[i].y = jet[i].y;

fire[i].isshow = true;

}

}

}

}

}

void bloom(DWORD* pMem) {//传参数

//printf("NUM=%d", NUM);

if (!pMem) {

return;

}//解决空指针问题

for (int i = 0; i < NUM; i++) {

if (fire[i].isshow) {//烟花已经准备好

if (fire[i].r < fire[i].max_r) {//小于最大半径

fire[i].r++;//扩大

fire[i].isdraw = true;//可以开始绘制烟花

}

if (fire[i].r >= fire[i].max_r) {

InitDate(i);//里面有isdraw=false

}

}

if (fire[i].isdraw) {

//求当前坐标下园上每个点对应的弧度

for (double a = 0; a <= 2 * PI; a += 0.01) {

int img_x = fire[i].cen_x + fire[i].r * cos(a);//求园上每个点坐标

int img_y = fire[i].cen_y + fire[i].r * sin(a);

if (img_x > 0 && img_x < fire[i].width && img_y>0 && img_y < fire[i].height) {//正方形中的园,不越界

int b = fire[i].pixel[img_x][img_y] & 0xff;

int g = fire[i].pixel[img_x][img_y] >> 8 & 0xff;

int r = fire[i].pixel[img_x][img_y] >> 16;

//针对现在绽放点对应的屏幕坐标

int win_x = fire[i].x + fire[i].r * cos(a);

int win_y = fire[i].y + fire[i].r * sin(a);

if (r > 0x20 && g > 0x20 && b > 0x20 && win_x > 0 && win_x < WIDETH && win_y>0 && win_y < WIDETH)

{

pMem[win_y * WIDETH + win_x] = BGR(fire[i].pixel[img_x][img_y]);

}

}

}

fire[i].isdraw = false;

}

}

}

//产生烟花弹

void createJet1() {//初始化操作

int i = rand() % NUM;//0-13的数字

if (jet[i].isLaunch == false) {//没有发射

jet[i].y = rand() % (HEIGHT);//初始化防擦边,里面任意一个数

jet[i].x1 = 0;//本来+200,去掉之后不让从窗口里面出来,从下面出来

jet[i].x2 = 1200;

jet[i].hy = jet[i].y;//因为笔直走,不变

jet[i].hx = 600;//在中间处爆炸

jet[i].isLaunch = true;//让他发射

}

}

void launch1() {

for (int i = 0; i < NUM; i++) {

if (jet[i].isLaunch) {//在发射

putimage(jet[i].x1, jet[i].y, &jet[i].img[jet[i].n]);//图片和背景进行什么样的二进制操作,srcinver去除轨迹

putimage(jet[i].x2, jet[i].y, &jet[i].img[jet[i].n]);

if (jet[i].x1 < jet[i].hx) {

jet[i].x1 += 5;

jet[i].n++;

}

//putimage(jet[i].x1, jet[i].y, &jet[i].img[jet[i].n]);

putimage(jet[i].x1, jet[i].y, &jet[i].img[jet[i].n]);

if (jet[i].x1 >= jet[i].hx) {

putimage(jet[i].x1, jet[i].y, &jet[i].img[jet[i].n]);

jet[i].isLaunch = false;

//可以放烟花

fire[i].x1 = jet[i].x1;

fire[i].y = jet[i].y;

fire[i].isshow = true;

}

if (jet[i].x2 > jet[i].hx) {

jet[i].x2 -= 5;

jet[i].n++;

}

putimage(jet[i].x2, jet[i].y, &jet[i].img[jet[i].n]);

if (jet[i].x2 <= jet[i].hx) {

putimage(jet[i].x2, jet[i].y, &jet[i].img[jet[i].n]);

jet[i].isLaunch = false;

//可以放烟花

fire[i].x2 = jet[i].x2;

fire[i].y = jet[i].y;

fire[i].isshow = true;

}

}

}

}

void bloom1(DWORD* pMem1) {//传参数

if (!pMem1) {

return;

}//解决空指针问题

for (int i = 0; i < NUM; i++) {

if (fire[i].isshow) {//烟花已经准备好

if (fire[i].r < fire[i].max_r) {//小于最大半径

fire[i].r++;//扩大

fire[i].isdraw = true;//可以开始绘制烟花

}

if (fire[i].r >= fire[i].max_r - 1) {

fire[i].isdraw = false;

InitDate(i);//里面有isdraw=false

}

}

if (fire[i].isdraw) {

//求当前坐标下园上每个点对应的弧度

for (double a = 0; a <= 2 * PI; a += 0.01) {

int img_x = fire[i].cen_x + fire[i].r * cos(a);//求园上每个点坐标

int img_y = fire[i].cen_y + fire[i].r * sin(a);

if (img_x > 0 && img_x < fire[i].width && img_y>0 && img_y < fire[i].height) {//正方形中的园,不越界

//针对现在绽放点对应的屏幕坐标

int win_x = fire[i].x2 + fire[i].r * cos(a);

//int win_x = WIDETH / 2;

int win_y = fire[i].y + fire[i].r * sin(a);

if (win_x > 0 && win_x < WIDETH && win_y>0 && win_y < HEIGHT)

{

pMem1[win_y * WIDETH + win_x] = BGR(fire[i].pixel[img_x][img_y]);

}

}

}

}

}

}

struct Point {

double x, y;

COLORREF color;//保存坐标与颜色

};

COLORREF colors[256] = { RGB(255,32,83),RGB(252,222,250) ,RGB(255,0,0) ,

RGB(255,0,0) ,RGB(255,2,2) ,RGB(255,0,8) ,RGB(255,5,5) };

const int xScreen = 1200;//屏幕宽度

const int yScreen = 800;//屏幕高度

const double e = 2.71828;

const double averag_distance = 0.162;//弧度0.01增加时,原始参数方程每个点平均距离

const int quantity = 506;//爱心需要点的数目

const int circles = 210;//组成爱心主体的爱心个数,200圈,爱心内部填充物用不同大小的同心圆代替

const int frames = 20;//爱心扩张一次的帧数

Point  origin_points[quantity];

Point  points[circles * quantity];

IMAGE images[frames];

//坐标转变为屏幕坐标

double screen_x(double x)

{

x += xScreen / 2;

return x;

}

double screen_y(double y)

{

y = -y + yScreen / 2;

return y;

}

int creat_random(int x1, int x2)

{

if (x2 > x1)

return  rand() % (x2 - x1 + 1) + x1;

}

void creat_data()

{

//保存相邻的坐标信息以便于使用计算距离

int index = 0;

double x1 = 0, y1 = 0, x2 = 0, y2 = 0;

for (double radian = 0.1; radian <= 2 * PI; radian += 0.005)

{

//爱心参数方程

x2 = 16 * pow(sin(radian), 3);

y2 = 13 * cos(radian) - 5 * cos(2 * radian) - 2 * cos(3 * radian)

- cos(4 * radian);

//计算连点之间的距离

double distance = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));

//当两点之间的距离大于平均距离时保存这个点,防止点过度密集,不然上下两头非常密集

if (distance > averag_distance)

{

x1 = x2, y1 = y2;

origin_points[index].x = x2;

origin_points[index++].y = y2;

}

}

index = 0;

for (double size = 0.1, lightness = 1.5; size <= 20; size += 0.1)

{

double success_p = 1 / (1 + pow(e, 8 - size / 2));

//用sigmoid函数计算当前系统的成功率,作用是把输入的数据压缩到0-1进行输出,可以用作概率,如果80%,就有100中80个点筛选留下来

if (lightness > 1) lightness -= 0.0025;

//遍历原始爱心的所有数据

for (int i = 0; i < quantity; ++i)

{

//用概率对进行筛选

if (success_p > creat_random(0, 100) / 100.0)//0-100数字再次除以100,以便于和成功概率比对

{

//从颜色数组随机获取

COLORREF color = colors[creat_random(0, 6)];//获取随机生成的颜色

points[index].color = RGB(GetRValue(color) / lightness, GetGValue(color) / lightness, GetBValue(color) / lightness);

//对原始数据乘系数保存在系point中

points[index].x = size * origin_points[i].x + creat_random(-4, 4);//取一个随机数让分布更加均匀

points[index++].y = size * origin_points[i].y + creat_random(-4, 4);

}

}

}

//内部填充的所有点的数量

int points_size = index;

for (int frame = 0; frame < frames; ++frame)//外循环产生20张单独图片

{

//初始化每张图片

images[frame] = IMAGE(xScreen, yScreen);//帮图片设置宽度高度,让绘制的步骤在单独的图片上进行,而不是屏幕

//把第frame张图片设置为当前工作图片

SetWorkingImage(&images[frame]);

for (index = 0; index < points_size; ++index)

{

double x = points[index].x, y = points[index].y;//先把当前点赋值给x和y

double distance = sqrt(pow(x, 2) + pow(y, 2));//距离原点长度

double diatance_increase = -0.0009 * distance * distance

+ 0.35714 * distance + 5;

double x_increase = diatance_increase * x / distance / frames;//x轴的增长是distence*cosa

double y_increase = diatance_increase * y / distance / frames;//y轴是distence*sina

points[index].x += x_increase;//更新每一次跳动的数据

points[index].y += y_increase;

setfillcolor(points[index].color);//填充颜色

solidcircle(screen_x(points[index].x), screen_y(points[index].y), 1);//算的方法基于数学坐标,这里转换为屏幕坐标,20次运行得到20帧图片

}

//产生外围跳动的小红点

for (double size = 17; size < 23; size += 0.3)

{

for (index = 0; index < quantity; ++index)

{//原始数据500多个点,适当删除

if ((creat_random(0, 100) / 100.0 > 0.6 && size >= 20)

|| (size < 20 && creat_random(0, 100) / 100.0 > 0.95))//系数大于20,通过概率百分之40;系数小于20概率百分之5

{

double x, y;

if (size >= 20)

{

x = origin_points[index].x * size +

creat_random(-frame * frame / 5 - 15, frame * frame / 5 + 15);//15是数据的不断优化,发现更好看

y = origin_points[index].y * size +

creat_random(-frame * frame / 5 - 15, frame * frame / 5 + 15);

}

else

{

x = origin_points[index].x * size + creat_random(-5, 5);

y = origin_points[index].y * size + creat_random(-5, 5);

}

setfillcolor(colors[creat_random(0, 6)]);//获取颜色

solidcircle(screen_x(x), screen_y(y), 1);//转化坐标

}

}

}

}

SetWorkingImage();

}

int main(void) {

initgraph(1200, 800);//创建窗口

srand((unsigned)time(NULL));//设置随机数种子

welcome();

loadImg();

DWORD* pMem = GetImageBuffer();//获取窗口内的内存指针

DWORD* pMem1 = GetImageBuffer();//获取窗口内的内存指针

int m = 1000;

while (m) {

for (int i = 0; i < WIDETH; i++) {

for (int k = 0; k < 30; k++) {

int x = rand() % WIDETH;

int y = rand() % HEIGHT;

if (y < HEIGHT) {

pMem1[y * WIDETH + x] = BLACK;//不断打印黑点覆盖住爆炸的烟花

}

}

}

createJet1();

launch1();

//style();

bloom1(pMem1);

FlushBatchDraw();

Sleep(30);//爆炸速度

m--;

}

BeginBatchDraw();

int t = 1000;

while (t) {

for (int i = 0; i < WIDETH; i++) {

for (int k = 0; k < 4; k++) {

int x = rand() % WIDETH;

int y = rand() % HEIGHT;

if (y < HEIGHT) {

pMem[y * WIDETH + x] = BLACK;//不断打印黑点覆盖住爆炸的烟花

}

}

}

creatJet();

launch();

//style();

bloom(pMem);

FlushBatchDraw();

Sleep(10);//爆炸速度

t--;

}

//if (pMem != NULL);

BeginBatchDraw();

//srand(time(0));

creat_data();

bool extend = true, shrink = false;

for (int frame = 0; !_kbhit();)

{

putimage(0, 0, &images[frame]);

FlushBatchDraw();

Sleep(20);

cleardevice();

if (extend)

frame == 19 ? (shrink = true, extend = false) : ++frame;

else

frame == 0 ? (shrink = false, extend = true) : --frame;

}

EndBatchDraw();

closegraph();

return 0;

}

验证与结论:

每一个函数都有自己对应的效果,写完后将其添加到主函数里面,看看效果,如果成功运行,那就成功;如果没有实现就不断改进,不断尝试。通过成功运行,我的结论是烟花程序成功。

总结与心得体会:

这次烟花程序的设计是我目前写过最大的一项工作,不敢相信之前c语言都学不明白的我会写出几百行代码而且还能成功运行,我学习了easyx里面各种图形函数的使用,与visual studio的使用方法,及项目完成的基本流程,在不断的卡壳中绞尽脑汁寻求突破,有的时候代码真找不出问题,但就是运行不了,编译器都没报错,结果运行几秒自己给我退出去了等问题中,我锻炼了自学能力,与自己解决问题的能力,我相信这些很多东西是老师上课不会去讲的,而是要在一次次的实践中自己感悟出来的,我还体会到不需要一次性追求到完美的境界,无论怎么样,先让程序跑起来,一些功能太复杂先放一放,把其他的程序先运行起来,或者在出错的程序里面去单独新建一个主函数单独测试,最后基本工作完成后,再去不断优化,可以调数值,或者加入几层循环,多尝试几次,可能就会有惊喜。

  • 19
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值