效果展示


动态图

总体框架

/*****************************************
* 项目名称:浪漫烟花
* 项目描述:贴图
* 项目环境:vs2019
* 生成日期:2020-9-7
* 作者所属:追梦
*****************************************/
#include<graphics.h>
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#define NUM 10 //烟花弹个数,烟花
//烟花弹
struct jet
{
int x, y; //烟花弹坐标
int hx, hy; //烟花弹最高点坐标
bool shoot; //烟花弹是否处于发射状态
DWORD t1, t2, dt; //发射时间 引爆时间 间隔时间
IMAGE img[2]; //2张图片 一明一暗 01下标
byte n : 1; //C结构体 位段 //n 变量 1个位 0,1 n++ 0,1,0,1
}jet[NUM]; //烟花弹个数 //烟花
struct Fire
{
int x, y; //烟花的坐标
int r; //烟花的半径
int max_r; //烟花的最大半径
int cen_x, cen_y; //中心距左上角的距离
int width, height; //长宽
int xy[240][240]; //重要,像素,矩阵
bool draw; //画出
bool show; //显示
DWORD t1, t2, dt; //发射时间 引爆时间 间隔时间
}fire[NUM];
//初始化函数
void FireInit()
{}
//加载资源
void Load()
{}
//选择烟花弹
void ChoiceJet()
{}
//判断发射
void Shoot()
{}
//显示烟花
void ShowFire()
{}
//菜单界面
void welcome()
{}
//主函数
int main()
{
//初始界面(1000,600)
initgraph(1000, 600);
welcome();
Load();
while (1)
{
ChoiceJet();
Shoot();
ShowFire();
}
system("pause");
return 0;
}

逻辑关系

首先绘制菜单界面显示文字。烟花有上升阶段和爆炸阶段,定义烟花和烟花弹结构体。
烟花:坐标位置,爆炸的半径大小,最大半径,中心距左上角的距离,长宽,像素,时间等。
烟花弹:坐标位置,最高点,是否发射,时间,个数等。
初始化数据。加载资源贴图。随机发射数目随机,

源代码

/*****************************************
* 项目名称: 浪漫烟花
* 项目描述:贴图
* 项目环境:vs2019
* 生成日期:2020-9-7
* 作者所属:追梦
*****************************************/
#include<graphics.h>
#include<time.h>
#include<stdlib.h>
#include<math.h>
#include<windows.h>
#pragma comment(lib,"winmm.lib")
#define NUM 10 //烟花弹个数,烟花
#define PI 3.1415925
//烟花弹
struct jet
{
int x, y; //烟花弹坐标
int hx, hy; //烟花弹最高点坐标
bool shoot; //烟花弹是否处于发射状态
DWORD t1, t2, dt; //发射时间 引爆时间 间隔时间
IMAGE img[2]; //2张图片 一明一暗 01下标
byte n : 1; //C结构体 位段 //n 变量 1个位 0,1 n++ 0,1,0,1
}jet[NUM]; //烟花弹个数
//烟花
struct Fire
{
int x, y; //烟花的坐标
int r; //烟花的半径
int max_r; //烟花的最大半径
int cen_x, cen_y; //中心距左上角的距离
int width, height; //长宽
int xy[240][240]; //重要,像素,矩阵
bool draw; //画出
bool show; //显示
DWORD t1, t2, dt; //发射时间 引爆时间 间隔时间
}fire[NUM];

初始化函数

void FireInit(int i)
{
//初始化烟花弹
jet[i].t1 = GetTickCount(); //GetTickCount()返回从操作系统启动到当前所经过的毫秒数。使用前包含windows.h。
jet[i].shoot = false; //未发射
jet[i].dt = 10; //上升时间
jet[i].n = 0; //初始化烟花
fire[i].show = false; //未引爆
fire[i].r = 0;
fire[i].dt = 5; //上升时间
fire[i].t1 = GetTickCount();
fire[i].max_r = rand() % 50 + 100; //100-149
fire[i].cen_x = rand() % 30 + 80; //中心距左上角的距离
fire[i].cen_y = rand() % 30 + 80; //
fire[i].width = 240; //宽
fire[i].height = 240; //长
}

加载

void Load()
{
//加载烟花弹
IMAGE jetimg;
loadimage(&jetimg, L"./fire/shoot.jpg", 200, 50);
SetWorkingImage(&jetimg);
for (int i = 0; i < NUM; i++)
{
int n = rand() % 5; //01234
getimage(&jet[i].img[0], n * 20, 0, 20, 50);
getimage(&jet[i].img[1], (n + 5) * 20, 0, 20, 50);
}
SetWorkingImage(NULL);
//加载烟花
IMAGE fireimage, Fireimage;
loadimage(&Fireimage, L"./fire/flower.jpg", 3120, 240);
for (int i = 0; i < NUM; i++)
{
SetWorkingImage(&Fireimage);
getimage(&fireimage, i * 240, 0, 240, 240);
SetWorkingImage(&fireimage);
for (int a = 0; a < 240; a++)
{
for (int b = 0; b < 240; b++)
{
fire[i].xy[a][b] = getpixel(a, b);
}
}
}
SetWorkingImage(NULL);
}

选择烟花弹

void ChoiceJet(DWORD& t1)
{
DWORD t2 = GetTickCount();
if (t2 - t1 > 100) //烟花弹出现的时间间隔100ms
{
//烟花弹个数
int i = rand() % 10;
//不处于发射状态
if (jet[i].shoot == false && fire[i].show == false)
{
//烟花弹
jet[i].x = rand() % 1000;
jet[i].y = rand() % 100 + 450; //450-549
jet[i].hx = jet[i].x;
jet[i].hy = rand() % 300; //0-299
jet[i].shoot = true; //发射状态 putimage(jet[i].x, jet[i].y, &jet[i].img[jet[i].n], SRCINVERT);
}
t1 = t2;
}
}

判断发射

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);
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);
jet[i].shoot = false;
//达到最大高度,接下来交给烟花
//重新发射
fire[i].x = jet[i].hx;
fire[i].y = jet[i].hy;
fire[i].show = true;
}
}
jet[i].t1 = jet[i].t2;
}
}
//显示烟花
void ShowFire(DWORD* pMem)
{
int drt[16] = { 5, 5, 5, 5, 5, 10, 25, 25, 25, 25, 55, 55, 55, 55, 55, 65 }; for (int i = 0; i < NUM; i++)
{
fire[i].t2 = GetTickCount();
if (fire[i].t2 - fire[i].t1 >= fire[i].dt && fire[i].show == true)
{
if (fire[i].r < fire[i].max_r)
{
fire[i].r++;
fire[i].dt = drt[fire[i].r / 10];
fire[i].draw = true;
}
if (fire[i].r >= fire[i].max_r - 1)
{
fire[i].draw = false;
FireInit(i);
}
fire[i].t1 = fire[i].t2;
// 如果该号炮花可爆炸,根据当前爆炸半径画烟花,颜色值接近黑色的不输出。
if (fire[i].draw)
{
for (double a = 0; a <= 6.28; a += 0.01)
{
int x1 = (int)(fire[i].cen_x + fire[i].r * cos(a));
int y1 = (int)(fire[i].cen_y - fire[i].r * sin(a));
if (x1 > 0 && x1 < fire[i].width && y1>0 && y1 < fire[i].height)
{
int b = fire[i].xy[x1][y1] & 0xff;
int g = (fire[i].xy[x1][y1] >> 8) & 0xff;
int r = (fire[i].xy[x1][y1] >> 16);
// 烟花像素点在窗口上的坐标
int xx = (int)(fire[i].x + fire[i].r * cos(a));
int yy = (int)(fire[i].y - fire[i].r * sin(a));
//较暗的像素点不输出、防止越界
if (r > 0x20 && g > 0x20 && b > 0x20 && xx > 0 && xx < 1000 && yy >0 && yy < 600)
{
pMem[yy * 1000 + xx] = BGR(fire[i].xy[x1][y1]);
}
fire[i].draw = false;
}
}
}
}
}
}

菜单界面

void welcome()
{
setcolor(YELLOW);
for (int i = 0; i < 50; i++)
{
int x = 600 + int(180 * sin(PI * 2 * i / 60));
int y = 200 + int(180 * cos(PI * 2 * i / 60));
cleardevice();
settextstyle(i, 0, L"楷体");
outtextxy(x - 80, y, L"一首小辛运送上");
outtextxy(x - 10, y + 100, L"献给所有编程大佬");
Sleep(65);
}
Sleep(130);
cleardevice();
settextstyle(25, 0, L"楷体");
outtextxy(400, 200, L"原来你是我最想留住的幸运");
outtextxy(400, 250, L"原来我们和爱情曾经靠得那么近");
outtextxy(400, 300, L"那为我对抗世界的决定");
outtextxy(400, 350, L"那陪我淋的雨");
outtextxy(400, 400, L"一幕幕都是你");
outtextxy(400, 450, L"-尘不染的真心");
outtextxy(650, 500, L"小幸运");
Sleep(2000);
cleardevice();
}

主函数

int main()
{
//初始界面(1000,600)
initgraph(1000, 600);
//初始化种子
srand((unsigned int)time(NULL));
//音乐 爱的翅膀
mciSendString(L"open ./fire/bk1.mp3 alias music", 0, 0, 0); //send(发送) string(字符串)
mciSendString(L"play music", 0, 0, 0);
//其它音乐类型 wav PlaySound()
//0,0,0 音乐播放器时:播放设备,快进设备 快退 暂停
welcome();
DWORD t1 = GetTickCount();
DWORD* pMem = GetImageBuffer();
for (int i = 0; i < NUM; i++)
{
FireInit(i);
}
Load();
BeginBatchDraw();
while (1)
{
// 随机选择像素点擦除
for (int clr = 0; clr < 200; clr++)
{
int px1 = rand() % 1000;
int py1 = rand() % 600; // 防止越界
if (py1 < 599)
{
//对显存赋值擦除像素点
pMem[py1 * 1000 + px1] = pMem[py1 * 1000 + px1 + 1] = BLACK;
}
}
ChoiceJet(t1);
Shoot();
ShowFire(pMem);
FlushBatchDraw();
}
system("pause");
return 0;
}

素材

两张图片放在fire文件夹下面。音乐的话自己随便找一首放进去,就可以播放了。

总结

需要安装图形库,以及了解相关的知识,素材路径的话也要写对,不然是没有效果的。

C语言实现面向对象编程
http://www.makeru.com.cn/live/1392_1051.html?s=45051
结构体普及与应用
http://www.makeru.com.cn/live/5413_1909.html?s=45051
C语言玩转链表
http://www.makeru.com.cn/live/1392_338.html?s=45051
数据类型、常量、变量及运算符
http://www.makeru.com.cn/video/1877.html?s=45051
C语言与数据结构的经典实战案例
http://www.makeru.com.cn/live/5413_2014.html?s=45051
Linux C语言高级开发
http://www.makeru.com.cn/course/details/2478?s=45051
必备Linux命令和C语言基础
http://www.makeru.com.cn/video/1862.html?s=45051
九天学会linuxC语言
http://www.makeru.com.cn/course/1861.html?s=45051

C语言教你写个‘浪漫烟花‘---特别漂亮的更多相关文章

  1. 《分布式对象存储》作者手把手教你写 GO 语言单元测试!

    第一部分:如何写Go语言单元测试 Go语言内建了单元测试(Unit Test)框架.这是为了从语言层面规范写UT的方式. Go语言的命名规则会将以_test.go结尾的go文件视作单元测试代码. 当我 ...

  2. 手把手教你写Sublime中的Snippet

    手把手教你写Sublime中的Snippet Sublime Text号称最性感的编辑器, 并且越来越多人使用, 美观, 高效 关于如何使用Sublime text可以参考我的另一篇文章, 相信你会喜 ...

  3. 只有20行Javascript代码!手把手教你写一个页面模板引擎

    http://www.toobug.net/article/how_to_design_front_end_template_engine.html http://barretlee.com/webs ...

  4. 网络编程懒人入门(八):手把手教你写基于TCP的Socket长连接

    本文原作者:“水晶虾饺”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.引言 好多小白初次接触即时通讯(比如:IM或者消息推送应用)时,总是不 ...

  5. 手把手教你写基于C++ Winsock的图片下载的网络爬虫

    手把手教你写基于C++ Winsock的图片下载的网络爬虫 先来说一下主要的技术点: 1. 输入起始网址,使用ssacnf函数解析出主机号和路径(仅处理http协议网址) 2. 使用socket套接字 ...

  6. 看过《大湿教我写.net通用权限框架(1)之菜单导航篇》之后发生的事(续)——主界面

    引言 在UML系列学习中的小插曲:看过<大湿教我写.net通用权限框架(1)之菜单导航篇>之后发生的事 在上篇中只拿登录界面练练手,不把主界面抠出来,实在难受,严重的强迫症啊.之前一直在总 ...

  7. 手把手教你写LKM rookit! 之 第一个lkm程序及模块隐藏(一)

    唉,一开始在纠结起个什么名字,感觉名字常常的很装逼,于是起了个这<手把手教你写LKM rookit> 我觉得: 你们觉得:...... 开始之前,我们先来理解一句话:一切的操作都是系统调用 ...

  8. 手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取

    版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 看完两篇,相信大家已经从开始的 ...

  9. 手把手教你写电商爬虫-第四课 淘宝网商品爬虫自动JS渲染

    版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 手把手教你写电商爬虫-第三课 ...

随机推荐

  1. 动态路由——OSPF

    目录: 一. OSPF路由协议概述     1,OSPF协议    2,内部网关协议和外部网关协议   3,OSPF的工作过程 二.OSPF基本概念   1,OSPF区域   2,区域ID   3,R ...

  2. 使用Redis Stream来做消息队列和在Asp.Net Core中的实现

    写在前面 我一直以来使用redis的时候,很多低烈度需求(并发要求不是很高)需要用到消息队列的时候,在项目本身已经使用了Redis的情况下都想直接用Redis来做消息队列,而不想引入新的服务,kafk ...

  3. ysoserial payloads/JRMPClient

    ysoserial payloads/JRMPClient 环境:JDK8u102 payloads/JRMPClient可以配合exploit/JRMPListener模块来使用 1.在自己服务器上 ...

  4. Linux之crontab命令

    简介 通过crontab 命令,我们可以在固定的间隔时间执行指定的系统指令或 shell 脚本.时间间隔的单位可以 是分钟.小时.日.月.周及以上的任意组合.这个命令非常适合周期性的日志分析或数据备份 ...

  5. 从输入 URL 到展现页面的全过程

    总体分为以下几个过程 DNS解析 TCP连接 发送HTTP请求 服务器处理请求并返回HTTP报文 浏览器解析渲染页面 连接结束 DNS解析 域名到ip地址转换 TCP连接 HTTP连接是基于TCP连接 ...

  6. Dockerfile 自动制作 Docker 镜像(三)—— 镜像的分层与 Dockerfile 的优化

    Dockerfile 自动制作 Docker 镜像(三)-- 镜像的分层与 Dockerfile 的优化 前言 a. 本文主要为 Docker的视频教程 笔记. b. 环境为 CentOS 7.0 云 ...

  7. js设计模式之发布订阅模式

    1. 定义 发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知. 订阅者(Subscriber)把自己想订阅的事件注册(Subscri ...

  8. jvm运行过程

    ------------恢复内容开始------------ 把文件编译成字节码文件的叫编译器的前端, 线程共享的方法去和堆,非线程共享的:java虚拟机栈,本地方法栈,还有程序计数器 都是每个线程独 ...

  9. 让PHP能够调用C的函数-FFI扩展

    在大型公司中,一般会有很我编程语言的配合.比如说让 Java 来做微服务层,用 C++ 来进行底层运算,用 PHP 来做中间层,最后使用 JS 展现效果.这些语言间的配合大部分都是通过 RPC 来完成 ...

  10. tp5 引入 没有命名空间的类库的方法(以微信支付SDK为例)

    use think\Loader; Loader::import('Wxpay.WxPay',EXTEND_PATH,'.Api.php'); 注意扩展名的点"."不能省略 使用之 ...