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 文档 - 图形绘制相关函数),这些可以绘制的图形大致可以分为三类:

  1. 纯线条,如linecircle

  2. 纯填充,如solidcirclesolidrectangle

  3. 线条加填充,如fillcirclefillpolygon

图形的线条和填充的样式及颜色需要在绘制前分别设置

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 设置当前设备多边形填充模式

该设置影响 fillpolygonsolidpolygonclearpolygon 三个绘制多边形函数的执行效果。慎用这三个函数。

图形绘制相关函数

详见EasyX 文档 - 图形绘制相关函数

  1. 纯线条类

line 画直线

circle 画无填充圆

rectangle 画无填充矩形

  1. 纯填充类

solidrectangle 画无边框的填充矩形

solidcircle 画无边框的填充圆

  1. 混合类

fillcircle 画有边框的填充圆

fillrectangle 画有边框的填充矩形

文字输出相关函数

settextcolor 设置当前文字颜色

settextstyle 设置当前文字样式

可以使用这个函数设置文字的高度、宽度、书写角度、粗细、字体、抗锯齿选项等等

outtextxy 在指定位置输出字符串

默认情况下,输出字符串的背景会用当前背景色填充。使用函数 setbkmode 可以设置文字的背景部分保持透明或使用背景色填充。

为了适应MBCS 和 Unicode两种编码,请使用 TCHAR 字符串及相关函数。

// 输出字符串(自适应字符集)
TCHAR s[] = _T("Hello World");
outtextxy(10, 20, s);

消息处理相关函数

消息缓冲区可以缓冲 63 个未处理的消息。每次获取消息时,将从消息缓冲区取出一个最早发生的消息。消息缓冲区满了之后,不再接收任何消息。

ExMessage 消息结构体的定义

flushmessage 清空消息缓冲区

getmessage 获取一个消息。如果当前消息缓冲区中没有,就一直等待

peekmessage 获取一个消息,并立即返回

其他函数

BeginBatchDraw 开始批量绘图

FlushBatchDraw 执行未完成的绘制任务

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");
}
posted @ 2023-01-10 00:15  leason_lyx  阅读(544)  评论(0编辑  收藏  举报