成果展示
多说无益,上效果
我相信当你们看完成果之后一定会更有动力向下探索,因为这可是让你脱单的大好机会啊,当你拥有了此爱心,你就赢得了人生,从此不再单身,快来瞧瞧吧~
前言:
认识Easyx库中的函数
如果你想做一个非常漂亮的动态爱心,当然少不了Easyx库,有了它你就可以绘制图片,让我们一起向下学习吧我把下载Easyx的官网放到这里了点进去下载即可:Easyx官网
注意Easyx图形库是基于C++封装的,所以在使用的时候把源文件创建为后缀.ccp文件
使用时先包含头文件
include<easyx.h>
🚘进入正题
首先我们先认识一下如何创建图形窗口
创建图形库的函数:
HWND initgraph( int width,int height,int flag = NULL);
windth:指定窗口宽度
height:指定窗口高度
flag:设置窗口模式
窗口模式有以下几种:
- EX_DBLCLKS (在绘图窗口中支持鼠标双击事件)
- EX_NOCLOSE(禁用绘图窗口的关闭按钮)
- EX_NOMINIMIZE(禁用绘图窗口的最小化按钮)
- EX_SHOWCONSOLE(显示控制台窗口)
创建图形窗口展示
代码写法#include<easyx.h>
int main()
{
initgraph(500, 500);
getchar(); //防止直接退出
return 0;
}
效果
如果加上窗口模式
#include<stdio.h>
#include<easyx.h>
int main()
{
//此时加入了两种窗口模式,第一种是关闭按钮取消,第二种是最小化按钮取消
initgraph(500, 500,EW_NOCLOSE|EW_NOMINIMIZE);
getchar(); //防止直接退出
return 0;
}
效果展示
小知识
图形窗口也是有坐标轴的
文字输出
当我们有了窗口时,如果想在窗口上输出自己喜欢人的名字该怎么办呢?我知道大家很急,但请大家先不要急,接下来我们认识一下输出文字的函数
文字输出函数:
void outtextxy( int x, int y, LPCTSTR str );
参数:
x:字符串输出时头字母的 x 轴的坐标值
y:字符串输出时头字母的 y 轴的坐标值
str:想输入的文字(此文字是字符串需要加双引号)
常见错误
代码写法
会发现使用此函数是会报错,这是因为跟字符集有关此时我使用的字符集是如图所示
如何更改字符集
此时代码就不会报错了,可以顺利的打印出字
去除字的背景
用Easyx库打印出的字是会带背景的,我们需要用一个函数来去掉字的背景
此函数功能去掉字的背景:
void setbkmode(int mode);
设置字体大小以及字体样式
我们会发现这个字体非常的丑陋,并且想更改字体的大小,如何做到呢?
接下来这个函数就可以做到以上功能
设置文字样式:
void settextstyle( int nHeight, int nWidth, LPCTSTR lpszFace );
参数介绍
nHeight:设置字体的高度
nWidth:设置字体的宽度(一般只设置字体高度,宽度取0自适应高度)
lpszFace:字体名称
代码写法
#include<stdio.h>
#include<easyx.h>
int main()
{
initgraph(500, 500);
settextstyle(60, 0, "鸿雷行书简体");
outtextxy(130, 180, "我是大帅比啊");
getchar();
return 0;
}
展示效果
字体颜色
接下来就是字体颜色的介绍了
设置字体颜色:
void settextcolor(COLORREF color);
参数
color :填颜色英文单词大写
代码写法
这里以红色为例
#include<stdio.h>
#include<easyx.h>
int main()
{
initgraph(500, 500);
settextcolor(RED); //RED 红色单词
settextstyle(60, 0, "鸿雷行书简体");
outtextxy(130, 180, "我是大帅比啊");
getchar();
return 0;
}
展示效果
BeginBatchDraw和EndBatchDraw函数
🦄BeginBatchDraw函数
功能:用于开始批量绘图。执行后,任何绘图操作都将暂时不输出到绘图窗口上,直到执行 FlushBatchDraw 或 EndBatchDraw 才将之前的绘图输出。
void BeginBatchDraw();
🐴EndBatchDraw函数
功能:这个函数用于结束批量绘制,并执行未完成的绘制任务。
// 结束批量绘制,并执行未完成的绘制任务
void EndBatchDraw();
🎵实现音乐播放
实现动态爱心,必然少不了音乐的渲染,有了音乐氛围不就来了吗?这样你表白还会失败吗?快来学学如何播放音乐吧~
播放音乐必须先包含这两个
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")
播放音乐的函数
mciSendString("play 告白气球.mp3 repeat", 0, 0, 0);
播放:plya
音乐名:告白气球.mp3
模式:repeat(重复播放)
后三个参数全部传0即可
学了以上函数我们写这个动态爱心已经不成问题了
我们先不急着做爱心,让我们来一起看看爱心公式背后的精彩故事吧
💘笛卡尔的爱心故事
52岁的笛卡尔邂逅了18岁瑞典公主克莉丝汀。笛卡尔落魄无比,穷困潦倒又不愿意请求别人的施舍,每天只是拿着破笔破纸研究数学题。有一天克莉丝汀的马车路过街头发现了笛卡尔是在研究数学,公主便下车询问,最后笛卡尔发现公主很有数学天赋。
道别后的几天笛卡尔收到通知,国王要求他做克莉丝汀公主的数学老师。其后几年中相差34岁的笛卡尔和克莉丝汀相爱,国王发现并处死了笛卡尔。笛卡尔给公主写了十二封情书,不幸的是都被国王拦了下来。
在临死之前笛卡尔给公主写了第十三封情书,信里面没有一个字,只有一个方程。国王收到这封信后百思不得其解,于是召集了瑞典所有的数学家进行研究,还是一无所获,就把这封信交给了公主。公主很快就找到了答案,这个方程的对应曲线就是著名的心形线。
好了,故事说完了,该写代码了。问题是我们很多人数学并不好,很多程序员老鸟的数学也不好,如果因为这个东西,就要去重学数学,那这个撩妹成本也太了。所以直接“借鉴”方便的公式使用。
公式选择及其分析
笛卡尔曲线公式演变出很多公式,我选择了这个
(注意感觉这个公式绘制的心形比较苗条,有些公式绘制的效果像个大馒头,影响B格和效果)
因为笛卡尔公式是用极坐标表示的,所以我们先学习一下极坐标
在极坐标中,任意一点的位置,由r和θ决定,r表示这个点到“极点”O之间的距离,θ表示如上图所示的夹角。根据这个公式,我们可以直接写出如下代码:
/ angle表示角度θ,r表示ρ
double r = sin(angle) * sqrt(fabs(cos(angle))) / (sin(angle) + 1.4142) - 2 * sin(angle) + 2;
大家看到代码可能有几个函数不知道是干什么的,我在此逐个解释一下
如:
sin()函数 ,cos()函数,sqrt()函数,fabs()函数
,这几个函数都需要包含数学头文件#include<math.h>
否则无法使用
sqrt()函数
:是给括号内的数据加根号的,如sqrt(3) 其实可以翻译成根号3
fabs()函数
:是给括号内部的数据取绝对值的,如fabs(-3) 其实可以翻译成|-3|
由于我们在使用代码进行绘制的时候,使用的都是直角坐标系,所以还需要把这个极坐标,转换成直角坐标系,转换公式如下
x=rcos(0);
y=rsin(0);
现在我们就的到了完整的曲线公式
r = sin(angle) * sqrt(fabs(cos(angle))) / (sin(angle) + 1.4142) - 2 * sin(angle) + 2;
x = r * cos(angle);
y = r * sin(angle);
轨迹测试(用 ' * ' 打印)
#include <stdio.h>
#include <easyx.h>
#include <math.h>
int main(void)
{
initgraph(800, 600);
int R = 100;
double angle = 0;
for (angle = 0; angle <= 3.14 * 2; angle += 0.1)
{
double r = (sin(angle) * sqrt(fabs(cos(angle))) / (sin(angle) + 1.4142)
- 2 * sin(angle) + 2);
int x = R * r * cos(angle)+400; //这里的400指的是向右偏移多少
int y = -R * r * sin(angle)+100; //这里的100指的是向下偏移多少
outtextxy(x, y, "*");
}
system("pause");
return 0;
}
展示效果
用特殊字体打印爱心轨迹
到现在,我们已经把心形轨迹模拟出来了,但是每个轨迹点的“爱心”还没有实现。其实最简单的办法就是直接使用一个心形小图片,但是这样子不方便表白活动的展开,总不能给学妹分享你的B格代码时,还要额外附送一个心形图片,这样子就没有神秘感,B格维度直接降低到零点。
其实也很简单,只要把字体调整一下就可以了,把字体设置为“Webdings”,再直接输出字符Y即可得到一个心形,但是代码中可以不要直接输出Y, 可以在代码中输出Y的ASCII码值89, 这样就更神秘了
我把字体下载官网放在这里,有需要的可以点击下载:Webdings
下载好了字体再做一个测试
#include <stdio.h>
#include <easyx.h>
#include <math.h>
int main(void) {
initgraph(550, 500);
int R = 100;
double angle = 0;
for (angle = 0; angle <= 3.14 * 2; angle += 0.1)
{
double r = (sin(angle) *sqrt(fabs(cos(angle))) / (sin(angle) + 1.4142)
- 2 * sin(angle) + 2);
int x = R * r * cos(angle) + 260;
int y = -R * r * sin(angle) + 80; //垂直翻转
setbkmode(TRANSPARENT);
settextcolor(RED);
settextstyle(40, 0, "Webdings");
setbkmode(TRANSPARENT);
outtextxy(x, y, 89); //89 就是 'Y'
}
system("pause");
return 0;
}
展示效果
粒子喷射器原理
我们可以一次再爱心轨迹上随机创建20颗小爱心,打印,然后再对创建的20颗小爱心的大小(加大)和位置(向外偏移)进行修改,前后间隔30毫秒循环一次,当一颗小爱心被加大20次之后,即偏移20次之后,对这颗小爱心进行初始化,就这样一直循环就能得到爱心的动态效果。
动态爱心代码分部实现:
①main函数框架
int main()
{
init(); //初始化
while (1)
{
Creatlove(); //创建小爱心
Printlove(); //打印小爱心
Modifylove(); //修改爱心大小和位置
Sleep(30); //间隔30毫秒
}
}
②定义爱心结构、放大倍数R和喷射池子的大小
#define R 100 //R为放大倍数
struct love
{
int x;
int y;
//爱心x,y坐标位置
int height; //控制字符大小
double angle; //角度
double r; //笛卡尔曲线半径
int cuR; //对半径的放大倍数
};
③初始化
void init()
{
initgraph(700, 600);
memset(movelove, 0, sizeof(struct love) * 400); //初始化为零
srand(time(NULL)); //设置随机种子
}
④创建小爱心
void Creatangle(int angles[], int count)
{
int M = 3.14 * 2*100; //因为rand()函数返回的是整数所以只好把3.14放大一百倍
for (int i = 0;i < count;i++)
{
x:
int angle = rand() % M;
for (int k = 0; k < i; k++)
{
if (angles[k] == angle)
{
goto x;
}
}
angles[i] = angle;
}
}
void Creatlove()
{
int k = 0;
for ( k = 0;k < 400 && movelove[k].cuR>0;k++); //过滤已经创建的心心
//创建随机角度
int angles[20];
Creatangle(angles, 20);
//连续创建20个随机爱心
for (int i = k;i < k + 20;i++)
{
movelove[i].angle = angles[i-k]*0.01;
movelove[i].r =(sin(movelove[i].angle) * sqrt(fabs(cos(movelove[i].angle)))) / (sin(movelove[i].angle) + 1.4142)
- 2 * sin(movelove[i].angle) + 2;
movelove[i].cuR = R;
movelove[i].height = 0;
movelove[i].x = movelove[i].cuR * movelove[i].r * movelove[i].angle + 360;
movelove[i].y = -movelove[i].cuR * movelove[i].r * movelove[i].angle + 170;
}
}
⑤打印小爱心
void Printlove()
{
BeginBatchDraw();
cleardevice(); //清除当前窗口
settextcolor(RED);
for (int i = 0;i < 400;i++)
{
if (movelove[i].cuR == 0) continue;
settextstyle(movelove[i].height+20, 0, "Webdings");
setbkmode(TRANSPARENT); //字符背景透明
outtextxy(movelove[i].x+10, movelove[i].y-80, "Y"); //movelove[i].x加10和 movelove[i].y减80
//是为了控制输出在窗口的中间
}
EndBatchDraw();
}
⑥修改小爱心
void Modifylove()
{
for (int i = 0;i < 400;i++)
{
if (movelove[i].cuR == 0) continue;
movelove[i].cuR++; //字符位置往外移
//一次随机创20颗爱心,创建20次,movelove[i].cuR初始值为100,当它大于120,让它回归最初状态
if (movelove[i].cuR >= 120)
{
memset(&movelove[i], 0, sizeof(struct love));
}
movelove[i].height++; //字符变大
movelove[i].x = movelove[i].cuR * movelove[i].r * cos(movelove[i].angle) + 360;
movelove[i].y = -movelove[i].cuR * movelove[i].r * sin(movelove[i].angle) + 170 ;
}
}
🎉 源码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <easyx.h>
#include <math.h>
#include <time.h>
#define R 100
struct love
{
int x;
int y;
//爱心x,y坐标位置
int height; //控制字符大小
double angle; //角度
double r; //笛卡尔曲线半径
int cuR; //对半径的放大倍数
};
//一次创建20颗爱心,20轮
struct love movelove[20 * 20]; //存放爱心
void init()
{
initgraph(700, 600);
memset(movelove, 0, sizeof(struct love) * 400);
srand(time(NULL)); //设置随机种子
}
void Creatangle(int angles[], int count)
{
int M = 3.14 * 2 * 100;
for (int i = 0;i < count;i++)
{
x:
int angle = rand() % M;
for (int k = 0; k < i; k++)
{
if (angles[k] == angle)
{
goto x;
}
}
angles[i] = angle;
}
}
void Creatlove()
{
int k = 0;
for ( k = 0;k < 400 && movelove[k].cuR>0;k++); //过滤已经创建的心心
//创建随机角度
int angles[20];
Creatangle(angles, 20);
//连续创建20个随机爱心
for (int i = k;i < k + 20;i++)
{
movelove[i].angle = angles[i-k]*0.01;
movelove[i].r =(sin(movelove[i].angle) * sqrt(fabs(cos(movelove[i].angle)))) / (sin(movelove[i].angle) + 1.4142)
- 2 * sin(movelove[i].angle) + 2;
movelove[i].cuR = R;
movelove[i].height = 0;
movelove[i].x = movelove[i].cuR * movelove[i].r * movelove[i].angle + 360;
movelove[i].y = -movelove[i].cuR * movelove[i].r * movelove[i].angle + 170;
}
}
void Printlove()
{
BeginBatchDraw();
cleardevice(); //清除当前窗口
settextcolor(RED);
for (int i = 0;i < 400;i++)
{
if (movelove[i].cuR == 0) continue;
settextstyle(movelove[i].height+20, 0, "Webdings");
setbkmode(TRANSPARENT); //字符背景透明
outtextxy(movelove[i].x+10, movelove[i].y-80, "Y"); //movelove[i].x加10和 movelove[i].y减80
//是为了控制输出在窗口的中间
}
EndBatchDraw();
}
void Modifylove()
{
for (int i = 0;i < 400;i++)
{
if (movelove[i].cuR == 0)
{
continue;
}
movelove[i].cuR++; //字符位置往外移
//一次随机创20颗爱心,创建20次,movelove[i].cuR初始值为100,当它大于120,让它回归最初状态
if (movelove[i].cuR >= 120)
{
memset(&movelove[i], 0, sizeof(struct love));
}
movelove[i].height++; //字符变大
movelove[i].x = movelove[i].cuR * movelove[i].r * cos(movelove[i].angle) + 360;
movelove[i].y = -movelove[i].cuR * movelove[i].r * sin(movelove[i].angle) + 170 ;
}
}
int main()
{
init(); //初始化
while (1)
{
Creatlove(); //创建小爱心
Printlove(); //打印小爱心
Modifylove(); //修改爱心大小和位置
Sleep(30); //间隔30毫秒
}
}