主函數
int main()
{
initgraph(1200, 800);//
DWORD tt1 = GetTickCount();
DWORD* pmem = GetImageBuffer();//獲取顯存指針
load();//加載圖片
//初始化數據(先初始化煙花彈 )
for (int i = 0; i < NUM; i++)
{
init(i);
}
BeginBatchDraw();//開始批量繪制
while (1)
{
Sleep(10);//擦除時間
for (int c = 0;c < 1000;c++)
{
for (int r = 0; r < 2; r++)//保證每次擦除兩千的像素點
{
//隨機找兩千個窗口的點
int px1 = rand() % 1200;
int py1 = rand() % 800;
if (py1 < 799)//防止越界
{
pmem[py1 * 1200 + px1] = pmem[py1*1200 + px1 + 1] = BLACK;//擦除 -》 將像素點變成背景色 (黑色)
}
}
}
choise(tt1);
shoot();
firedraw(pmem);//顯出指針
FlushBatchDraw();//結束批量繪制
}
getchar();//可不需要
return 0;
}
頭文件 結構體
#include <stdio.h>
#include <graphics.h>
#include <windows.h>
#include <math.h>
#include <time.h>
#include<stdlib.h>
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib")//庫文件
#define NUM 13
//煙花
struct Fire
{
int x, y;//煙花在窗口里面爆炸的坐標
int r;//煙花爆炸的當前半徑
int max_r;//煙花爆炸最大半徑
int cen_x, cen_y;//圖片坐標系中的中心位置
int xy[240][240];//240*240的像素數組,圖片的像素點保存在這里
bool show;//煙花是否開始爆炸 表示可不可以爆炸
bool draw;//開始繪制 表示是否爆炸
DWORD t1, t2, dt;//單位時間 幀數 爆炸的速度
//unsigned long = DWORD 4字節
}fire[NUM];//
struct Jet
{
int x, y; //煙花彈的坐標 當前的坐標
int hx, hy;//煙花彈最高的坐標
bool shoot;//用一個數組來存放煙花彈的數據
//煙花彈的狀態 是否發射的狀態 true false
unsigned long t1, t2, dt; //速度 dt = t2 - t2 可用DWORD 代替
IMAGE img[2];//兩張圖片 一明一亮 img[0]表示暗色 img[1]亮色
//模擬一閃一閃的效果
byte n : 1;//typedef unsigned char byte;
//C語言結構體 位段 一個位 1/8字節 假設n++(01010010100)要么是零 要么是一
//img[n++]//一閃一閃
}jet[NUM];
加載圖片
void load()
{
mciSendString(L"open ./bk1.mp3 alias music", 0, 0, 0);
mciSendString(L"play music", 0, 0, 0);//播放背景音樂
srand(time(NULL));//初始化隨機種子
IMAGE bigimg;//一整張圖片加載進來
loadimage(&bigimg, L"./shoot.jpg", 200, 50);
//把shoot.jpg保存在bigimg
SetWorkingImage(&bigimg);//十個煙花彈的圖片
for (int i = 0; i < NUM; i++)
{
int n = rand() % 5;
getimage(&jet[i].img[0],n*20,0,20,50);//隨機將前面的5張的任意一張圖片保存到。。
getimage(&jet[i].img[1],(n + 5)*20,0,20,50);
}
SetWorkingImage(NULL);//把工作區域回到窗口
IMAGE flower, stepimg;//定義兩張圖片 一大一小
loadimage(&flower, L"./flower.jpg", 3120, 240);//3120 240(高度) 13張
//工作區 默認工作區是窗口
//flower.jpg這個圖片中有13朵煙花
//getimage(&stepimg,);
for (int i = 0; i < NUM; i++)
{
SetWorkingImage(&flower);//在flower圖片上,裁圖片
getimage(&stepimg, i * 240, 0, 240, 240);//裁剪第一朵煙花 獲取
SetWorkingImage(&stepimg);//小圖片搞事情(ke ke )
for (int a = 0; a < 240; a++)
{
for (int b = 0; b < 240; b++)
{
fire[i].xy[a][b] = getpixel(a, b);//獲取小圖片上的每一個像素,存放在xy數組
}
}
}
SetWorkingImage(NULL);//工作區回到窗口
//return 0;
}
數據的初始化
//初始化
void init(int i)
{
//初始化煙花彈的數據
jet[i].x = 0;
jet[i].y = 0;
jet[i].hx = 0;
jet[i].hy = 0;
jet[i].t1 = GetTickCount();//獲取系統時間
jet[i].t2 = 0;
jet[i].dt = rand()%10 + 1;//單位時間 1ms - 10ms
jet[i].n = 0;
jet[i].shoot = false;
fire[i].x = rand() % 1200;
fire[i].y = rand() % 800;
fire[i].r = 0;
fire[i].max_r = 120;//圖片的寬度240 240
fire[i].cen_x = 120;
fire[i].cen_y = 120;
//fire[i].xy[240][240];
fire[i].show = false; //不能一上來就爆炸啊
fire[i].draw = false;
fire[i].t1 = GetTickCount();//獲取系統的時間
//fire[i].t2;
fire[i].dt = 5;//爆炸的間隔 5毫秒
fire[i].xy[240][240];//矩陣 像素點
}
最核心的地方
//最核心的地方
void firedraw(DWORD* pmem)//顯存指針
{
//邏輯
for (int i = 0; i < NUM; i++)
{
fire[i].t2 = GetTickCount();//GetTickCount:獲取系統的時間
//“detat = t2 - t1”“
if (fire[i].t2 - fire[i].t1 >= fire[i].dt && fire[i].show == true)
{
//r不能超過max_r
if (fire[i].r < fire[i].max_r)
{
fire[i].r++;//半徑不斷變大 半徑變大 煙花變大
fire[i].draw = true;//可以繪制
}
//萬一超過 爆炸結束
if (fire[i].r >= fire[i].max_r - 1)
{
fire[i].draw = false;
init(i);//爆炸結束給內存初始化,等待下一次爆炸
}
//重置時間 否則永遠t2-t1不為5(只有一次為5)
fire[i].t1 = fire[i].t2;
}
//如果是可以繪制的狀態
if (fire[i].draw = true)
{
// 繪制不是貼圖 而是輸出像素點
// 像素點 就是顏色(顏色:32位的數據)
// 00000000 00000000 00000000 00000000
// a透明度 red green blue
for (double a = 0; a <= 6.28; a += 0.01)
{
//x1 y1 圖片坐標系的坐標
int x1 = fire[i].cen_x + fire[i].r * cos(a);//這是初中數學就不講了
//三角函數
int y1 = fire[i].cen_y - fire[i].r * sin(a);
//編程坐標系 與數學不一樣
//以半徑r得到圓弧上的每一個x和y
if (x1 > 0 && x1 < 240 && y1 > 0 && y1 < 240)
{
//分開32位數據 顏色 00000000 00000000 00000000
//點不能超出圖片的該范圍以外
int b = fire[i].xy[x1][y1] & 0xff;//blue
int g = (fire[i].xy[x1][y1] >> 8) & 0xff;//green
int r = (fire[i].xy[x1][y1] >> 16);//red
// 像素轉移 小圖片->工作區
int xx = fire[i].x + fire[i].r * cos(a);
int yy = fire[i].y - fire[i].r * sin(a);
//xx yy 窗口坐標系的坐標
if (r > 0x20 && g > 0x20 && b > 0x20 && xx > 0 && xx < 1200 && yy > 0 && yy < 800)
{
//如果這個點顏色太深(黑) 就不輸出
pmem[yy * 1200 + xx] = BGR(fire[i].xy[x1][y1]);
// 移像素點 圖片 =》 窗口
}
}
}
//總共得到628個圓弧上的點
fire[i].draw = false;
}
}
}
choise
//選擇 出現 一點時間間隔
void choise(DWORD tt1)
{
DWORD tt2 = GetTickCount();
if (tt2 - tt1 > 100)//保證時間間隔
{
int i = rand() % NUM;
if ( jet[i].shoot == false && fire[i].show == false)//未發射狀態 空閑
{
//數據重新規划一下
jet[i].x = rand()% (1200 - 20);
jet[i].y = rand()% (100 + 600);
jet[i].hx = jet[i].x;
jet[i].hy = rand()% 400;
jet[i].shoot = true;//可以發色
putimage(jet[i].x, jet[i].y,&jet[i].img[jet[i].n], SRCINVERT);//這個SRCINVERT 很重要
}
tt1 = tt2;//重置一下時間
}
}
shoot
void shoot()
{
for (int i = 0;i < NUM;i++)
{
jet[i].t2 = GetTickCount();
if (jet[i].t2 - jet[i].t1 > jet[i].dt && jet[i].shoot == true)
{
putimage(jet[i].x, jet[i].y, &jet[i].img[jet[i].n],SRCINVERT);//n++
if (jet[i].y > jet[i].hy)
{
jet[i].n++;//一閃一閃
jet[i].y -= 5;//上升效果
}
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);
//把它的坐標傳遞個煙花
//這里很重要
fire[i].x = jet[i].hx;
fire[i].y = jet[i].hy;
fire[i].show = true;
//它的使命完成
jet[i].shoot = false;
}
jet[i].t1 = jet[i].t2;
}
}
}
源代碼
#include <stdio.h>
#include <graphics.h>
#include <windows.h>
#include <math.h>
#include <time.h>
#include<stdlib.h>
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib")
/*
1.把程序里面的元素用數據類型表示
//兩個坐標桌面窗口的坐標,圖片的坐標
單個煙花的大小240*240
2.初始化
*/
#define NUM 13
//煙花
struct Fire
{
int x, y;//煙花在窗口里面爆炸的坐標
int r;//煙花爆炸的當前半徑
int max_r;//煙花爆炸最大半徑
int cen_x, cen_y;//圖片坐標系中的中心位置
int xy[240][240];//240*240的像素數組,圖片的像素點保存在這里
bool show;//煙花是否開始爆炸 表示可不可以爆炸
bool draw;//開始繪制 表示是否爆炸
DWORD t1, t2, dt;//單位時間 幀數 爆炸的速度
//unsigned long = DWORD 4字節
}fire[NUM];
struct Jet
{
int x, y; //煙花彈的坐標 當前的坐標
int hx, hy;//煙花彈最高的坐標
bool shoot;//用一個數組來存放煙花彈的數據
//煙花彈的狀態 是否發射的狀態 true false
unsigned long t1, t2, dt; //速度 dt = t2 - t2
IMAGE img[2];//兩張圖片 一明一亮 img[0]表示暗色 img[1]亮色
//模擬一閃一閃的效果
byte n : 1;//typedef unsigned char byte;
//C語言結構體 位段 一個位 1/8字節 假設n++(01010010100)要么是零 要么是一
//img[n++]//一閃一閃
}jet[NUM];
void load()
{
mciSendString(L"open ./bk1.mp3 alias music", 0, 0, 0);
mciSendString(L"play music", 0, 0, 0);
srand(time(NULL));
IMAGE bigimg;//一整張圖片加載進來
loadimage(&bigimg, L"./shoot.jpg", 200, 50);
//把shoot.jpg保存在bigimg
SetWorkingImage(&bigimg);//十個煙花彈的圖片
for (int i = 0; i < NUM; i++)
{
int n = rand() % 5;
getimage(&jet[i].img[0],n*20,0,20,50);//隨機將前面的5張的任意一張圖片保存到。。
getimage(&jet[i].img[1],(n + 5)*20,0,20,50);
}
SetWorkingImage(NULL);//把工作區域回到窗口
IMAGE flower, stepimg;
loadimage(&flower, L"./flower.jpg", 3120, 240);//3120 240(高度) 13張
//工作區 默認工作區是窗口
//flower.jpg這個圖片中有13朵煙花
//getimage(&stepimg,);
for (int i = 0; i < NUM; i++)
{
SetWorkingImage(&flower);//在flower圖片上,裁圖片
getimage(&stepimg, i * 240, 0, 240, 240);//裁剪第一朵煙花 獲取
SetWorkingImage(&stepimg);//小圖片搞事情
for (int a = 0; a < 240; a++)
{
for (int b = 0; b < 240; b++)
{
fire[i].xy[a][b] = getpixel(a, b);//獲取小圖片上的每一個像素,存放在xy數組
}
}
}
SetWorkingImage(NULL);//工作區回到窗口
//return 0;
}
//初始化
void init(int i)
{
//初始化煙花彈的數據
jet[i].x = 0;
jet[i].y = 0;
jet[i].hx = 0;
jet[i].hy = 0;
jet[i].t1 = GetTickCount();
jet[i].t2 = 0;
jet[i].dt = rand()%10 + 1;//單位時間 1ms - 10ms
jet[i].n = 0;
jet[i].shoot = false;
fire[i].x = rand() % 1200;
fire[i].y = rand() % 800;
fire[i].r = 0;
fire[i].max_r = 120;//圖片的寬度240 240
fire[i].cen_x = 120;
fire[i].cen_y = 120;
//fire[i].xy[240][240];
fire[i].show = false; //***********
fire[i].draw = false;
fire[i].t1 = GetTickCount();//獲取系統的時間
//fire[i].t2;
fire[i].dt = 5;//爆炸的間隔 5毫秒
fire[i].xy[240][240];
}
//最核心的地方
void firedraw(DWORD* pmem)//顯存指針
{
//邏輯
for (int i = 0; i < NUM; i++)
{
fire[i].t2 = GetTickCount();//GetTickCount:獲取系統的時間
//“detat = t2 - t1”
if (fire[i].t2 - fire[i].t1 >= fire[i].dt && fire[i].show == true)
{
//r不能超過max_r
if (fire[i].r < fire[i].max_r)
{
fire[i].r++;//半徑不斷變大
fire[i].draw = true;//可以繪制
}
//萬一超過 爆炸結束
if (fire[i].r >= fire[i].max_r - 1)
{
fire[i].draw = false;
init(i);//爆炸結束給內存初始化,等待下一次爆炸
}
//重置時間 否則永遠t2-t1不為5(只有一次為5)
fire[i].t1 = fire[i].t2;
}
//如果是可以繪制的狀態
if (fire[i].draw = true)
{
// 繪制不是貼圖 而是輸出像素點
// 像素點 就是顏色(顏色:32位的數據)
// 00000000 00000000 00000000 00000000
// a透明度 red green blue
for (double a = 0; a <= 6.28; a += 0.01)
{
//x1 y1 圖片坐標系的坐標
int x1 = fire[i].cen_x + fire[i].r * cos(a);
int y1 = fire[i].cen_y - fire[i].r * sin(a);
//編程坐標系 與數學不一樣
//以半徑r得到圓弧上的每一個x和y
if (x1 > 0 && x1 < 240 && y1 > 0 && y1 < 240)
{
//分開32位數據 顏色 00000000 00000000 00000000
//點不能超出圖片的該范圍以外
int b = fire[i].xy[x1][y1] & 0xff;//blue
int g = (fire[i].xy[x1][y1] >> 8) & 0xff;//green
int r = (fire[i].xy[x1][y1] >> 16);//red
// 像素轉移 小圖片->工作區
int xx = fire[i].x + fire[i].r * cos(a);
int yy = fire[i].y - fire[i].r * sin(a);
//xx yy 窗口坐標系的坐標
if (r > 0x20 && g > 0x20 && b > 0x20 && xx > 0 && xx < 1200 && yy > 0 && yy < 800)
{
//如果這個點顏色太深(黑) 就不輸出
pmem[yy * 1200 + xx] = BGR(fire[i].xy[x1][y1]);
// 移像素點 圖片 =》 窗口
}
}
}
//628個圓弧上的點
fire[i].draw = false;
}
}
}
//選擇 出現 一點時間間隔
void choise(DWORD tt1)
{
DWORD tt2 = GetTickCount();
if (tt2 - tt1 > 100)//保證時間間隔
{
int i = rand() % NUM;
if ( jet[i].shoot == false && fire[i].show == false)//未發射狀態 空閑
{
//數據重新規划一下
jet[i].x = rand()% (1200 - 20);
jet[i].y = rand()% (100 + 600);
jet[i].hx = jet[i].x;
jet[i].hy = rand()% 400;
jet[i].shoot = true;
putimage(jet[i].x, jet[i].y,&jet[i].img[jet[i].n], SRCINVERT);
}
tt1 = tt2;
}
}
/*
-----------------------------------X
1
1
1
1
1
1
1
1
1
Y
*/
void shoot()
{
for (int i = 0;i < NUM;i++)
{
jet[i].t2 = GetTickCount();
if (jet[i].t2 - jet[i].t1 > jet[i].dt && jet[i].shoot == true)
{
putimage(jet[i].x, jet[i].y, &jet[i].img[jet[i].n],SRCINVERT);//n++
if (jet[i].y > jet[i].hy)
{
jet[i].n++;//一閃一閃
jet[i].y -= 5;//上升效果
}
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);
//把它的坐標傳遞個煙花
fire[i].x = jet[i].hx;
fire[i].y = jet[i].hy;
fire[i].show = true;
//它的使命完成
jet[i].shoot = false;
}
jet[i].t1 = jet[i].t2;
}
}
}
int main()
{
initgraph(1200, 800);
DWORD tt1 = GetTickCount();
DWORD* pmem = GetImageBuffer();//獲取顯存指針
load();
for (int i = 0; i < NUM; i++)
{
init(i);
}
BeginBatchDraw();
while (1)
{
Sleep(10);//擦除時間
for (int c = 0;c < 1000;c++)
{
for (int r = 0; r < 2; r++)//保證每次擦除兩千的像素點
{
//隨機找兩千個窗口的點
int px1 = rand() % 1200;
int py1 = rand() % 800;
if (py1 < 799)//防止越界
{
pmem[py1 * 1200 + px1] = pmem[py1*1200 + px1 + 1] = BLACK;
}
}
}
choise(tt1);
shoot();
firedraw(pmem);
FlushBatchDraw();
}
getchar();
return 0;
}
一些數據類型
typedef unsigned long DWORD;//4字節typedef int BOOL;typedef unsigned char BYTE;typedef unsigned short WORD;
參考資源
[【C/C++】程序員表白神器!c語言實現浪漫煙花表白,撩妹新技能你get了嘛?_嗶哩嗶哩_bilibili]
(https://www.bilibili.com/video/BV1Jy4y1B7YV?spm_id_from=333.999.0.0)
[【C/C++】C語言實現浪漫煙花表白程序,表白必備的滿天花海!_嗶哩嗶哩_bilibili](https://www.bilibili.com/video/BV1Zy4y1r7dL/)
</mmsystem.h></stdlib.h></time.h></math.h></windows.h></graphics.h></stdio.h></mmsystem.h></stdlib.h></time.h></math.h></windows.h></graphics.h></stdio.h>
代碼下載鏈接:
https://pan.baidu.com/s/1yGbsojDQii3kLRYjFJUhdw
提取碼:m7oa