EasyX入门笔记
基于EasyX的C++图形化界面实现
什么是EasyX?
EasyX 是针对 C++ 的图形库,可以帮助 C/C++ 初学者快速上手图形和游戏编程。
比如,可以基于 EasyX 图形库很快的用几何图形画一个房子,或者一辆移动的小车,可以编写俄罗斯方块、贪吃蛇、黑白棋等小游戏,可以练习图形学的各种算法,等等。
EasyX将windows下的复杂程序进行封装,开发者无需调用底层的windows api即可实现各种图形功能。
安装EasyX
[官网地址](EasyX Graphics Library for C++)
[官网下载地址](下载 EasyX)
EasyX 安装程序是用 7-Zip 封装的自解压缩包程序。
EasyX原生支持windows系统下的Visual C++ 6.0以及Visual Studio 2008以上的版本
若使用上述ide,安装时只需要跟随安装程序的指引安装即可
若使用其他ide,可以将安装文件解压,再根据下面的文件列表说明,将解压后的相关文件分别拷贝到对应的 include 和 lib 文件夹内。
EasyX 安装程序
├ include <folder>
│ ├ easyx.h // 头文件(提供了当前最新版本的接口)
│ └ graphics.h // 头文件(在 easyx.h 的基础上,保留了若干旧接口)
├ lib <folder>
│ ├ VC6 <folder>
│ │ └ x86 <folder>
│ │ ├ EasyXa.lib // VC6 库文件(MBCS 版本)
│ │ └ EasyXw.lib // VC6 库文件(Unicode 版本)
│ ├ VC2008 <folder>
│ │ ├ x64 <folder>
│ │ │ ├ EasyXa.lib // VC2008 ~ 2013 库文件(x64, MBCS 版本)
│ │ │ └ EasyXw.lib // VC2008 ~ 2013 库文件(x64, Unicode 版本)
│ │ └ x86 <folder>
│ │ ├ EasyXa.lib // VC2008 ~ 2013 库文件(x86, MBCS 版本)
│ │ └ EasyXw.lib // VC2008 ~ 2013 库文件(x86, Unicode 版本)
│ └ VC2015 <folder>
│ ├ x64 <folder>
│ │ ├ EasyXa.lib // VC2015 ~ 2022 库文件(x64, MBCS 版本)
│ │ └ EasyXw.lib // VC2015 ~ 2022 库文件(x64, Unicode 版本)
│ └ x86 <folder>
│ ├ EasyXa.lib // VC2015 ~ 2022 库文件(x86, MBCS 版本)
│ └ EasyXw.lib // VC2015 ~ 2022 库文件(x86, Unicode 版本)
└ Setup.hta // 安装程序
EasyX的基本概念
颜色
EasyX使用24bit真彩色表示颜色
16进制的颜色表示规则为0xbbggrr (bb=蓝,gg=绿,rr=红)
一些预定义的颜色如下
常量 值 颜色
-------- -------- --------
BLACK 0 黑
BLUE 0xAA0000 蓝
GREEN 0x00AA00 绿
CYAN 0xAAAA00 青
RED 0x0000AA 红
MAGENTA 0xAA00AA 紫
BROWN 0x0055AA 棕
LIGHTGRAY 0xAAAAAA 浅灰
DARKGRAY 0x555555 深灰
LIGHTBLUE 0xFF5555 亮蓝
LIGHTGREEN 0x55FF55 亮绿
LIGHTCYAN 0xFFFF55 亮青
LIGHTRED 0x5555FF 亮红
LIGHTMAGENTA 0xFF55FF 亮紫
YELLOW 0x55FFFF 黄
WHITE 0xFFFFFF 白
可以使用RGB宏来定义颜色
RGB(255, 255, 255)//等价于0xFFFFFF
设备
所谓“设备”,就是指当前操作的绘图界面。”设备“包括默认的绘图窗口以及IMAGE对象。
通过SetWorkingImage函数可以设置当前用于绘图的设备
void SetWorkingImage(IMAGE* pImg = NULL);
pImg为绘图设备指针。如果为 NULL,表示绘图设备为默认绘图窗口。
坐标
在 EasyX 中,坐标分两种:物理坐标和逻辑坐标。
物理坐标
物理坐标是描述设备的坐标体系。
坐标原点在设备的左上角,X 轴向右为正,Y 轴向下为正,度量单位是像素。
坐标原点、坐标轴方向、缩放比例都不能改变。
逻辑坐标
逻辑坐标是在程序中用于绘图的坐标体系。该体系的原点、坐标系方向、缩放比例可以自行修改
坐标默认的原点在窗口的左上角,X 轴向右为正,Y 轴向下为正,度量单位是点。
默认情况下,逻辑坐标与物理坐标是一一对应的,一个逻辑点等于一个物理像素。
凡是没有注明的坐标,均指逻辑坐标。
图形绘制原理
EasyX提供了大量图形绘制函数(见EasyX 文档 - 图形绘制相关函数),这些可以绘制的图形大致可以分为三类:
-
纯填充,如solidcircle、solidrectangle等
-
线条加填充,如fillcircle、fillpolygon等
图形的线条和填充的样式及颜色需要在绘制前分别设置
EasyX的常用函数
绘图设备相关函数
initgraph 初始化绘图窗口
HWND initgraph(
int width,
int height,
int flag = NULL
);
参数
width
绘图窗口的宽度。
height
绘图窗口的高度。
flag
绘图窗口的样式,默认为 NULL。可为以下值:
值 | 含义 |
---|---|
EX_DBLCLKS | 在绘图窗口中支持鼠标双击事件。 |
EX_NOCLOSE | 禁用绘图窗口的关闭按钮。 |
EX_NOMINIMIZE | 禁用绘图窗口的最小化按钮。 |
EX_SHOWCONSOLE | 显示控制台窗口。 |
initgraph(640, 480);//创建一个尺寸为 640x480 的绘图窗口
cleardevice 清空绘图
这个函数使用当前背景色清空绘图设备。
void cleardevice();
closegraph关闭绘图
这个函数用于关闭绘图窗口。
void closegraph();
颜色设置相关函数
setbkmode
设置背景模式为透明还是填充
EasyX 文档 - setbkcolor
注意设置完之后不会立即生效,需要cleardevice()之后才会改变背景颜色
EasyX 文档 - setlinecolor
setlinestyle 设置当前设备画线样式
setfillcolor 设置当前设备填充颜色
setfillstyle 设置当前设备填充样式
setpolyfillmode 设置当前设备多边形填充模式
该设置影响 fillpolygon、solidpolygon、clearpolygon 三个绘制多边形函数的执行效果。慎用这三个函数。
图形绘制相关函数
-
纯线条类
-
纯填充类
-
混合类
文字输出相关函数
可以使用这个函数设置文字的高度、宽度、书写角度、粗细、字体、抗锯齿选项等等
默认情况下,输出字符串的背景会用当前背景色填充。使用函数 setbkmode 可以设置文字的背景部分保持透明或使用背景色填充。
为了适应MBCS 和 Unicode两种编码,请使用 TCHAR 字符串及相关函数。
// 输出字符串(自适应字符集)
TCHAR s[] = _T("Hello World");
outtextxy(10, 20, s);
消息处理相关函数
消息缓冲区可以缓冲 63 个未处理的消息。每次获取消息时,将从消息缓冲区取出一个最早发生的消息。消息缓冲区满了之后,不再接收任何消息。
getmessage 获取一个消息。如果当前消息缓冲区中没有,就一直等待
其他函数
EndBatchDraw 结束批量绘制,并执行未完成的绘制任务
这三个函数主要用于制作动画的情况。如果不使用批量绘图,动画各个元素绘制时间不同,可能会出现闪烁。
使用批量绘图可以在生成完整的一帧画面之后再进行绘制,保证了每一帧的完整性。
样例程序
Firework Simulation
使用EasyX做一个简单的烟花模拟器
#include<graphics.h>
#include<cstdio>
#include<time.h>
#include<math.h>
#define tick 10 //定义帧刷新速度
#define width 960
#define height 960
#define interval 3000 //烟花间隔时间
#define eblife 4000 //碎片的持续时间
#define g 10.0
#define k 0.7 //阻力系数
#define ek 1.5 //阻力系数
#define e 2.7
//速度单位:pixel/s
struct particle
{
double x;//初始x坐标
double y;//初始y坐标
double nx, ny;//当前坐标
double vx;//初始vx
double vy;//初始vy
double t;//起始时间
int color;
int active;
};
particle fw[2005];
particle eb[500005];
particle temp[500005];
int fwcnt, ebcnt;
int tcnt;
double T = 0;
int set;
void setnewfw()
{
fwcnt++;
fw[fwcnt].t = T;
fw[fwcnt].active = 1;
fw[fwcnt].x = rand() % (width - 200) + 100;
fw[fwcnt].y = height;
fw[fwcnt].vx = -50 + rand() % 100;
fw[fwcnt].vy = -(400 + rand() % 200);
fw[fwcnt].color = RGB(150 + rand() % 105, 150 + rand() % 105, 150 + rand() % 105);
}
void setnewember(double x, double y)
{
int Color = RGB(150 + rand() % 105, 150 + rand() % 105, 150 + rand() % 105);
for (int i = 1; i <= 20; i++)
{
ebcnt++;
eb[ebcnt].t = T;
eb[ebcnt].active = 1;
eb[ebcnt].x = x;
eb[ebcnt].y = y;
eb[ebcnt].vx = rand() % 300 - 150;
eb[ebcnt].vy = rand() % 300 - 150;
eb[ebcnt].color = Color;
}
}
void updatefw()
{
double dt;
for (int i = 1; i <= fwcnt; i++)
{
dt = (T - fw[i].t) / 1000;
fw[i].nx = fw[i].x + (fw[i].vx - fw[i].vx * pow(e, -k * dt)) / k;
fw[i].ny = fw[i].y + ((k * fw[i].vy + g) - (k * fw[i].vy + g) * pow(e, -k * dt) + g * k * dt) / (k * k);
if ((fw[i].ny < height / 4) || (dt >= 5))
{
fw[i].active = 0;
setnewember(fw[i].nx, fw[i].ny);
}
}
tcnt = 0;
for (int i = 1; i <= fwcnt; i++)
{
if (fw[i].active)
{
tcnt++;
temp[tcnt] = fw[i];
}
}
for (int i = 1; i <= tcnt; i++)
{
fw[i] = temp[i];
}
fwcnt = tcnt;
}
void updateeb()
{
double dt;
int R, G, B;
for (int i = 1; i <= ebcnt; i++)
{
dt = (T - eb[i].t) / 1000;
eb[i].nx = eb[i].x + (eb[i].vx - eb[i].vx * pow(e, -ek * dt)) / ek;
eb[i].ny = eb[i].y + ((ek * eb[i].vy + g) - (ek * eb[i].vy + g) * pow(e, -ek * dt) + g * ek * dt) / (ek * ek);
R = GetRValue(eb[i].color);
G = GetGValue(eb[i].color);
B = GetBValue(eb[i].color);
R = (double)R * (1.0 - pow(dt / eblife, 5));
G = (double)G * (1.0 - pow(dt / eblife, 5));
B = (double)B * (1.0 - pow(dt / eblife, 5));
eb[i].color = RGB(B, G, R);
if (T - eb[i].t > eblife)
{
eb[i].active = 0;
}
}
tcnt = 0;
for (int i = 1; i <= ebcnt; i++)
{
if (eb[i].active)
{
tcnt++;
temp[tcnt] = eb[i];
}
}
for (int i = 1; i <= tcnt; i++)
{
eb[i] = temp[i];
}
ebcnt = tcnt;
}
void printscreen()
{
BeginBatchDraw();
cleardevice();
for (int i = 1; i <= fwcnt; i++)
{
setfillcolor(fw[i].color);
solidcircle((int)fw[i].nx, (int)fw[i].ny, 2);
}
for (int i = 1; i <= ebcnt; i++)
{
setfillcolor(eb[i].color);
solidcircle((int)eb[i].nx, (int)eb[i].ny, 1);
}
FlushBatchDraw();
EndBatchDraw();
}
int main()
{
srand((unsigned int)time(0));
initgraph(width, height);
set = interval;
while (1)
{
T += tick;
set += tick;
if (set >= interval)
{
set -= interval;
setnewfw();
}
updatefw();
updateeb();
printscreen();
Sleep(tick);
}
system("pause");
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理