• 手把手教你用C语言制作七夕流星雨---优雅永不过时(详细教程)


    在这里插入图片描述

    前言

    今天是星期五,八月五号,因为文章太长,写了好久,拖到今天才发。文章很详细,不过也要有一点C语言基础,代码都是用C语言写的。
    我是一个平平无奇的普通人,也有自己喜欢的人,喜欢的事情。不知道在昨天有多少人在思考自己,亦或是羡慕他人。
    本人并没有体会过爱情的美好,也没有任何经验。但有喜欢的人,和说不出的话、不能做的事。这也许,就是遗憾吧。但不是每一段故事,都要有美好的结局啊。诚挚地去爱过,不管结果如何,你都是那个最棒地自己!
    在这个特殊的日子,不知是表示甜,还是表示苦,人生有很多遗憾和无奈,也有很多快乐和希望。这里祝福所有的有情人终成眷属,在纷纷扰扰的世间,遇见有缘人,恩爱一生,白头偕老!不必羡慕他人,不必懊恼自己,我觉得爱情,是世间最美好、最奇妙的感情,你我一定会遇到属于自己的美好爱情!
    这次要做的效果,很常见,但我并不想直接把效果和代码一放,无事走人。
    大家在忙碌之中,看我的文章,我也想让大家学点东西,有点收获。
    如果你想直接拿走代码,可以翻到最后;如果想看看如何做的,可以一步一步跟着学,一定有你不会的东西。
    好啦,看一下这次的效果
    请添加图片描述

    一、准备工作

    (1)安装EasyX图形库

    1)原理

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GJYncxBY-1659659008995)(D:\Typora图片\image-20220803223829422.png)]

    2)安装

    直接百度搜索EasyX

    点击第一个。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-88pixatk-1659659008995)(D:\Typora图片\image-20220803223250806.png)]

    然后点击下载EasyX

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rx2AZTnI-1659659008997)(D:\Typora图片\image-20220803223344868.png)]

    双击文件。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iC22LSAX-1659659008997)(D:\Typora图片\image-20220803223558036.png)]

    “下一步”。

    然后会自动查找到你自己电脑里面安装的编译器。这里就以VS2019为例,点击“安装”即可。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kbXIgQxX-1659659008998)(D:\Typora图片\image-20220803224049343.png)]

    然后“关闭”。

    3)帮助文档

    帮助文档链接:https://docs.easyx.cn/zh-cn/intro

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KZlxCp28-1659659008998)(D:\Typora图片\image-20220803222735932.png)]

    下载好安装包,解压即可。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mJ88I6lx-1659659008998)(D:\Typora图片\image-20220803222945484.png)]

    然后双击这个文件就可以打开了。

    4)本质

    本质是,查找Vs的安装目录,并将相关文件分别拷贝至lib目录和include目录。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HFbME8Le-1659659008998)(D:\Typora图片\image-20220803224308897.png)]

    安装成功后,包含头文件graphics.h即可开始快乐学习了。

    (2)新建项目

    1)建立空项目

    注意:本次项目使用的编译器是VS2019。

    这里选择建立“空项目”。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rs6oYlbk-1659659008998)(D:\Typora图片\image-20220803221036657.png)]

    2)添加新建项

    点击“源文件”,右键“添加”–>“新建项”。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-swIoNE2k-1659659008999)(D:\Typora图片\image-20220803221223966.png)]

    3)添加文件

    将后缀名写为.cpp

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kfRVf77W-1659659009000)(D:\Typora图片\image-20220803221357196.png)]

    二、文字效果

    (1)主函数

    创建完文件,开始写主函数。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sMKGve1y-1659659009000)(D:\Typora图片\image-20220803221746241.png)]

    运行一下,看看有没有什么错误。

    好啦,出现下列提示就是创建没有问题,可以进行接下来的操作。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DPexzMMP-1659659009004)(D:\Typora图片\image-20220803221851695.png)]

    (2)包含图形库

    接下来,我们要包含EasyX图形库。

    直接在主函数之前写上:

    #include 
    
    • 1

    easyx是第三方图形库。

    在C语言中,除了stdio这种标准输入输出的,以及标准头文件之外。还有很多插件(需要安装)。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4rmXwftM-1659659009005)(D:\Typora图片\image-20220803224823006.png)]

    (3)创建图形窗口

    刚才我们看了效果,是一个黑窗口。

    我们绘制图片、形状,很难做到,那么我们就需要另外一个窗口。

    这个窗口如何创建呢?

    有这样一个函数:initgraph()

    我们这里就可以利用这个函数,创建一个窗口。

    initgraph(1200,800);
    
    • 1

    函数有三个参数:宽度和高度和窗口标识。(窗口标识可以显示控制台窗口等等,用到的时候再具体讲解)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jWnwgRwM-1659659009006)(D:\Typora图片\image-20220803225202295.png)]

    我们运行一下。

    一闪而过?

    我们来让它停留一下,加一行代码:

    getchar();
    
    • 1

    如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TTojeusL-1659659009006)(D:\Typora图片\image-20220803225635541.png)]

    然后运行一下。

    出来了一个黑窗口,名字就是我们项目的名称。

    在这里插入图片描述

    (4)开始界面文字

    1)写函数

    刚才我们的效果图有一个开始界面(文字渐变等)。

    现在我们写一个welcome函数。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-slehYNID-1659659009007)(D:\Typora图片\image-20220803230006163.png)]

    2)输出文字

    在这个函数里面,输出一些文字

    outtextxy函数:输出(out)文本(text)到指定位置(xy)。

    有三个参数,x、y、字符(字符串)。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qxMYrBA4-1659659009007)(D:\Typora图片\image-20220803230325539.png)]

    这里介绍一下,窗口具体的坐标体系。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3op1INla-1659659009007)(D:\Typora图片\image-20220803230632371.png)]

    了解之后,咱们再来写具体位置。

    比如:

    outtextxy(50,50,"风筝有风,海豚有海,而我有你");
    
    • 1

    运行之后发现错误:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OVMpzaBg-1659659009007)(D:\Typora图片\image-20220803231128250.png)]

    字符集问题

    解决办法:在字符串前面用_T()包裹,给它进行转换。

    就像这样:

    outtextxy(50,50,_T("风筝有风,海豚有海,而我有你"));
    
    • 1

    然后继续运行一下:(文字就出现在窗口啦)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9FR9905P-1659659009008)(D:\Typora图片\image-20220803231426891.png)]

    3)改变文字大小

    上面显示的文字有点小,怎么变大呢?

    这里用到函数settextstyle:设置(set)文字(text)样式(style)。

    将逗号敲上,参数就可以看到了。第一个是高度,第二个是宽度,第三个是字体名称(本地字体)。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tNwmBHop-1659659009008)(D:\Typora图片\image-20220803231931874.png)]

    这里咱们设置高度40,宽度为0(自适应),字体名称为华文行楷(你的电脑里边有的字体,写在这里都可以)。

    如下:

             settextstyle(40,0,"华文行楷");
    
    • 1

    这个地方有下划线,不正确。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jFKBGhR1-1659659009008)(D:\Typora图片\image-20220804082142657.png)]

    上面说了,这是字符集的问题。

    我们用_T()将它们包裹即可。

    只要是EasyX图形库的字符串的,都要用_T()进行转换。

    那么就可以这样来写:

       settextstyle(40,0,_T("华文行楷"));
    
    • 1

    直接改成多字节太麻烦?

    这次的案例,必须使用UniCode,因为要输出爱心,这个爱心是UniCode字符集。改成多字节看不了!

    写完之后,运行看一下。

    可以看到,字体变大了,而且字体变化了。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wdQLKjdM-1659659009008)(D:\Typora图片\image-20220804082848487.png)]

    4)文字居中显示

    刚才文字是我们自己定义的坐标,现在我想让文字居中显示

    要想居中显示,就要知道居中的具体坐标。(只需要知道X坐标即可,X坐标居中,文字就居中啦)

    看图:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fY1j17rZ-1659659009008)(D:\Typora图片\image-20220804084036539.png)]

    由上图可知,我们只需要用(窗口宽度-文字宽度)/2,就得到了文字块的X坐标。

    具体的求法如下:

    窗口宽度:getwidth()
    文字宽度:textwidth()
    文字:_T("XXX")
    
    • 1
    • 2
    • 3

    那么我们求X坐标,就可以得到这样的代码:

    int tx=(getwidth()-textwidth(_T("风筝有风,海豚有海,而我有你")))/2;
    //这里的tx用来接收x坐标位置
    
    • 1
    • 2

    这样的话,输出文字的X坐标就可以用tx表示:

    outtextxy(tx, 50, _T("风筝有风,海豚有海,而我有你"));
    
    • 1

    输出看一下效果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fnhxbfKB-1659659009008)(D:\Typora图片\image-20220804084919287.png)]

    5)输出大量文字

    接下来,咱们就可以输出很多文字啦。

    我这里统一将X坐标设为300,y坐标每一行加50隔开距离。

    	outtextxy(300, 100, _T("织女牵牛送夕阳,临看不觉鹊桥长"));
    	outtextxy(300, 150, _T("此夜星繁河正白,人传织女牵牛客"));
    	outtextxy(300, 200, _T("烟外柳丝湖外水,山眉澹碧月眉黄"));
    	outtextxy(300, 250, _T("相逢一醉是前缘,风雨散飘然何处"));
    	outtextxy(300, 300, _T("别离还有经年客,怅望不如河鼓星"));
    	outtextxy(300, 350, _T("迢迢牵牛星,皎皎河汉女"));
    	outtextxy(300, 400, _T("此日六军同驻马,当时七夕笑牵牛"));
    	outtextxy(300, 450, _T("七夕今宵看碧霄,牵牛织女渡河桥"));
    	outtextxy(300, 500, _T("天上分金镜,人间望玉钩"));
    	outtextxy(300, 550, _T("乌鹊桥成上界通,千秋灵会此宵同"));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    为了将效果更好的展现,这里将第一个标题改一下文字。

    	int tx = (getwidth() - textwidth(_T("此生都是你"))) / 2;
    	outtextxy(tx, 50, _T("此生都是你"));
    
    • 1
    • 2

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6fUVnkgs-1659659009009)(D:\Typora图片\image-20220804090418981.png)]

    输出看一下效果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-22o5am7H-1659659009009)(D:\Typora图片\image-20220804090502506.png)]

    6)设置文字颜色

    可以用函数settextcolor()来设置文字颜色。

    先来设置一下文本的字体颜色(不包括标题),比如蓝色:

    settextcolor(BLUE);
    
    • 1

    看一下效果。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q4wzLGZg-1659659009009)(D:\Typora图片\image-20220804090906948.png)]


    不太美观,现在想让文字颜色变化起来。

    要用RGB合成颜色。

    每个颜色都是由三原色构成。比如这个浅蓝色:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CtjXpMnw-1659659009009)(D:\Typora图片\image-20220804091301554.png)]

    将这个RGB值输入进来。

    settextcolor(RGB(179,161,248));
    
    • 1

    运行一下,文字颜色就改变啦。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DJbuHMum-1659659009010)(D:\Typora图片\image-20220804091517814.png)]


    现在,不想只要一种颜色。

    让它变动起来!

    所有的颜色,都是黑白之间的。

    黑色RGB:0,0,0

    白色RGB:255,255,255

    那我们现在让它随机生成0-256之间的数。

    rand()函数随机生成就好啦,这个函数左闭右开,所以取值范围是0-256。

    如下:

    settextcolor(RGB(rand()%256,rand()%256,rand()%256));
    
    • 1

    用随机函数,得要包含一下头文件:

    #include;
    
    • 1

    然后在welcome函数前面,用srand()函数设置随机数种子。

    srand()seed种子。

    种子需要一个不断变化的数据(每次运行都不同的数据)。

    这里将当前系统的时间作为种子。

    类型不一致,这里强转成unsigned类型。

    如下:

    srand((unsigned)time(NULL));
    
    • 1

    每次运行的颜色都不一样。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L3t2bDf2-1659659009010)(D:\Typora图片\image-20220804092724171.png)]


    现在想让它不断变化,而不是运行一次一个样。

    让它不断循环,设置颜色。

    不断绘制就好。

    while(true){
        
    }
    
    • 1
    • 2
    • 3

    将所有的文字,放在这个死循环当中。

    while (true) {
    		//设置文字颜色
    		settextcolor(RGB(rand() % 256, rand() % 256, rand() % 256));
    
    		outtextxy(300, 100, _T("织女牵牛送夕阳,临看不觉鹊桥长"));
    		outtextxy(300, 150, _T("此夜星繁河正白,人传织女牵牛客"));
    		outtextxy(300, 200, _T("烟外柳丝湖外水,山眉澹碧月眉黄"));
    		outtextxy(300, 250, _T("相逢一醉是前缘,风雨散飘然何处"));
    		outtextxy(300, 300, _T("别离还有经年客,怅望不如河鼓星"));
    		outtextxy(300, 350, _T("迢迢牵牛星,皎皎河汉女"));
    		outtextxy(300, 400, _T("此日六军同驻马,当时七夕笑牵牛"));
    		outtextxy(300, 450, _T("七夕今宵看碧霄,牵牛织女渡河桥"));
    		outtextxy(300, 500, _T("天上分金镜,人间望玉钩"));
    		outtextxy(300, 550, _T("乌鹊桥成上界通,千秋灵会此宵同"));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    然后运行看一下:

    请添加图片描述

    哎呀,一直闪,眼睛都要闪瞎了。

    现在让它慢下来。

    这里用Sleep()函数,1秒变化一次,就是1000毫秒。(在文字末输入这个函数)

    Sleep(1000);
    
    • 1

    再次运行。

    请添加图片描述

    (5)开始界面星星

    这里还是用outtextxy函数。(放在while循环里)

    先将它随便放在一个位置。(❤就是直接用中文输入法,敲aixin,然后第五个就是这个小爱心啦)

    outtextxy(20,20,_T("❤"));
    
    • 1

    这里点击“是”就好。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y2kgHtzY-1659659009011)(D:\Typora图片\image-20220804093945266.png)]

    运行之后,看一下效果:

    这个小爱心还可以不停地闪烁呢。
    请添加图片描述


    一颗星不好,而且位置太固定。

    ①先随机生成坐标。

    rand函数,只要在窗口内部就都合适。

    宽度就膜上窗口宽度(getwidth()),高度就膜上窗口高度(getheight())。

    outtextxy(rand()%getwidth(),rand()%getheight(),_T("❤"));
    
    • 1

    ②将它放入循环里边。

    for(size_t i=0;i<10;i++){
        outtextxy(rand()%getwidth(),rand()%getheight(),_T("❤"));
    }
    
    • 1
    • 2
    • 3

    看一下效果:

    请添加图片描述


    哎呀,这个爱心咋越来越多嘞。

    不是想像中的样子。

    可以让每次画之前,将原来的清除掉

    而且,可以看到,文字会把星星盖住唉,这样不是很好。

    设置背景模式,将文字背景透明。(在while函数外边设置)

    这里就要用到setbkmode()函数,有这样一个参数TRANSPARENT

    setbkmode(TRANSPARENT);
    
    • 1

    清屏

    在while函数前边,写上:

    cleardevice();
    
    • 1

    看一下效果:

    请添加图片描述

    标题不见了,忘记移进来了。

    问题不大。

    放到while循环里边就好。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mMvnntTa-1659659009012)(D:\Typora图片\image-20220804100049972.png)]


    刚才的爱心变化,和字体颜色变化一致不太好。

    这里让爱心与字体产生不同颜色。

    直接将上面的settextcolor函数放进for循环即可。

    for(size_t i=0;i<10;i++){
        settextcolor(RGB(rand() % 256, rand() % 256, rand() % 256));
        outtextxy(rand()%getwidth(),rand()%getheight(),_T("❤"));
    }
    
    • 1
    • 2
    • 3
    • 4

    看一下效果:

    请添加图片描述

    (6)播放音乐

    将这些文件复制到项目的当前路径中。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aEfw2TbR-1659659009012)(D:\Typora图片\image-20220804141858929.png)]

    当前路径:右键项目名称,打开“文件资源管理器”。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8n3SajKQ-1659659009012)(D:\Typora图片\image-20220804130647458.png)]

    将刚才的文件夹复制粘贴进来。

    一定要和.cpp在同一个目录下。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a2JEvRJY-1659659009013)(D:\Typora图片\image-20220804130807668.png)]

    然后包含头文件。

    #include
    
    • 1

    包含库文件。

    #pragma comment(lib,"winmm.lib")
    
    • 1

    包含之后,就可以来播放音乐啦。

    welcome前边添加mciSendString()函数。

    解释一下:

    mci:media(多媒体) device(设备) interface(接口)
    Send:发送
    String:字符串
    合起来就是,向多媒体设备接口发送字符串。
    
    • 1
    • 2
    • 3
    • 4

    看一下参数。(就看第一个就好,后边的不用管)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bg3molsK-1659659009013)(D:\Typora图片\image-20220804142626184.png)]

    既然要播放音乐,首先得打开(open)音乐(当前路径下的音乐)。

    mciSendString(_T("open ./images/音乐.mp3"), NULL, 0, NULL);
    
    • 1

    打开音乐之后,播放音乐。

    mciSendString(_T("play ./images/音乐.mp3"), NULL, 0, NULL);
    
    • 1

    然后就可以运行播放啦!

    这里不太好演示,大家自行播放。

    这里介绍一种简洁写法:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jrde11xi-1659659009013)(D:\Typora图片\image-20220804233402199.png)]

    最后,在主函数里面进行调用welcome函数。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aZjhZhHD-1659659009013)(D:\Typora图片\image-20220803230112728.png)]

    三、星星

    (1)定义结构体

    在图形库上绘制任何东西,都要有坐标。

    咱们先来定义一个结构体。

    struct Star {	 //小星星
    	int x;	//x坐标
    	int y;	//y坐标
    	int speed;	//速度
    	COLORREF color;	//颜色
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    定义一个结构体数组:(绘制500个)

    #define STAR_NUM 500
    struct Star star[STAR_NUM];
    
    • 1
    • 2

    (2)初始化星星

    定义一个函数,来初始化星星。

    传一个下标进来,对指定位置的星星进行初始化。

    void initStar(int i) {
    	
    }
    
    • 1
    • 2
    • 3

    初始化x。(只要在窗口里面就是合法的)

    star[i].x = rand() % getwidth();
    
    • 1

    初始化y

    star[i].y=rand()%getheight();
    
    • 1

    初始化speed。速度不要太快,这里膜上5,就是0、1、2、3、4。

    star[i].speed=rand()%5;
    
    • 1

    初始化颜色color。随机生成颜色,上面说过随机生成颜色:RGB(rand() % 256, rand() % 256, rand() % 256)

    star[i].color=RGB(rand() % 256, rand() % 256, rand() % 256);
    
    • 1

    这样,初始化星星就好啦。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ov2xrfXe-1659659009013)(D:\Typora图片\image-20220804180403404.png)]

    (3)绘制星星

    现在来绘制星星。

    定义一个函数。

    void drawStar() {
    
    }
    
    • 1
    • 2
    • 3

    这里就不传进来了,对所有的都进行绘制。

    既然对所有的进行绘制,就要用到循环。

    for(size_t i=0;i<STAR_NUM;i++){
    	
    }
    
    • 1
    • 2
    • 3

    这里绘制的星星,是一个像素点

    像素点是用putpixel()绘制的。

    参数分别是xycolor

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-njZYDhm0-1659659009014)(D:\Typora图片\image-20220804180919235.png)]

    三个参数我们之前定义好啦,直接拿进来。

    putpixel(star[i].x,star[i].y,star[i].color);
    
    • 1

    (4)调用

    现在函数是定义好了,并没有调用。

    那怎么调用呢?

    1)初始化

    在主函数里边。

    初始化星星。

    initStar();
    
    • 1

    然后传参数,每一个都要进行初始化。

    照样需要遍历一下。

    for(size_t i=0;i<STAR_NUM;i++){
        initStar(i);
    }
    
    • 1
    • 2
    • 3

    2)绘制

    接下来要进行绘制。

    drawStar();
    
    • 1

    3)问题解答

    ①运行一下并没有星星出现。

    为啥呢?

    当时我们的welcome函数里面用了while死循环!

    没有退出条件,就在那里卡死了。

    这里我们再次包含一个头文件:

    #include
    
    • 1

    这个头文件里面有一个函数_kbhit()

    有按键,就会返回一个真,没有按键就返回假。

    这里给它加上一个!,取反。如果没有按键,就一直循环;如果有按键,就退出循环。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ProTkrQv-1659659009015)(D:\Typora图片\image-20220804182549583.png)]

    将初始化放在welcome函数调用之前。

    然后进行welcome函数,有按键,就退出循环。

    继续执行drawStar函数,画星星。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rHI13S2n-1659659009015)(D:\Typora图片\image-20220804182834624.png)]

    ②执行一下,按了任意键之后,退出了?

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XKiRLZck-1659659009015)(D:\Typora图片\image-20220804182954778.png)]

    咱们将画星星循环起来。

    while(true){
        drawStar();
    }
    
    • 1
    • 2
    • 3

    然后运行,就有了漫天的星星。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VRdWCQVV-1659659009015)(D:\Typora图片\image-20220804183200425.png)]

    但是文字没有消失。

    这里可以清屏(cleardevice)一下。

    while(true){
        cleardevice();
        drawStar();
    }
    
    • 1
    • 2
    • 3
    • 4

    再运行,然后按下任意键,就可以看到漫天闪烁的星星了。

    请添加图片描述

    ③其实这是一个Bug,因为是一直画星星,一直在清屏,所以星星会闪。

    这里为了解决闪屏,用到另一个技术:双缓冲绘图

    分别在循环前、后、中间写上:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pKaGS7LW-1659659009016)(D:\Typora图片\image-20220804184017604.png)]

    然后再运行,就不会闪烁啦。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HzgkVvRK-1659659009016)(D:\Typora图片\image-20220804184109486.png)]

    (5)移动星星

    现在的星星是不动的,我们想让它移动,怎么办?

    再来写一个函数moveStar

    void moveStar(){
        
    }
    
    • 1
    • 2
    • 3

    这里要遍历没有一个星星,就要用到循环。

    for(size_t i=0;i<STAR_NUM;i++){
    	
    }
    
    • 1
    • 2
    • 3

    从左向右移动,要改变x坐标。(这里一个像素一个像素地移动)

    for(size_t i=0;i<STAR_NUM;i++){
    	star[i].x++;
    }
    
    • 1
    • 2
    • 3

    写好了函数,接下来在主函数循环里边对它进行调用。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7G9EUn8D-1659659009016)(D:\Typora图片\image-20220804184647085.png)]

    运行看一下效果:

    请添加图片描述

    这儿咱们做一下优化。

    因为星星本来就有速度,可以这样写:

    for(size_t i=0;i<STAR_NUM;i++){
    	star[i].x+=star[i].speed;
    }
    
    • 1
    • 2
    • 3

    上边的效果,也不是咱们想要的。

    可以看到,星星走了之后,没有啦。

    我想让它们走了再回来!(超过窗口边界之后,转身回头)

    在循环里边做一个判断就好:

    if(star[i].x>getwidth()){  //如果x坐标超出窗口
        star[i].x=0;		//让x坐标重新为0
    }
    
    • 1
    • 2
    • 3

    写这儿就对啦,别写错地方了:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-npGUxnEw-1659659009017)(D:\Typora图片\image-20220804185408332.png)]

    这样就会有源源不断的星星

    自己去运行一下试试。

    (6)拓展

    有人可能会说,星星不够亮。

    这里可以画圆。

    比如:

    solidcircle(star[i].x, star[i].y, star[i].speed); 
    //前面两个参数是x和y,最后一个是圆的半径,这里就拿星星速度做演示了(将星星速度作为半径)
    
    • 1
    • 2

    然后将它的颜色改为随机的。

    setfillcolor(star[i].color);
    
    • 1

    就是这样:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hQ9Us8z8-1659659009017)(D:\Typora图片\image-20220804190117050.png)]

    运行一下看看:
    请添加图片描述

    好像这个更好看。

    干脆来个半径吧,上面用速度当作半径来着。

    这里咱们搞个半径。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IeaYp6hq-1659659009018)(D:\Typora图片\image-20220804190441756.png)]

    同样在初始化的时候,对半径也进行一下初始化。让圆的半径小一点,这里膜上3加1。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-II4QCQsM-1659659009018)(D:\Typora图片\image-20220804190604248.png)]

    然后在绘制里边,改一下半径。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fFpAqyrf-1659659009018)(D:\Typora图片\image-20220804190712890.png)]

    运行看一下效果:

    请添加图片描述

    四、流星

    (1)加载图片

    接下来,我们来做流星。

    其实和上边做星星差不多,只不过这次用图片来做。

    图片也是数据,这里定义一个数组。

    IMAGE img[1]; //这里提供1张流星图片
    
    • 1

    然后定义一个加载函数。

    void loadImg(){
        
    }
    
    • 1
    • 2
    • 3

    通过函数给图片初始化。

    loadimage();
    
    • 1

    这个函数有好几个参数。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v4buv2Xp-1659659009019)(D:\Typora图片\image-20220804192104789.png)]

    第一个参数是图片的指针,直接将数组名写入。

    第二个是图片路径。

    后边是图片宽度和高度。

    loadimage(img + 0, _T("./images/流星1.png"));
    //img+0是为了让它对齐。
    
    • 1
    • 2

    (2)输出图片

    用函数putimage输出图片。

    这里随便输出到窗口,比如(10,10)的位置。

    putimage(10,10,img);
    
    • 1

    放在主函数while循环里面输出。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qyUbJVa-1659659009019)(D:\Typora图片\image-20220804192752329.png)]

    (3)写入主函数

    然后将函数写入主函数。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rnJ0qIJu-1659659009019)(D:\Typora图片\image-20220804192556589.png)]

    图片太大,可以进行一下缩放。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-unZm1lpl-1659659009020)(D:\Typora图片\image-20220804193113070.png)]

    (4)定义结构体

    和上边星星一样,我们要先定义一个结构体。

    struct Meteor {
    	int x;
    	int y;
    	int speed;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    然后定义一个变量。

    #define METEOR_NUM 100
    struct Meteor meteor[METEOR_NUM];
    
    • 1
    • 2

    (5)初始化流星

    void initMeteor(int i) {
    
    }
    
    • 1
    • 2
    • 3

    然后:

    void initMeteor(int i) {
    	meteor[i].x;
    	meteor[i].y;
    	meteor[i].speed;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (6)流星生成范围

    如果只是在屏幕里边,会显得不和谐。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lzk1LWde-1659659009020)(D:\Typora图片\image-20220804194236027.png)]

    最少要在这个范围产生一些流星。(绿色范围)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HYRIgRQr-1659659009020)(D:\Typora图片\image-20220804194458600.png)]

    这里就当是窗口的宽高来计算,比较省事。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ine36fs1-1659659009020)(D:\Typora图片\image-20220804194913731.png)]

    现在我们将上边的数值改变一下。

    ①第一个x

    先分析一下:

    meteor[i].x=rand()%getwidth();  //[0,1200)
    现在想要[-1200,1200)
         2*getwidth()          ---> [0,2400)
         2*getwidth()-getwidth ---> [-1200,1200)
    
    • 1
    • 2
    • 3
    • 4

    然后得出:

    meteor[i].x=rand()%(2*getwidth())-getwidth();
    
    • 1

    ②第二个y

    一开始就让它产生在负的地方,从上边往下落。

    可以这样:

    meteor[i].y=20-200;
    
    • 1

    ③第三个speed

    meteor[i].speed=rand()%15+1;
    
    • 1

    (7)绘制流星

    接下来就是绘制流星了。

    定义一个函数。

    void drawMeteor() {
    
    }
    
    • 1
    • 2
    • 3

    再遍历一下,给每一个都进行绘制。(随机绘制)

    for (size_t i = 0; i < METEOR_NUM; i++) {
    		putimage(meteor[i].x, meteor[i].y, img + 0);
    }
    
    • 1
    • 2
    • 3

    如果你有两张图,可以这样写,来随机贴一张。

    for (size_t i = 0; i < METEOR_NUM; i++) {
    		putimage(meteor[i].x, meteor[i].y, img + rand()%2);
    }
    
    • 1
    • 2
    • 3

    主函数里边的贴图就可以不要了。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2PSNsjgt-1659659009020)(D:\Typora图片\image-20220804220235266.png)]

    (8)移动流星

    定义一个函数。

    void moveMeteor(){
        
    }
    
    • 1
    • 2
    • 3

    然后遍历。(x和y都要移动)

    for (size_t i = 0; i < METEOR_NUM; i++) {
    		meteor[i].x+=meteor[i].speed;
        	meteor[i].y+=meteor[i].speed;
    }
    
    • 1
    • 2
    • 3
    • 4

    主函数里边,初始化:

    	for (size_t i = 0; i < METEOR_NUM;i++) {
    		initMeteor(i);
    
    	}
    
    • 1
    • 2
    • 3
    • 4

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-83RV2bT0-1659659009021)(D:\Typora图片\image-20220804220710635.png)]

    然后是绘制和移动。(在while循环里)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mOVSgSiF-1659659009021)(D:\Typora图片\image-20220804220836072.png)]

    ①接下来,在移动流星里,要做一些小处理,让流星降落之后,又返回重新开始

    xy都要判断。

    if(meteor[i].x>=getwidth() || meteor[i].y>=getheight()){
        initMeteor(i); //哪一个消失就初始化哪一个
    }
    
    • 1
    • 2
    • 3

    写在这个地方:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k0o289IV-1659659009021)(D:\Typora图片\image-20220804222911248.png)]

    ②流星在下坠的时候,会有一个背景颜色,遮挡住星星。

    这里我们将它的背景颜色清除

    putimage函数后边,还可以加一个参数SRCPAINT

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bCif1QHM-1659659009022)(D:\Typora图片\image-20220804223356435.png)]

    五、设置背景颜色

    我们可以在主函数,设置一下背景颜色。

    比如灰色。

    	setbkcolor(RGB(82, 164, 143));
    	cleardevice();
    
    • 1
    • 2

    如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9taIygMP-1659659009022)(D:\Typora图片\image-20220804224126857.png)]

    哎呀,不好看。

    这里我就不设置了。

    六、设置背景图片

    可以自行去找一张图。

    保存进images文件夹。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UgpWM8Ro-1659659009022)(D:\Typora图片\image-20220804224527939.png)]

    直接在主函数里面写:

    IMAGE bk;
    loadimage(&bk, _T("./images/图.png"), getwidth(), getheight());
    
    • 1
    • 2

    如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pmORIb5m-1659659009023)(D:\Typora图片\image-20220804225059894.png)]

    注意,贴图要在循环里边。

    这里有cleardevice,不写的话是看不到的。

    这个地方贴:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6mApopUT-1659659009023)(D:\Typora图片\image-20220804225344855.png)]

    改一下位置:(一开始要贴图片的话,就要将它作为全局变量)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ansIsDXl-1659659009023)(D:\Typora图片\image-20220804225740102.png)]

    再将putimage复制一个,放在清屏之后。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-swpyIIRB-1659659009023)(D:\Typora图片\image-20220804230139031.png)]

    七、所有代码

    这里将所有代码奉上,供大家学习使用:

    #include
    #include //第三方图形库,需要安装
    #include
    #include
    #pragma comment(lib,"winmm.lib")
    #include
    
    #define STAR_NUM 500
    #define METEOR_NUM 100
    
    struct Star {	 //小星星
    	int x;	//x坐标
    	int y;	//y坐标
    	int r;
    	int speed;	//速度
    	COLORREF color;	//颜色
    };
    struct Meteor {
    	int x;
    	int y;
    	int speed;
    };
    
    struct Star star[STAR_NUM];
    struct Meteor meteor[METEOR_NUM];
    
    /*星星*/
    //初始化星星
    void initStar(int i) {
    	star[i].x = rand() % getwidth();
    	star[i].y = rand() % getheight();
    	star[i].r = rand() % 3 + 1;
    	star[i].speed = rand() % 5;
    	star[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
    }
    
    //绘制星星
    void drawStar() {
    	for (size_t i = 0; i < STAR_NUM; i++) {
    		//putpixel(star[i].x, star[i].y, star[i].color);
    		setfillcolor(star[i].color);
    		solidcircle(star[i].x, star[i].y, star[i].r);
    	}
    }
    
    //移动星星
    void moveStar() {
    	for (size_t i = 0; i < STAR_NUM; i++) {
    		star[i].x += star[i].speed;
    		if (star[i].x > getwidth()) {  //如果x坐标超出窗口
    			star[i].x = 0;		//让x坐标重新为0
    		}
    	}
    }
    
    
    /*流星*/
    IMAGE img[1];
    IMAGE bk;
    void loadImg() {
    	loadimage(img + 0, _T("./images/流星1.png"),50,50);
    	
    }
    //初始化流星
    void initMeteor(int i) {
    	meteor[i].x = rand() % (2 * getwidth()) - getwidth();
    	meteor[i].y = 20 - 200;
    	meteor[i].speed = rand() % 15 + 1;
    }
    //绘制流星:贴图
    void drawMeteor() {
    	for (size_t i = 0; i < METEOR_NUM; i++) {
    		putimage(meteor[i].x, meteor[i].y, img + 0,SRCPAINT);
    	}
    }
    //移动流星
    void moveMeteor() {
    	for (size_t i = 0; i < METEOR_NUM; i++) {
    		meteor[i].x += meteor[i].speed;
    		meteor[i].y += meteor[i].speed;
    		if (meteor[i].x >= getwidth() || meteor[i].y >= getheight()) {
    			initMeteor(i); //哪一个消失就初始化哪一个
    		}
    	}
    }
    
    
    
    void welcome() {
    
    	//播放音乐
    	mciSendString(_T("open ./images/音乐.mp3"), NULL, 0, NULL);//打开音乐
    	mciSendString(_T("play ./images/音乐.mp3"), NULL, 0, NULL);//播放音乐
    
    	//设置随机数种子
    	srand((unsigned)time(NULL));
    
    	//设置背景模式
    	setbkmode(TRANSPARENT);
    
    	//设置文字样式
    	settextstyle(40, 0, _T("华文行楷"));
    	
    	//如果没有按键按下,就一直循环
    	while (!_kbhit()) {
    
    		//清屏
    		cleardevice();
    		putimage(0, 0, &bk);
    
    		//设置文字颜色
    		settextcolor(RGB(rand() % 256, rand() % 256, rand() % 256));
    
    		//输出文字
    		int tx = (getwidth() - textwidth(_T("此生都是你"))) / 2;
    		outtextxy(tx, 50, _T("此生都是你"));
    
    		outtextxy(300, 100, _T("织女牵牛送夕阳,临看不觉鹊桥长"));
    		outtextxy(300, 150, _T("此夜星繁河正白,人传织女牵牛客"));
    		outtextxy(300, 200, _T("烟外柳丝湖外水,山眉澹碧月眉黄"));
    		outtextxy(300, 250, _T("相逢一醉是前缘,风雨散飘然何处"));
    		outtextxy(300, 300, _T("别离还有经年客,怅望不如河鼓星"));
    		outtextxy(300, 350, _T("迢迢牵牛星,皎皎河汉女"));
    		outtextxy(300, 400, _T("此日六军同驻马,当时七夕笑牵牛"));
    		outtextxy(300, 450, _T("七夕今宵看碧霄,牵牛织女渡河桥"));
    		outtextxy(300, 500, _T("天上分金镜,人间望玉钩"));
    		outtextxy(300, 550, _T("乌鹊桥成上界通,千秋灵会此宵同"));
    
    		for (size_t i = 0; i < 10; i++) {
    			settextcolor(RGB(rand() % 256, rand() % 256, rand() % 256));
    			outtextxy(rand() % getwidth(), rand() % getheight(), _T("❤"));
    		}
    
    		Sleep(1000);
    	}
    
    	
    
    }
    
    int main() {
    	//1.创建图形窗口
    	initgraph(1200,800);
    
    	//设置背景颜色
    	//setbkcolor(RGB(82, 164, 143));
    	//cleardevice();
    
    	
    	loadimage(&bk, _T("./images/图.png"), getwidth(), getheight());
    
    	for (size_t i = 0; i < STAR_NUM; i++) {
    		initStar(i);
    	}
    
    	for (size_t i = 0; i < METEOR_NUM;i++) {
    		initMeteor(i);
    
    	}
    
    	loadImg();
    
    	welcome();
    
    	//双缓冲绘图
    	BeginBatchDraw();
    
    	while (true) {
    		cleardevice();
    
    		putimage(0, 0, &bk);
    
    		drawStar();
    		moveStar();
    
    		drawMeteor();
    		moveMeteor();
    
    		//putimage(10, 10, img);
    
    		FlushBatchDraw();
    	}
    
    	EndBatchDraw();
    
    	getchar();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188

    图片啥的大家可以自己改。
    网盘链接放在评论区,需要源文件的自取。
    PDF文件也会上传到资源里面。
    希望文章对你有帮助!
    拜拜~
    在这里插入图片描述

  • 相关阅读:
    JavaScript中的包装类型详解
    pg-备份和还原
    你需要知道的13个有用的Python片段
    Java多线程按顺序输出10以内的奇偶数
    聚焦AI丨车企如何用AI服务争夺市场话语权
    yum clean all 后导致yum报错排查解决方案
    SLAM从入门到精通(编写第一个package)
    WebSocket实时应用
    UI组件库Kendo UI for Vue中文入门指南(一)
    Java设计模式之建造者模式
  • 原文地址:https://blog.csdn.net/m0_55746113/article/details/126170268