本文的封面第三章照片来自博客:爱心气球
本文章所使用到的资源分享:(均是免费,白嫖万岁)
EasyX
是针对 C++ 的图形库,可以帮助 C/C++ 初学者快速上手图形和游戏编程。虽然是C++图形库,但是C和C++不分家,只要你学了C语言,或者一门编程语言,那么学习成本都不会很高,学习门槛很低。它是可以帮助我们去实现图形化,优美化的小游戏的工具,例如:贪吃蛇,俄罗斯方块,扫雷等等。
大部分学校只交基础语法,一直都在黑窗口(也就是我们平时所说的控制台)上练习,同学们都学习很乏味。于是博主给大家出了一个
easyx
开发软件的教程哦~快乐学编程
操作系统:Windows XP(sp3) 及以上操作系统。
编译环境:Visual C++ 6.0,Visual Studio 2008 至 Visual Studio 2022 (x86 & x64)。
大家不要拿着devc++,gcc去下个EasyX图形库然后跟博主说我的用不了咧
安装网址(<—点这里)安装过程很简单,跟着指导来即可,而且不需要考虑会不会安装位置不对,在安装的时候它会找到你的vs的安装文件对应的位置进行安装,由于博主的vs版本是2013和2019,所以我安装的也是13和19的(两个都安装了),不出意义的话大家是可以去下载vs2022版本的EasyX的。
特别注意:大家平时写程序怎么创建项目,就怎么创建项目即可,但是注意我们在使用EasyX的时候,源文件后缀要是.cpp
总所周知,所有的颜色都可以由三原色构成 :
简称RGB
,也就是说只要有这三种颜色,那么我们可以通过改变这三种颜色的浓度值,即可构成不同的颜色
例如上述画板选中的颜色的值可以表示成RGB(128,128,192)
其中R G B的值的范围都是0~255
没有小看大家,我们在数学中就已经学过平面直角坐标系了,如下
而当我们使用EasyX
创建的窗口的纵轴方向和上图中的是相反的。如下:
我们在使用EasyX
的时候都是去调用图形库里面的接口,说白了就是学习它里面的函数怎么使用。
涉及接口:
HWND initgraph(int width, int height, int flag = 0);//创建窗口void closegraph();//关闭窗口
//参数1决定窗口的宽度,参数2决定窗口的高度,参数三决定窗口的模式参数3:
#define SHOWCONSOLE 1 // 显示控制台
#define NOCLOSE 2 // 不可关闭,就是窗口的❎不见了
#define NOMINIMIZE 4 // 不可最小化,就是窗口的➖不见了
代码演示1:
#include//记得加上这个头文件#includeint main()
{initgraph(1024, 480,SHOWCONSOLE);getchar();//防止窗口一闪而过,我们让其等待输出停一会儿closegraph();//关闭窗口return 0;
}
在我们创建的窗口中,宽度和高度的单位是:像素点,当然了,我们要看完整的函数声明,是可以对函数名进行,右键 > 转到定义去查看的
大家可以将试一试这几个参数的效果,查看更多参数可以右键>转到定义,就可以看到更到的宏参数了
上面的三个参数是可以同时放进去的,就像这样initgraph(1024, 480,SHOWCONSOLE | NOCLOSE | NOMINIMIZE);
再次温馨提示,源文件必须是.cpp
文件
没错上面的窗口就是我们创建出来的窗口,我们发现它的窗口名不再是对应的项目路径,而且右侧已经不再是滚动条,说明此时它已经不是控制台窗口了,没错这个现在看起来还是很low,下面我们就自己设计一下自己的窗口
咱们先将可以设置颜色的接口介绍一遍,后面都会使用到
使用到的接口
void setbkcolor(COLORREF color);//设置背景颜色,参数填入颜色RGB值void cleardevice(); //清空设备
当这两个接收一起使用时,才能达到效果
void setlinecolor(COLORREF color);
void settextcolor(COLORREF color);
void setfillcolor(COLORREF color);
咱们先将可以设置样式的接口介绍一遍,后面都会使用到
可以设置成是背景是填充还是覆盖
void setbkmode(int mode);//设置背景模式参数:
#define TRANSPARENT 1 //透明的
#define OPAQUE 2 //覆盖的默认不设置的情况下是覆盖的的
void setlinestyle(int style, int thickness = 1, const DWORD *puserstyle = NULL, DWORD userstylecount = 0);//第一个参数是设置线条的样式 -- 实线,虚线等
参数1:
#define PS_SOLID 0
#define PS_DASH 1 /* ------- */
#define PS_DOT 2 /* ....... */
#define PS_DASHDOT 3 /* _._._._ */
#define PS_DASHDOTDOT 4 /* _.._.._ *///参数2是设置线条的宽度,单位是像素点第三个参数,第四个参数大家可以不填
void settextstyle(int nHeight, int nWidth, LPCTSTR lpszFace);//参数1:文本高度
//参数2:文本宽度
//参数3:文本的字体 -- 大家可以理解成就是填一个const char*的参数参数三:
1. "宋体"
2. "楷体"
很多很多,大家可以试一试word里面的那些字体哪些可以用
⚠:千万注意,千万注意,千万注意,很多同学在使用文本接口的时候经常报错,这是应为需要修改项目属性,我们要将使用 Unicode 字符集 — 改成 使用多字节字符集
你在画板中可以画的图案,几乎都可以做到,下面我们已画圆举例
使用的接口:
void circle (int x, int y, int radius); //画一个由线条边框组成,无填充的圆
void fillcircle (int x, int y, int radius); // 画一个有线条边框,有填充的圆
void solidcircle(int x, int y, int radius); //画一个无线条边框,有填充的圆
x y–即圆的坐标,radius – 即圆的半径
代码举例2:
#include#include//1.设置颜色
void set_color()
{//1.设置背景颜色,要和cleardevice一起使用setbkcolor(RGB(204, 232, 207));//将背景设置成浅绿色cleardevice();//2.设置线条颜色setlinecolor(RGB(128, 255, 255));//将线条颜色设置成天蓝色//3.设置填充颜色setfillcolor(RGB(255, 201, 14));//设置填充颜色为黄色//4.设置文本颜色settextcolor(RGB(245, 245, 245));//文本颜色设置成白色
}//2.设置样式
void set_mode()
{//1.将背景设置成透明setbkmode(TRANSPARENT);//2.将线条设置成3个像素点宽度setlinestyle(PS_SOLID, 3);//3.将文本高度设置成20,宽度设置成10,字体设置成楷体settextstyle(20, 10, "楷体");
}//3.绘制图案
void draw_picture()
{circle(50,50,50);//在(50,50)的位置画一个圆fillcircle(150, 50, 50);solidcircle(250, 50, 50);
}int main()
{initgraph(1024, 480,SHOWCONSOLE);//显示控制台set_color();//设置颜色draw_picture();//绘制图形getchar();closegraph();return 0;
}
⚠再次提示,上述代码跑不过的同学,去检查一下项目属性,有没有将使用 Unicode 字符集 — 改成 使用多字节字符集
那么如何获取一个颜色的RGB
值呢?温馨小科普:在我们登录qq
之后
快去动手试试吧。
绘制各种图形也是大同小异,大家可以查看这个图形的接口文档来绘制自己喜欢的图形:图形文档(点击这里哟)
这里我们使用的接口是非easyx
库函数,而是头文件#include
的函数,大家记得包含。
接口介绍
void outtextxy(int x, int y, LPCTSTR str);大家可以将LPCTSTR类型理解成char*类型
为了节省空间,上述写过的set_color()
,set_mode()
,我们就在不放在代码块里面,大家自行引用哈(在画圆示例里面已经写了)
代码示例3
#include#include#include//1.//2.//3.绘制图案
void draw_picture()
{circle(50,50,50);fillcircle(150, 50, 50);solidcircle(250, 50, 50);
}//4.文本输出
void textout()
{outtextxy(50, 50, "大家好,我叫彭于晏");
}int main()
{initgraph(1024, 480,SHOWCONSOLE);//显示控制台set_color();set_mode();draw_picture();textout();getchar();closegraph();return 0;
}
效果:
使用接口:
int textwidth(LPCTSTR str); // Get the width of a stringint textheight(LPCTSTR str); // Get the height of a string想必大家都看的懂这几句英文,单位还是像素点
原理介绍:
#include#include#include//1.//2.//3.绘制图像
void draw_picture()
{circle(50,50,50);fillcircle(150, 50, 50);solidcircle(250, 50, 50);rectangle(50, 150, 350, 250);//300*100的矩形
}//4.文本输出
void textout()
{int x = 0;int y = 0;char str[] = "大家好,我叫彭于晏";x = 50 + (300 - textwidth(str)) / 2;y = 150 + (100 - textheight(str)) / 2;outtextxy(x, y, str);
}int main()
{initgraph(1024, 480,SHOWCONSOLE);//显示控制台set_color();set_mode();draw_picture();textout();getchar();closegraph();return 0;
}
效果:
这样就可以自动居中了,非常方便
需求:我们要将下面的动图加载到内存中,并且输出在我们的窗口上面。
首先我们要将这张照片放到,当前项目的路径底下,与.cpp文件是一个路径,然后我们给他进行一个重命名叫么么么.jpg
使用的接口和类:
IMAGE //大家可以理解成,描述图片的结构体,我们可以用来定义对象来描述图片void loadimage(IMAGE *pDstImg, LPCTSTR pImgFile, int nWidth = 0, int nHeight = 0, bool bResize = false);
//加载图片进内存中,建立图片对象和图片的练习
//就像c语言的文件和文件指针一样,用来控制文件,操作文件//参数1:图片对象指针
//参数2:图片路径
//参数3:图片宽度(可不填)
//参数4:图片高度(可不填)
//参数5可不填void putimage(int dstX, int dstY, const IMAGE *pSrcImg, DWORD dwRop = SRCCOPY);
//输出图片
//参数1,2:输出图片的坐标
//参数3:图片对象指针//参数4可不填
代码示例:
为了节省空间,上述的1.set_color()
,2.set_mode()
,3.draw_picture()
,4.textout()
我们就不放在代码块里面了,大家如果跟着动手也应该把函数放好了
#include#include#include//1.//2.//3.//4.//5.图片输出
void test_photo()
{IMAGE img;loadimage(&img, "./么么么.gif");putimage(450, 200, &img);
}int main()
{initgraph(1024, 480,SHOWCONSOLE);//显示控制台set_color();set_mode();draw_picture();//绘制图形textout();//文本输出test_photo();//图片输出getchar();closegraph();return 0;
}
效果:
不过没有输出动图的效果,有点可惜
需求:当我们使用左键点击文本框区域时,控制台窗口会输出左键被激活,当我们使用右键点击文本区域时,控制台窗口会输出右键被激活。
涉及接口和类:
ExMessage//描述接收信息的结构体,可以用来创建鼠标,键盘等对象//以鼠标举例bool peekmessage(ExMessage *msg, BYTE filter = -1, bool removemsg = true);
//接收鼠标消息 -- 看是否有鼠标消息//参数1 , 对象指针
//参数2 , 对象类型
参数2参数选择:
#define EX_MOUSE 1 //鼠标
#define EX_KEY 2 //键盘
#define EX_CHAR 4
#define EX_WINDOW 8mouse的成员变量:1.
message//鼠标信息 -- 左键还是右键还是移动还是滚轮有如下信息:
#define WM_MOUSEMOVE 0x0200//鼠标移动
#define WM_LBUTTONDOWN 0x0201//左键点击
#define WM_LBUTTONUP 0x0202//左键弹起
#define WM_LBUTTONDBLCLK 0x0203//左键双击
#define WM_RBUTTONDOWN 0x0204//右键点击
#define WM_RBUTTONUP 0x0205//右键弹起
#define WM_RBUTTONDBLCLK 0x0206//右键双击
#define WM_MBUTTONDOWN 0x0207//中键点击
#define WM_MBUTTONUP 0x0208//中键弹起
#define WM_MBUTTONDBLCLK 0x0209//中间双击2.
struct
{short x; // 横坐标short y; // 纵坐标
};
上代码:
#include#include#include//1.//2.//3.//4.//5//6.鼠标操作
void MouseButton()
{ExMessage mouse;//创建鼠标对象while (true){if (peekmessage(&mouse, EX_MOUSE))//接收鼠标信息{if (mouse.x >= 50 && mouse.x <= 350 && mouse.y >= 150 && mouse.y <= 250)//点击矩形区域内{switch (mouse.message)//鼠标信息发布{case WM_LBUTTONDOWN:printf("我是左键,我被激活了\n");break;case WM_RBUTTONDOWN:printf("我是右键,我被激活了\n");break;case WM_MOUSEMOVE:// 鼠标移动的时候画红色的小点putpixel(mouse.x, mouse.y, RGB(237, 28, 36));break;case WM_MOUSEWHEEL:printf("滚轮滚动\n");break;}}}}
}//用来测试
//基本框架int main()
{//initgraph(1024, 480);initgraph(1024, 480,SHOWCONSOLE);//显示控制台//initgraph(1024, 480, SHOWCONSOLE | NOCLOSE | NOMINIMIZE);//显示控制台//initgraph(1024, 480, EW_DBLCLKS);//显示控制台set_color();set_mode();draw_picture();//绘制图形textout();//文本输出test_photo();//图片输出MouseButton();//鼠标操作getchar();closegraph();return 0;
}
效果演示:
除了上述演示的左键,右键,大家也可以仿照着函数模式(接口使用方式上述已经介绍),加入不同的宏参数,达到不同的效果,例如:左键双击,左键弹起,学会举一反三哟!!!
需求:画出一个小球,现在使用键盘的上下左右键,或者wasd键去控制它的上下左右移动
####9.1平动
设计的接口和类
bool kbhit(void);//判断键盘是否输入char _getch();//接收键盘按下的按键的值,如果是字符就是ASCII码
其中上下左右键对应的值分别是:
//⬆ 72 -- 上键
//⬇ 80 -- 下键
//⬅ 75 -- 左键
//➡ 77 -- 右键//为了方便大家理解,我把这个接口的类型修改了一下,具体是什么大家可以转到定义去看
注意进行鼠标操作,键盘操作时,我们都是要在循环中进行的,因为接口函数在等待着你的鼠标,键盘输入消息,如果不再循环中进行操作,那么在第一个0.0000001秒时,你没有输入消息,这些接收信息接口就过去了,我们需要重复调用这些接口。
#include#include#include//1.//2.//3.//4.//5.//6.//7.键盘操作
void key_oper()
{//1.画一个半径50的圆,起始位置是(0,0)int x = 0;int y = 0;int r = 50;setbkmode(TRANSPARENT);while (true){fillcircle(x, y, 50);if (kbhit())//判断有没有键盘按下{char key = _getch();//阻塞函数,等待键盘的输入//printf("%d -> %c\n", key, key);switch (key){case 72:case 'w':case 'W':printf("上键操作\n");y -= 3;break;case 80:case 's':case 'S':printf("下键操作\n");y += 3;break;case 75:case 'a':case 'A':printf("左键操作\n");x -= 3;break;case 77:case 'd':case 'D':printf("右键操作\n");x += 3;break;}}}
}int main()
{initgraph(1024, 480,SHOWCONSOLE);//显示控制台set_color();//设置颜色set_mode();//设置样式draw_picture();//绘制图形textout();//文本输出test_photo();//图片输出//MouseButton();//鼠标操作key_oper();//键盘操作getchar();closegraph();return 0;
}
为了演示出键盘操作的效果,我们将鼠标操作的函数关闭掉,因为不关闭会一直卡在鼠标操作的那个死循环里面。
效果演示:
效果虽然演示出来了,但是小球走过的区域都会被覆盖成蓝色,明明背景样式已经设置成透明了,为什么呢?其实是因为线条的颜色给走过的区域覆盖成了一层蓝色。为了让样条颜色不对走过区域进行覆盖,我们让循环每进行一次,我们都重新打印一遍我们的图形,照片。这样就能达到我们想要的效果!!!
代码改装:
#include#include#include//1.//2.//3.//4.//5.//6.//7.键盘操作
void key_oper()
{//1.画一个半径50的圆,起始位置是(0,0)int x = 0;int y = 0;int r = 50;setbkmode(TRANSPARENT);while (true){BeginBatchDraw();//我们将这些操作放在了循环中set_color();//设置颜色set_mode();//设置样式draw_picture();//绘制图形textout();//文本输出test_photo();//图片输出fillcircle(x, y, 50);FlushBatchDraw();if (kbhit())//判断有没有键盘按下{char key = _getch();//阻塞函数,等待键盘的输入//printf("%d -> %c\n", key, key);switch (key){case 72:case 'w':case 'W':printf("上键操作\n");y -= 3;break;case 80:case 's':case 'S':printf("下键操作\n");y += 3;break;case 75:case 'a':case 'A':printf("左键操作\n");x -= 3;break;case 77:case 'd':case 'D':printf("右键操作\n");x += 3;break;}}}
}int main()
{initgraph(1024, 480,SHOWCONSOLE);//显示控制台//MouseButton();//鼠标操作key_oper();//键盘操作getchar();closegraph();return 0;
}
BeginBatchDraw()``FlushBatchDraw()
是防止 屏幕频闪,下方就讲解了原理。
效果演示:(wasd + 上下左右键)均支持
涉及接口:
void BeginBatchDraw(); // Begin batch drawing modevoid FlushBatchDraw(); // Refreshes the undisplayed drawing
使用方法:
在程序的绘制图案的操作之前加上BeginBatchDraw();
在程序的绘制图案的操作之前加上FlushBatchDraw();
原理:
造成频闪的原因是:因为绘制一次图案就进行一次清屏,在这两个操作之间进行频繁切换就会看到频闪,即使计算机运行速度很快,我们还是很容易看到其中的频闪。
大家可以理解成一个小屁孩拿着一个手电筒在你面前来回的晃,你会觉得很闪,即使它晃得速度很快。
解决方法:
进行多次绘制图案操作,进行一次清屏,减少切换的频率。而BeginBatchDraw()
就会在我们进行输入操作之后,将这些输入信息先放到缓冲区,遇到FlushBatchDraw()
再刷新到窗口上。
大家可以理解成一个小屁孩拿着一个手电筒在你面前找了很久,才进行一次晃动,这样闪的频率就会大大缩短。
涉及接口:
bool GetAsyncKeyState(int vKey);
//为了方便大家理解,类型修改了以下,完整版大家可以转到定义查看参数3选择:(参数太多,介绍几个常有的)
#define VK_LEFT 0x25//←键
#define VK_UP 0x26//↑键
#define VK_RIGHT 0x27//→键
#define VK_DOWN 0x28//↓键
#define VK_SPACE 0x20//空格键当然了还有以下这些,大家都可以试一试
VK_SHIFT Shift键
VK_LSHIFT 左Shift键
VK_RSHIFT 右Shift键
VK_CONTROL Ctrl键
VK_LCONTROL 左Ctrl键
VK_RCONTROL 右Ctril键
VK_MENU Alt键
VK_LMENU 左Alt键
VK_RMENU 右Alt键
VK_LBUTTON 鼠标左键
VK_RBUTTON 鼠标右键
VK_ESCAPE ESC键
VK_RETURN回车键
#include#include#include//1.//2.//3.//4.//5.//6.//7.键盘操作
void key_oper()
{//1.画一个半径50的圆,起始位置是(0,0)int x = 0;int y = 0;int r = 50;setbkmode(TRANSPARENT);while (true){BeginBatchDraw();set_color();//设置颜色set_mode();//设置样式draw_picture();//绘制图形textout();//文本输出test_photo();//图片输出fillcircle(x, y, 50);FlushBatchDraw();//平动+斜动(丝滑版本)if (GetAsyncKeyState(VK_UP))//上键{y -= 3;}if (GetAsyncKeyState(VK_DOWN))//下键{y += 3;}if (GetAsyncKeyState(VK_LEFT))//左键{x -= 3;}if (GetAsyncKeyState(VK_RIGHT))//右键{x += 3;}}
}//用来测试
//基本框架int main()
{initgraph(1024, 480,SHOWCONSOLE);//显示控制台//MouseButton();//鼠标操作key_oper();//键盘操作getchar();closegraph();return 0;
}
效果演示: – 非常地丝滑
包含头文件 + 导入静态库
#include//包含的头文件,包含在graphics.h头文件下面#pragma comment(lib,"winmm.lib")//导入静态库
放一些音乐文件在当前项目路径底下(不一定所有的文件类型都可以),在图片输出的时候已经讲过怎么找当前项目路径,博主放了一首IU的歌。
涉及接口:
mciSendString(open "文件路径",0,0,0);//打开音乐mciSendString(open "文件路径",0,0,0);//打开音乐mciSendString(open "文件路径",0,0,0);//打开音乐
//可以修改路径名,下面有介绍
代码实操
#include#include
#include//包含的头文件,包含在graphics.h头文件下面#include
#pragma comment(lib,"winmm.lib")//导入静态库//1.//2.//3.//4.//5.//6.//7.//8.音乐播放
void BGM()
{mciSendString("open ./Blueming-IU.mp3 alias IU", 0, 0, 0);//打开音乐,并对文件路径进行了重命名mciSendString("play IU", 0, 0, 0);//播放音乐//现学现用的键盘操作while (true){if (GetAsyncKeyState(VK_SPACE))//空格关闭音乐{mciSendString("close IU", 0, 0, 0);break;}}}int main()
{initgraph(1024, 480,SHOWCONSOLE);//显示控制台//MouseButton();//鼠标操作//key_oper();//键盘操作BGM();getchar();closegraph();return 0;
}
记得把鼠标操作,键盘操作都给关闭
在音乐放起的那一刻,整个DNA都动起来了,赶紧去动手试试吧,给自己的程序加上音乐
涉及的接口和类:
句柄大家可以理解成是描述窗口的对象。用来建立与窗口之间的联系
HWND // 描述句柄的结构体HWND GetHWnd();//获取窗口句柄,并返回SetWindowText(HWND& hd,const char* name);//设置窗口的标题
参数1:句柄对象
参数2:标题名字
这里只是为了方便大家理解,做了一定修改,大家可以转到定义去看完整版,是一个宏int MessageBox((HWND& hd,const char* name1,const char* name2,int val);
参数1:NULL or 句柄对象
参数2和参数3都是是文本名 ,如下面窗口:
参数4决定窗口的效果,选择有: #define MB_OK 0x00000000L
#define MB_OKCANCEL 0x00000001L
#define MB_ABORTRETRYIGNORE 0x00000002L
#define MB_YESNOCANCEL 0x00000003L
#define MB_YESNO 0x00000004L
#define MB_RETRYCANCEL 0x00000005L 下面窗口的参数4就是 MB_OKCANCEL 返回值:鼠标点击的按钮值,帮助我们进行点击之后的操作,返回值有
#define IDOK 1
#define IDCANCEL 2
#define IDABORT 3
#define IDRETRY 4
#define IDIGNORE 5
#define IDYES 6
#define IDNO 7
代码演示:
#include#include
#include//包含的头文件,包含在graphics.h头文件下面#include
#pragma comment(lib,"winmm.lib")//导入静态库//1.//2.//3.//4.//5.//6.//7.//8.//9.弹出消息框
void Hatch()//Hatch是窗口的意思
{HWND hd = GetHWnd();//获取窗口的句柄SetWindowText(NULL, "贪吃蛇小游戏");//修改窗口的文本(名字)//int mbok = MessageBox(hd, "是否要再玩一局", "提示", MB_OKCANCEL);//弹出消息窗口int mbok = MessageBox(NULL, "是否要再玩一局", "提示", MB_OKCANCEL);//第一个参数决定,点击窗口是否有顺序if (mbok == IDOK){printf("再玩一局\n");}else if (mbok == IDCANCEL){printf("退出游戏\n");}
}int main()
{initgraph(1024, 480,SHOWCONSOLE);//显示控制台//MouseButton();//鼠标操作//key_oper();//键盘操作//BGM();Hatch();getchar();closegraph();return 0;
}
大家仔细看看,窗口名字也变成了贪吃蛇小游戏
int mbok = MessageBox(NULL, "是否要再玩一局", "提示", MB_OKCANCEL);
该接口的第一个参数决定了点击窗口的顺序,如果是NULL,那么窗口是可以任意点击的,如果是hd,那么只能优先点击消息弹出窗口。
如果你坚持看到这里来了,并且按照上面进行动手操作了
咱们下期见啦!!!