既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
\qquad
这段代码很容易理解,运行这段程序,就会出现逐渐明亮的屏幕。因为不断刷新背景色为
R
G
B
(
i
,
i
,
i
)
RGB(i,i,i)
RGB(i,i,i)。C语言中的颜色使用十六进制表示的,RGB函数可以将0~255范围内的三个整数三原色转换成这个十六进制。
\qquad
cleardevice()函数用于清屏,是界面内所有元素都被清空,一般只会在初始化出现。
\qquad
Sleep()是毫秒级延迟,当然界面变亮时间不一定是准确的15ms×255/5=0.765s,因为其他语句还需要执行时间。
\qquad
closegraph():关闭绘图界面。注意,如果初始化了绘图界面但没有在主函数结束前关闭它,可能会引发一些莫名其妙的错误!所以这个函数一定要有!
2.2.创建按钮
\qquad
我们尝试在界面创建几个按钮,按钮需要的操作是绘制矩形和打印文字。虽然看着简单,但是里面还是有点学问,为了方便大家理解,还是先放上代码和注释。
#include <graphics.h> // 引用图形库头文件
#include <conio.h>
#include <stdio.h>
#include <windows.h> //用到了定时函数sleep()
#include <math.h>
int r1[]={30,20,130,60};//输入按钮的矩形参数
int r2[]={170,20,220,60};//运行按钮的矩形参数
int r3[]={260,20,310,60};//退出按钮的矩形参数
int main()
{
int i;
short win_width,win_height;//定义窗口的宽度和高度
win_width = 480;win_height = 360;
initgraph(win_width,win_height);//初始化窗口(黑屏)
for(i=0;i<256;i+=5)
{
setbkcolor(RGB(i,i,i));//设置背景色,原来默认黑色
cleardevice();//清屏(取决于背景色)
Sleep(15);//延时15ms
}
RECT R1={r1[0],r1[1],r1[2],r1[3]};//矩形指针R1
RECT R2={r2[0],r2[1],r2[2],r2[3]};//矩形指针R2
RECT R3={r3[0],r3[1],r3[2],r3[3]};//矩形指针R3
LOGFONT f;//字体样式指针
gettextstyle(&f); //获取字体样式
\_tcscpy(f.lfFaceName,\_T("宋体")); //设置字体为宋体
f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿
settextstyle(&f); // 设置字体样式
settextcolor(BLACK); //BLACK在graphic.h头文件里面被定义为黑色的颜色常量
drawtext("输入参数",&R1,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R1内输入文字,水平居中,垂直居中,单行显示
drawtext("运行",&R2,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R2内输入文字,水平居中,垂直居中,单行显示
drawtext("退出",&R3,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示
setlinecolor(BLACK);
rectangle(r1[0],r1[1],r1[2],r1[3]);
rectangle(r2[0],r2[1],r2[2],r2[3]);
rectangle(r3[0],r3[1],r3[2],r3[3]);
system("pause");//暂停,为了显示
closegraph();
return 0;
}
\qquad
这里需要特别介绍的是矩形指针
p
R
e
c
t
pRect
pRect,它使用句柄RECT定义,并且不可以中途再次赋值。之所以要设置矩形指针了为了打印字体的时候以矩形为边界自动填充。它的格式是RECT r={X1,Y1,X2,Y2}
,X1和X2是矩形的左边和右边的横坐标,Y1和Y2是矩形的上边和下边的纵坐标,这一点和rectangle()绘制空心矩形函数参数排列一致。后面的DT_CENTER | DT_VCENTER | DT_SINGLELINE
就是描述填充格式的常量。使用drawtext书写文字不需要再计算文字的坐标和设置大小,会方便很多。
\qquad
LOGFONT是字体样式指针,通过gettextstyle()函数来获取当前的字体类型,再通过settextstyle()函数加以设置。这里只修改了字体的名称和显示质量,还可以修改斜体、下划线等属性,更详细的部分请参考帮助文档。
2.3.鼠标操作
2.3.1.单击特效
\qquad
作为一个图形化界面的C程序,鼠标操作总不能少吧。在讲解程序前先别着急,简单为大家科普一下鼠标事件:
\qquad
鼠标是输入设备,只要发生以下的事件,就会暂存在鼠标消息列表中,我们的操作系统就会依次响应列表中的鼠标消息事件,常用的鼠标事件如下:
- WM_MOUSEMOVE——鼠标移动
- WM_MOUSEWHEEL——鼠标滚轮滚动
- WM_LBUTTONDOWN——鼠标左键按下
- WM_LBUTTONUP——鼠标左键弹起
- WM_LBUTTONDBLCLK——鼠标左键双击
- WM_RBUTTONDOWN——鼠标右键按下
- WM_RBUTTONUP——鼠标右键弹起
- WM_RBUTTONDBLCLK——鼠标左键双击
- WM_MBUTTONDOWN——鼠标中键按下
- WM_MBUTTONUP——鼠标中键弹起
- WM_MBUTTONDBLCLK——鼠标中键双击
\qquad
我们只需要根据不断获取鼠标消息队列的消息并根据消息依次进行响应即可。
\qquad
相信大家已经迫不及待了,那么请看下面一个简单的程序。
#include <graphics.h> // 引用图形库头文件
#include <conio.h>
#include <stdio.h>
#include <windows.h> //用到了定时函数sleep()
#include <math.h>
int r1[]={30,20,130,60};//输入按钮的矩形参数
int r2[]={170,20,220,60};//运行按钮的矩形参数
int r3[]={260,20,310,60};//退出按钮的矩形参数
int main()
{
int i;
short win_width,win_height;//定义窗口的宽度和高度
win_width = 480;win_height = 360;
initgraph(win_width,win_height);//初始化窗口(黑屏)
for(i=0;i<256;i+=5)
{
setbkcolor(RGB(i,i,i));//设置背景色,原来默认黑色
cleardevice();//清屏(取决于背景色)
Sleep(15);//延时15ms
}
RECT R1={r1[0],r1[1],r1[2],r1[3]};//按钮1的矩形区域
RECT R2={r2[0],r2[1],r2[2],r2[3]};//按钮2的矩形区域
RECT R3={r3[0],r3[1],r3[2],r3[3]};//按钮2的矩形区域
LOGFONT f;
gettextstyle(&f); //获取字体样式
\_tcscpy(f.lfFaceName,\_T("宋体")); //设置字体为宋体
f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿
settextstyle(&f); // 设置字体样式
settextcolor(BLACK); //BLACK在graphic.h头文件里面被定义为黑色的颜色常量
drawtext("输入参数",&R1,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R1内输入文字,水平居中,垂直居中,单行显示
drawtext("运行",&R2,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R2内输入文字,水平居中,垂直居中,单行显示
drawtext("退出",&R3,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示
setlinecolor(BLACK);
rectangle(r1[0],r1[1],r1[2],r1[3]);
rectangle(r2[0],r2[1],r2[2],r2[3]);
rectangle(r3[0],r3[1],r3[2],r3[3]);
MOUSEMSG m;//鼠标指针
setrop2(R2_NOTXORPEN);//二元光栅——NOT(屏幕颜色 XOR 当前颜色)
while(true)
{
m = GetMouseMsg();//获取一条鼠标消息
if(m.uMsg==WM_LBUTTONDOWN)
{
for(i=0;i<=10;i++)
{
setlinecolor(RGB(25\*i,25\*i,25\*i));//设置圆颜色
circle(m.x,m.y,2\*i);
Sleep(25);//停顿2ms
circle(m.x,m.y,2\*i);//抹去刚刚画的圆
}
FlushMouseMsgBuff();//清空鼠标消息缓存区
}
}
system("pause");//暂停,为了显示
closegraph();
return 0;
}
\qquad
每点击鼠标以下,应该可以看到鼠标点击处有一个逐渐扩大并淡出的圆(截图无法清晰,在画面的中右侧),当循环体内Sleep的视觉大于20ms后视觉效果很强。
\qquad
每响应一次鼠标左键单击事件,都会调用一次清空鼠标消息缓存区的函数FlushMouseMsgBuff()
,如果没有这个函数会怎么样呢?如果我们快速连续地单击鼠标左键N次,那么特效就会播放N次,如果特效播放速度比单击的速度慢,那么即使你停下来了,程序仍然会接着播放单击特效,因为你的左键单击仍然在鼠标的消息队列m.uMsg
中的鼠标消息没有响应完。
\qquad
这里需要解释的是一个二元光栅设置函数setrop2()
,二元光栅是混合背景色和当前颜色的模式。我们这里采用的方式是同或(NOT XOR)的方式,若底色为白色(1),则当前颜色不变;若底色是黑色(0),则当前颜色反色。为什么需要采用这种方式呢?因为我们在第二次抹去原来的圆的时候不能采用白色,否则如果背景色原来就为黑(比如按钮和文字),就也会被抹成白色。而背景色与任意一个颜色同或两次都为其本身,即可起到还原背景色的效果。这里的背景色与cleardevice()前面那个背景色不同,这里的是指执行这一条绘画指令之前屏幕上的颜色。
2.3.2.光标感应
\qquad
我们希望鼠标移到按钮上时按钮会有所变化,移开按钮时又会回到原样。这里我们采用一种简单的填充颜色的方法,就是按钮变色。我们需要解决一个问题就是按钮变色了但是按钮的文字不能被覆盖,那么我们还是需要使用到二元光栅。只是我们这次的模式改成了同或。
\qquad
为了方便起见,存放三个按钮的数组我们合并为了一个二维数组,在鼠标事件中更容易使用和分配任务。
#include <graphics.h> // 引用图形库头文件
#include <conio.h>
#include <stdio.h>
#include <windows.h> //用到了定时函数sleep()
#include <math.h>
int r[3][4]={{30,20,130,60},{170,20,220,60},{260,20,310,60}};//三个按钮的二维数组
int button\_judge(int x,int y)
{
if(x>r[0][0] && x<r[0][2] && y>r[0][1] && y<r[0][3])return 1;
if(x>r[1][0] && x<r[1][2] && y>r[1][1] && y<r[1][3])return 2;
if(x>r[2][0] && x<r[2][2] && y>r[2][1] && y<r[2][3])return 3;
return 0;
}
int main()
{
int i,event=0;
short win_width,win_height;//定义窗口的宽度和高度
win_width = 480;win_height = 360;
initgraph(win_width,win_height);//初始化窗口(黑屏)
for(i=0;i<256;i+=5)
{
setbkcolor(RGB(i,i,i));//设置背景色,原来默认黑色
cleardevice();//清屏(取决于背景色)
Sleep(15);//延时15ms
}
RECT R1={r[0][0],r[0][1],r[0][2],r[0][3]};
RECT R2={r[1][0],r[1][1],r[1][2],r[1][3]};
RECT R3={r[2][0],r[2][1],r[2][2],r[2][3]};
LOGFONT f;
gettextstyle(&f); //获取字体样式
\_tcscpy(f.lfFaceName,\_T("宋体")); //设置字体为宋体
f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿
settextstyle(&f); // 设置字体样式
settextcolor(BLACK); //BLACK在graphic.h头文件里面被定义为黑色的颜色常量
drawtext("输入参数",&R1,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R1内输入文字,水平居中,垂直居中,单行显示
drawtext("运行",&R2,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R2内输入文字,水平居中,垂直居中,单行显示
drawtext("退出",&R3,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示
setlinecolor(BLACK);
rectangle(r[0][0],r[0][1],r[0][2],r[0][3]);
rectangle(r[1][0],r[1][1],r[1][2],r[1][3]);
rectangle(r[2][0],r[2][1],r[2][2],r[2][3]);
MOUSEMSG m;//鼠标指针
while(true)
{
m = GetMouseMsg();//获取一条鼠标消息
switch(m.uMsg)
{
case WM_MOUSEMOVE:
setrop2(R2_XORPEN);
setlinecolor(LIGHTCYAN);//线条颜色为亮青色
setlinestyle(PS_SOLID, 3);//设置画线样式为实现,10磅
setfillcolor(WHITE);//填充颜色为白色
if(button\_judge(m.x,m.y)!=0)
{
if(event != button\_judge(m.x,m.y))
{
event = button\_judge(m.x,m.y);//记录这一次触发的按钮
fillrectangle(r[event-1][0],r[event-1][1],r[event-1][2],r[event-1][3]);//有框填充矩形(X1,Y1,X2,Y2)
}
}
else
{
if(event != 0)//上次触发的按钮未被修正为原来的颜色
{
fillrectangle(r[event-1][0],r[event-1][1],r[event-1][2],r[event-1][3]);//两次同或为原来颜色
event = 0;
}
}
break;
case WM_LBUTTONDOWN:
setrop2(R2_NOTXORPEN);//二元光栅——NOT(屏幕颜色 XOR 当前颜色)
for(i=0;i<=10;i++)
{
setlinecolor(RGB(25\*i,25\*i,25\*i));//设置圆颜色
circle(m.x,m.y,2\*i);
Sleep(30);//停顿30ms
circle(m.x,m.y,2\*i);//抹去刚刚画的圆
}
break;
FlushMouseMsgBuff();//清空鼠标消息缓存区
}
}
system("pause");//暂停,为了显示
return 0;
}
\qquad
这里我们运用了两次设置二元光栅的函数setrop2,在鼠标的移动条件内(case MOUSEMOVE
),我们使用的是屏幕颜色和当前颜色异或,这是为什么呢?因为fillrectangle()
函数是画一个有框填充矩形,我们让这个有框填充矩形和原按钮的一样大。由于线条的颜色为亮青色,填充颜色为白色(1),白色的填充颜色和屏幕颜色异或,取的是屏幕颜色的反色。按钮的边框是黑色(0),它与亮青色异或,则会保留原来的亮青色。
\qquad
与同或一样,异或两次等于没有执行操作,所以可以还原到原屏幕画布的颜色。
2.3.3.进度条
\qquad
既然涉及到进度条了,那么就应该涉及到正式程序了,这里我不想涉及太多的专业知识影响大家理解,但又不能让程序运行得太快以至于看不出进度条的变化,所以我设计了一个简单的弹性球轨迹作图程序。
\qquad
假设球半径为R,初始高度为
h
0
h_0
h0,初速度为0(自由落体),非弹性碰撞时能量损失率为
α
\alpha
α。计算部分子函数如下:
int simulation()
{
float dt = 0.01;//仿真间隔10ms
long int N = (long int)(sim_t/dt);//迭代次数
float \*h=(float\*)calloc(N,sizeof(float));//高度
float \*v=(float\*)calloc(N,sizeof(float));//速度(竖直方向)
long int i;//迭代变量
for(i=1;i<N;i++)
{
if(h[i-1]>R)//未发生碰撞
{
v[i]=v[i-1]-9.8\*dt;//速度计算
}
else//发生碰撞,动能损失alpha,速度损失alpha的开方
{
v[i]=-sqrt(alpha)\*v[i-1];
}
}
free(h);
free(v);//释放内存
return 0;
}
\qquad
当然,我们还需要绘图网格,定义绘图网格的函数如下:
void init\_figure()
{
int i;
setrop2(R2_COPYPEN);//当前颜色
setlinecolor(BLACK);
setlinestyle(PS_SOLID);//实线
rectangle(30,100,420,330);//外框线
setlinestyle(PS_DOT);//点线
for(i=30+39;i<420;i+=39)
{
line(i,100,i,330);//竖直辅助线
}
for(i=100+23;i<330;i+=23)
{
line(30,i,420,i);//水平辅助线
}
}
\qquad
注意,我们使用了rectangle()空心矩形函数绘制网格外框架,使用了line函数依次画出了辅助线。
\qquad
我们现在的目标就是将h的坐标转换到网格上去,绘制出球心的轨迹,这样似乎并不复杂,只需要对simulation()函数稍加修改即可。
int simulation()
{
float dt = 0.01;//仿真间隔10ms
float dy = 230/h0;//单位纵坐标
long int N = (long int)(sim_t/dt);//迭代次数
float \*h=(float\*)calloc(N,sizeof(float));//高度
float \*v=(float\*)calloc(N,sizeof(float));//速度(竖直方向)
long int i;//迭代变量
float process_duty;//进度
init\_figure();//初始化图像网格
setrop2(R2_COPYPEN);//当前颜色
//计算步骤
h[0]=h0;v[0]=0;
for(i=1;i<N;i++)
{
if(h[i-1]>R)//未发生碰撞
{
v[i]=v[i-1]-9.8\*dt;//速度计算
}
else//发生碰撞,动能损失alpha,速度损失alpha的开方
{
v[i]=-sqrt(alpha)\*v[i-1];
}
h[i]=h[i-1]+v[i]\*dt;//高度计算
process_duty = (i+1)/(float)(N);
putpixel(30+(int)(process_duty\*390),330-(int)(h[i]\*dy),RED);//画点putpixel(X,Y,color\*)
Sleep(dt\*1000);//延时
}
free(h);
free(v);
return 0;
}
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
0;
for(i=1;i<N;i++)
{
if(h[i-1]>R)//未发生碰撞
{
v[i]=v[i-1]-9.8*dt;//速度计算
}
else//发生碰撞,动能损失alpha,速度损失alpha的开方
{
v[i]=-sqrt(alpha)*v[i-1];
}
h[i]=h[i-1]+v[i]*dt;//高度计算
process_duty = (i+1)/(float)(N);
putpixel(30+(int)(process_duty*390),330-(int)(h[i]*dy),RED);//画点putpixel(X,Y,color*)
Sleep(dt*1000);//延时
}
free(h);
free(v);
return 0;
}
[外链图片转存中...(img-iJDpQzqZ-1715624735564)]
[外链图片转存中...(img-sOtya6qo-1715624735564)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**