系列文章
目录
写在前面
C/C++语言实现新春烟花动画的完整代码。
技术需求
1. EasyX 图形库
- EasyX 是一个基于 Windows 的 C++ 图形库,适用于学生和初学者,能够轻松实现图形绘制、图像处理、动画制作等。代码中使用了
graphics.h
和IMAGE
类型来进行图形显示,加载和操作图像。 - 使用
loadimage()
和putimage()
等函数加载和绘制图像,能方便地进行图形操作。
2. 多媒体控制接口 (MCI)
- MCI (Media Control Interface) 是 Windows 提供的一种多媒体设备控制接口,能够对音频、视频等多媒体文件进行播放和控制。在这段代码中,通过
mciSendString()
函数来播放声音文件,如烟花发射和爆炸的音效。通过这种方式,程序能够与系统的音频设备进行交互。
示例代码:
mciSendString(c1, 0, 0, 0); // 播放音效
mciSendString(c2, 0, 0, 0); // 播放音效
mciSendString(c3, 0, 0, 0); // 关闭音效
3. 图形学中的动画与物理模拟
- 烟花模拟:通过控制烟花弹和烟花爆炸的时间,使用坐标计算实现烟花的发射、爆炸和绽放效果。对于每个烟花弹,程序都计算了其发射位置、最高点、爆炸时的半径变化等物理特性,并通过循环更新这些属性来模拟烟花的运动轨迹和爆炸过程。
- 分阶段渲染:烟花的每个阶段(如发射、上升、爆炸)都分开处理。通过设置时间间隔来控制不同阶段的图像绘制和爆炸效果,利用
putimage()
绘制图像,模拟烟花从发射到爆炸的过程。
4. 时间管理与定时
- 系统时间管理:通过
timeGetTime()
和GetTickCount()
获取当前时间,用来计算烟花的不同阶段,以及控制烟花发射的间隔。时间差t2 - t1
控制了烟花发射和绽放的速度,以及音效的播放时机。 - 定时器:定时器(通过
Sleep()
和timeGetTime()
)确保程序的每一帧都有适当的时间间隔,从而产生平滑的动画效果。
5. 随机数与图像处理
- 随机数生成:使用
rand()
函数生成随机数,用于决定烟花的发射位置、颜色、发射速度等。 - 图像拆分与存储:通过
getimage()
获取烟花图像的部分内容,并存储在Img
数组中。这是为了从一张大图中获取多个小的烟花图案,模拟不同烟花的效果。
6. 位段 (Bit-Field)
- 位段是 C 语言中的一种结构体成员类型,允许使用部分位来存储数据。在代码中,
byte n : 1;
使用位段来表示烟花的某个状态,节省内存空间。位段在处理小范围的状态标志时非常有用。
7. 内存操作与图像显示
- 显存操作:通过
GetImageBuffer()
获取当前窗口的显存指针,直接操作每个像素点。通过这种方式,程序能够更细粒度地控制图形绘制,例如绘制烟花爆炸时的像素点,模拟烟花的扩散效果。 - 批量绘制:通过
BeginBatchDraw()
和FlushBatchDraw()
函数,程序可以将多个图形绘制操作批量执行,从而提高绘图效率并确保绘制过程中的流畅性。
8. 位图绘制与颜色控制
- 位图绘制:程序使用了
IMAGE
类型存储和操作位图,进行烟花图像的加载和绘制。通过getpixel()
和setpixel()
等图像处理函数,程序能够精确地获取和修改图像中的像素值。 - 颜色控制:通过位运算来解析和操作图像中的颜色,控制烟花的爆炸效果。具体来说,程序通过分离出 RGB 三个颜色通道的值,避免在绘制时显示过于暗淡的颜色。
9. 数学模型与物理运动
- 烟花轨迹计算:程序通过数学函数(如
cos()
和sin()
)来计算烟花在二维空间中的运动轨迹。烟花的轨迹依赖于其发射角度和速度,使用了极坐标变换公式来确定烟花爆炸时的像素位置。 - 爆炸效果:烟花爆炸模拟采用了半径逐渐增大的方式,通过不断增加爆炸半径(
fire[i].r
)来实现绽放效果。此外,通过控制时间间隔,爆炸的速度和图像渲染的速度也可以动态调整。
10. 声音效果与音频播放
- 音频播放:通过 MCI 接口播放与烟花相关的音频文件。每个烟花弹发射时播放一个音效,烟花爆炸时播放另一个音效。音效通过
mciSendString()
控制,播放时机与烟花的动作同步。
环境搭建
环境:C/C++
软件:Visual Studio 2022
安装教程:C语言环境搭建教程(Visual Studio)_visual studio 2023-CSDN博客
1. EasyX是什么
EasyX是一个专为C++初学者和爱好者设计的图形库。该库以简洁易用、功能实用为宗旨,通过封装Windows GDI接口,极大降低了C++编程中图形界面设计的复杂度,使得用户能够快速上手并实现各类图形图像处理任务。
EasyX提供了一系列丰富的API函数,涵盖了绘制基本图形(如线段、圆形、矩形等)、填充图形、显示文本、加载与保存图片、颜色设置以及鼠标键盘事件处理等功能。通过简单的函数调用,开发者可以高效地进行2D图形绘制和交互式程序设计。
总的来说,EasyX以其友好的学习曲线和高效的图形处理能力,极大地激发了C++初学者对计算机图形学的兴趣,是广大编程入门者和教育领域广泛采用的一款图形库工具。
2. 下载安装EasyX
1.进入EasyX官网,点击下载按钮开始下载
2.下载完成后进入下载目录,双击.exe文件无脑安装即可
接下来,让我们一步步来实现这个跳动的爱心。首先,我们需要通过一系列的数学公式来定义爱心的形状,比如使用参数方程来描述爱心的轮廓。然后,我们可以使用C++的图形库来绘制出这个形状。当绘制好爱心后,就要想办法让它跳动起来了,这里我们可以使用C++的定时器功能,设置一个合适的时间间隔,让爱心在每个时间间隔内改变位置或大小,从而产生跳动的效果。
完整代码
#include<stdio.h>
#include<graphics.h>//之前要先安装EasyX,不然导入这个包会报错
#include<conio.h>
#include<math.h>
#include<time.h>//随机数
#include<mmsystem.h>//多媒体设备接口的头文件
#pragma comment(lib,"winmm.lib")//Media control interface
//这条指令和你在工程中添加"winmm.lib"的功能一样,告诉编译器要导入winmm库
#define NUM 13 //在整个屏幕内,最多出现13个烟雾弹
void Welcome();//进入程序运行界面
void init(int i);//初始化每个小烟花的属性值:比如x,y坐标值,是否发射等
void Load();//加载图片
void Choose(DWORD &t1);//选择要发送的烟花弹
void shoot();//发射烟花弹
void Show(DWORD*); // 绽放烟花
void Style(DWORD& st1);
void write(DWORD&);
void clear(DWORD*);
struct Jet
{
int x, y;//烟花弹的坐标
int hx, hy;//最高点的坐标
int height;//高:y-hy
bool isshot;//是否发射
DWORD t1, t2, dt;//DWORD是无符号的,相当于unsigned long ,
//用t1,t2,dt控制速度
IMAGE Img[2];//存放图片的数组
byte n : 1;//下标 位段:/:指一个位来存放n
//位段:(bit-field)是以位为单位来定义结构体(或联合体)中的成员变量所占的空间
}jet[NUM];//定义结构体数组
struct Fire
{
int r;//当前爆炸半径
int max_r;//爆炸中心距离边缘最大半径
int x, y;//爆炸中心在窗口的坐标
int cen_x,cen_y;//爆炸中心相对图片左上角的坐标
int width;//图片的宽度
int height;//图片的高度
int xy[240][240];//储存图片像素点
bool isshow;//是否显示
bool isdraw;//是否画出
DWORD t1, t2, dt;//绽放速度
}fire[NUM];
int main()
{
initgraph(1000, 600);//初始化一个窗口
srand((unsigned int)time(NULL)); //为了得到不一样的随机数
Load();
DWORD t1 = timeGetTime(); // 筛选烟花计时
DWORD st1 = timeGetTime(); // 播放花样计时
DWORD tm = timeGetTime(); //字体播放计时
DWORD tn;
DWORD TP;
DWORD TC;
DWORD* pMem = GetImageBuffer(); // 获取窗口显存指针,对每个像素点进行操作
for (int i = 0; i < NUM; i++)
{
init(i);//初始化每个小烟花的相关数据
}
Load(); // 将烟花图片信息加载进相应结构中
IMAGE img;
loadimage(&img, "./file/image.jpg", 150, 266);
BeginBatchDraw(); // 开始批量绘图
while (!_kbhit())
{
Sleep(10);
clear(pMem);
TC = timeGetTime();
if (TC - t1 > 0)
{
putimage(850, 334, &img);
}
Choose(t1);
shoot();
Show(pMem);
Style(st1);
FlushBatchDraw(); // 显示前面的所有绘图操作
}
getchar();
return 0;
}
void Load()
{
/****储存烟花的像素点图片****/
IMAGE fImg, gImg;
loadimage(&fImg,"./file/flower.jpg",3120,240);
for (int i = 0; i < NUM; i++)
{
SetWorkingImage(&fImg);
getimage(&gImg, i * 240, 0, 240, 240);
SetWorkingImage(&gImg);
for (int a = 0; a < 240; a++)
{
for (int b = 0; b < 240; b++)
{
fire[i].xy[a][b] = getpixel(a, b);//获取点的颜色
}
}
}
IMAGE jetImg;//存放图片
loadimage(&jetImg,"./file/shoot.jpg",200,50);//加载图片,将图片保存到jetImage对象里面去
//[2]拆分图片
for (int i = 0; i < NUM; i++)
{
SetWorkingImage(&jetImg);//设置当前的设备,当前设备设为jetImg
int n = rand() % 5;
getimage(&jet[i].Img[0],n*20,0,20,50);//得到的是暗色图片
//getimage()从当前设备获取图片
//getimage()参数1指将得到的拆分出的图片存放的位置
//参数2,3指得到的图片在原图片左上角的位置
//参数4,5指的是新的到的图片的宽和高 下面类推
getimage(&jet[i].Img[1], (n+5) * 20, 0, 20, 50);//得到的是明亮图片
}
SetWorkingImage(NULL);//重将当前设备设置为窗口
}//设置烟花小图片
……
代码分析
这段代码实现了一个烟花模拟效果,利用 C 语言和 EasyX 图形库制作了一个基于窗口的烟花展示程序。通过使用图像的加载、位置控制、动画效果、随机数生成、声音播放等技术,模拟了烟花从发射到爆炸的全过程。下面详细分析这段代码的工作原理、结构及实现的各个方面。
1. 代码整体结构与框架
这段代码主要包含了几个部分:初始化、加载图像、烟花弹的发射、烟花的爆炸和显示、花样的显示、以及清除显示内容等功能。其主要结构由以下几个函数组成:
- 主函数
main
:负责窗口的初始化,图像的加载,烟花发射、爆炸、显示的循环等。 Load
函数:加载烟花的图像和烟花弹的图像。init
函数:初始化每个烟花和烟花弹的属性。Choose
函数:控制烟花弹的发射,每100毫秒产生一个新的烟花弹。shoot
函数:实现烟花弹的发射过程,即烟花从发射点向上升起的动画效果。Show
函数:控制烟花的爆炸效果,逐步增加爆炸半径,模拟烟花的绽放过程。Style
函数:播放花样的烟花,设置烟花弹的规律分布,并在适当时机播放爆炸效果。clear
函数:清理屏幕上的像素,擦除已经绘制的烟花效果。
2. 数据结构
Jet
结构体
Jet
结构体表示一个烟花弹(或烟花飞行器),包含了烟花弹的位置信息、状态、时间戳等属性:
- 坐标:包括烟花弹的当前位置
(x, y)
和最高点的坐标(hx, hy)
,用来模拟烟花的飞行过程。 - 高度:
height
表示烟花弹飞行的最大高度,即烟花弹的起始位置和目标位置之间的距离。 - 发射状态:
isshot
用来标记烟花弹是否已经发射。 - 时间管理:
t1
、t2
、dt
分别用来管理烟花弹的发射时刻、当前时间以及速度控制。 - 图像:
Img
存储两张烟花弹的图像(暗色和明亮的烟花图像)。 - 位段:
n
用来表示烟花弹的显示状态。
Fire
结构体
Fire
结构体表示烟花的爆炸效果,定义了烟花的爆炸半径、爆炸中心坐标、图片尺寸等属性:
- 爆炸信息:包括爆炸的当前半径
r
、最大半径max_r
和爆炸中心的坐标(x, y)
。 - 图片数据:储存烟花爆炸时的图像像素点数据
xy
,以及图片的宽高信息。 - 显示控制:
isshow
和isdraw
用来控制烟花是否显示和是否绘制。 - 时间管理:与烟花弹类似,使用
t1
、t2
、dt
来管理绽放过程中的时间间隔和速度。
3. 关键函数分析
Load
函数
Load
函数负责加载烟花的图片资源,并将其分解存储为结构体数据。首先加载了一个包含多个烟花图片的图像(如 flower.jpg
)并将其切割成多个小图像存储到 fire
数组中。每个烟花爆炸的效果会从这些图像中获取像素数据,以便后续显示。接着加载烟花弹的图像 shoot.jpg
,并将其分解为两张图片(暗色和明亮)分别存储在 jet
数组中。
init
函数
init
函数对每个烟花和烟花弹进行初始化,设置它们的初始位置、最大爆炸半径、爆炸中心坐标以及时间戳等。烟花的初始位置和爆炸半径是预先定义的,通过这些参数可以控制烟花爆炸的大小和分布。
Choose
函数
Choose
函数用于选择一个烟花弹进行发射。每100毫秒产生一个新的烟花弹,首先检查当前的烟花弹是否已经发射过(isshot
)以及该烟花的爆炸是否已经开始(isshow
)。如果烟花弹符合发射条件(未发射且未爆炸),则随机生成其初始坐标,并将其状态设置为“已发射” (isshot = true
)。然后使用 mciSendString
播放发射的声音。
shoot
函数
shoot
函数实现烟花弹的飞行过程。每当烟花弹的飞行时间超过指定的时间间隔 (dt
),烟花弹的图像会向上移动,并逐渐改变为明亮的状态。当烟花弹达到设定的最大高度时,触发烟花爆炸(即切换到爆炸状态)。在这一过程中,putimage
函数用于绘制烟花弹的图像。
Show
函数
Show
函数负责控制烟花的爆炸效果。它通过增加爆炸半径来模拟烟花的绽放,使用了变速效果来增加烟花的真实感。每次更新时,根据当前半径和爆炸速度来绘制烟花的爆炸效果。使用三角函数(cos
和 sin
)来计算烟花的分布点,并将烟花的像素点显示到屏幕上。
Style
函数
Style
函数是一个特殊的烟花效果,用来显示更具规律的烟花形状。例如心形烟花的轨迹。函数通过修改烟花弹的起始位置,使其按照一定的模式(如心形)发射,同时播放发射的声音和爆炸效果。
clear
函数
clear
函数负责清除屏幕上绘制的烟花效果。它通过随机选择像素点并将其颜色重置为黑色来擦除已经显示的烟花效果,避免屏幕上留下遗留的图像,从而保持动画的流畅性。
4. 动画与性能优化
- 多线程与批量绘制:代码中使用了
BeginBatchDraw
和FlushBatchDraw
来实现批量绘图,减少了单次绘制操作的开销,提高了动画的流畅度。 - 时间管理与控制:通过
timeGetTime()
和GetTickCount()
来精确控制烟花弹的发射时间、飞行速度和爆炸时刻,确保烟花的动作具有一致性和节奏感。
5. 总结
这段代码通过利用 EasyX 图形库,结合了随机数、图像处理、声音播放等技术,模拟了烟花的发射、飞行和爆炸过程。它的结构清晰,功能分明,通过合理的时间控制和图像渲染,实现了较为生动的烟花效果。通过进一步优化,比如增加更多的烟花效果、改进图像加载与渲染性能等,可以让这个程序变得更加完善和有趣。
写在后面
我是一只有趣的兔子,感谢你的喜欢!