EasyX

这个

开头加上 graphic.h 头文件

绘图窗口与设备

initgraph(int width,int height,int flag=NULL);
initgraph(800,600,EX_NOMINIMIZE | EX_NOCLOSE); //创建禁用最小化和关闭按钮的绘图窗口
initgraph(800,600,EX_SHOWCONSOLE); //带控制台的图形窗口

结尾加上system("pause")或_getch()/*conio.h*/以显式看到画布
最后再加上closegraph()关闭画布

坐标

默认原点在设别的左上角,X轴向右为正,Y轴向下为正,单位是像素

void setorigin(int x,int y) //用于设置坐标原点
setaspectratio(float xasp,float yasp)
//通过设置 x 和 y 方向上的缩放因子,从而修改绘图的缩放比例或坐标轴方向

setaspectratio(2, 1); // x轴方向的缩放因子为2,y轴方向的缩放因子为1(默认值)
circle(100, 100, 100); //x轴方向长度为200,y轴长度为100

setaspectratio(1, -1); // 缩放因子为负数,可以实现坐标轴的翻转,此行可使y轴向上为正

我真是服了,都不许用setaspectratio(1, -1),这是人类能设计出来的吗

initgraph(400, 400);
setaspectratio(1, -1);
outtextxy(200, 200, "hello");

如上代码执行后什么都不会显示

initgraph(400, 400);
setaspectratio(1, -1);
outtextxy(200, -200, "hello");

上述执行后如图:
image
我真气笑了


update:好像确实是我的问题……应该先设置原点的
范例:

#include <graphics.h>

int main()
{
	initgraph(600, 600);

	setorigin(0, 600);			// 将绘图窗口的左下角作为坐标原点
	setaspectratio(1, -1);		// 缩放因子为负数,可以实现坐标轴的翻转,此行可使y轴向上为正
	circle(100, 100, 100);

	system("pause");
	closegraph();
	return 0;
}

颜色

1.用预定义常量表示颜色(常量名要大写)
2.用16进制
3.RGB(0~255) eg:setlinecolor(RGB(2,2,2));

常量 颜色
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

EasyX图形绘制函数

函数用法 函数说明
void circle ( int x, int y, int radius ) 画无填充的圆
fillcircle 画有边框的填充圆
solidcircle 画无边框的填充圆
clearcircle 用当前背景色清空圆形区域
void ellipse ( int left, int top, int right, int bottom ) 画无填充的椭圆
fillellipse 画有边框的填充椭圆
solidellipse 画无边框的填充椭圆
clearellipse 用当前背景色清空椭圆区域
void pie ( int left, int top, int right, int bottom, double stangle, double endangle ); 画无填充的扇形
fillpie 画有边框的填充扇形
solidpie 画无边框的填充扇形
clearpie 用当前背景色清空扇形区域
void rectangle ( int left, int top, int right, int bottom ) 画无填充的矩形
fillrectangle 画有边框的填充矩形
solidrectangle 画无边框的填充矩形
clearrectangle 用当前背景色清空矩形区域
void roundrect ( int left, int top, int right, int bottom, int ellipsewidth, int ellipseheight ) 画无填充的圆角矩形
fillroundrect 画有边框的填充圆角矩形
solidroundrect 画无边框的填充圆角矩形
clearroundrect 用当前背景色清空圆角矩形区域
void polygon ( const POINT *points, int num ); 画无填充的多边形
fillpolygon 画有边框的填充多边形
solidpolygon 画无边框的填充多边形
clearpolygon 用当前背景色清空多边形区域
函数名称 函数说明
void putpixel ( int x, int y, COLORREF color ) 画点
void line ( int x1, int y1, int x2, int y2 ) 画直线
void arc ( int left, int top, int right, int bottom, double stangle, double endangle ) 画椭圆弧
void polyline ( const POINT *points, int num ) 画多条连续的直线
void polybezier ( const POINT *points, int num ) 画三次方贝塞尔曲线
void floodfill ( int x, int y, COLORREF color, int filltype = FLOODFILLBORDER ) 填充区域
COLORREF getpixel ( int x, int y ) 获取坐标点的颜色
int getwidth ( ) 获取绘图区的宽度
int getheight ( ) 获取绘图区的高度
setlinecolor( COLOR ) 设置线条颜色
setfillcolor( COLOR ) 设置填充颜色
函数:setlinestyle(PS_SOLID,10)设置线为实线(PS_SOLID),线宽为10

关于floodfill函数,第四个参数0表示 FLOODFILLBORDER,1表示FLOODFILLSURFACE,不写默认为0
对于 FLOODFILLBORDER 填充类型,填充动作以 (x, y) 为起点,向周围扩散,直到遇到 border 指定的颜色才会终止。指定的区域必须是封闭的。适用于填充具有固定颜色边界的区域。
对于 FLOODFILLSURFACE 填充类型,填充动作以 (x, y) 为起点,只要邻接的颜色为 color, 填充就会延伸。适用于填充具有多种颜色边界的区域。

在设置背景色之后,并不会改变现有背景色,而是只改变背景色的值
如果需要修改全部背景色,可以在设置背景色后执行 cleardevice() 函数。
所以,

setbkcolor(BLUE); //该语句执行后背景色不会发生实际变化
clearrectangle(350,400,550,500); //该语句执行用背景色清空矩形区域,矩形内为蓝色

EasyX文字输出

字符数组 TCHAR 当字符串用就可以
定义方式为: TCHAR = _T('A') TCHAR s[] = _T("Hello World")
赋值: TCHAR name[10]; _tcscpy_s(name,10,_T("张三"));
将int转化为字符串:TCHAR num[5]; _stprintf_s(num,_T("数字%d"),1024);

settextcolor(BLUE) 设置字体颜色
settextstyle(int height,int width,_T("宋体")) width可以直接填0,自适应,第三个参数是字体
void outtextxy(int x,int y,TCHAR) 在(x,y)位置输出字符串,(x,y)是这个文本框的左上角位置
textheight(text) 获取字符串的高度
textwidth(text) 获取字符串的宽度(不是一个字的宽度)
outtextxy(300 - w / 2, 300 - h / 2, text); // 在屏幕中心绘制文本
setbkmode(TRANSPARENT),透明背景;setbkmode(OPAQUE),背景用背景色填充(默认)

if (_kbhit()) break;
while (!_kbhit())
// 按任意键退出,_kbhit是非阻塞函数,可以终止类似while(1)的循环

添加音乐

把mp3文件放到项目文件夹里
1.引用Winmm.lib链接库
语法:#pragma comment(lib,"Winmm.lib")
2.播放音乐
语法:mciSendString(_T("open v2.mp3 alias bkmusic"),0,0,0); //alias指定别名,v2.mp3 == bkmusic
音乐名v2.mp3必须和程序文件中存储的音乐文件名称相同,否则调用失败
3.循环播放
语法:mciSendString(_T("play bkmusic repeat"),0,0,0); //重复播放bkmusic,即v2.mp3
4.停止播放
mciSendString(_T("close bkmusic"),0,0,0); 可以不写,直接叉掉

双缓冲绘图

双缓冲绘图通过在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,从而减少对屏幕的直接绘图操作,提高绘图效率、消除屏幕闪烁
BeginBatchDraw() 开始批量绘图
EndBatchDraw() EndBatchDraw(int left,int top,int right,int bottom)(指定区域) 结束批量绘制,并执行未完成的绘制任务
FlushBatchDraw() FlushBatchDraw(int left,int top,int right,int bottom)(指定区域) 执行未完成的绘制任务

范例:自动移动的圆,可以把三条语句未注释和注释掉的对比一下,不使用缓冲会屏幕闪烁

#include <graphics.h>

int main()
{
	initgraph(640, 480);
	 //BeginBatchDraw();			// 开启批量绘图

	setlinecolor(WHITE);			// 设置画线颜色
	setfillcolor(RED);				// 设置填充颜色
	for (int i = 50; i < 600; i++)
	{
		cleardevice();				// 清屏
		circle(i, 100, 40);			// 绘制空心圆
		floodfill(i, 100, WHITE);	// 用红色填充,直到遇到边界为白色为止的区域
		//FlushBatchDraw();		// 刷新批量绘图
		Sleep(10);					// 延时10毫秒
	}

	 //EndBatchDraw();				// 关闭批量绘图
	closegraph();
	return 0;
}

关于时间的一些函数

控制帧数:

//include <windows.h>
#include <graphics.h>

int main()
{
	initgraph(640, 480);
	BeginBatchDraw();

	setlinecolor(WHITE);
	setfillcolor(RED);
	for (int i = 50; i < 600; i++)
	{
		DWORD beginTime = GetTickCount();			// 记录循环开始时间

		cleardevice();
		circle(i, 100, 40);
		floodfill(i, 100, WHITE);
		FlushBatchDraw();

		DWORD endTime = GetTickCount();				// 记录循环结束时间
		DWORD elapsedTime = endTime - beginTime;	// 计算循环耗时
		if (elapsedTime < 1000 / 60)				// 按每秒60帧进行补时
			Sleep(1000 / 60 - elapsedTime);
	}

	EndBatchDraw();
	closegraph();
	return 0;
}

GetTickCount 是一个 Windows 系统函数,用于获取从操作系统启动以来所经过的毫秒数,通过在代码中的不同位置调用该函数,并计算两次调用之间的差值,可以得知某段代码或某个操作的执行时间。单位:毫秒
image

图像处理

把图片放到项目文件夹内
image

IMAGE p; //定义图片变量
p.getwidth(); p.getheight(); //IMAGE类具有内置函数获取宽高
loadimage(&图片变量名,"XX.jpg"/*图片名称*/,int width=0,int height=0); //载入图片
loadimage(NULL, _T("image\\background.jpg")); loadimage(NULL, _T("testt.jpg"), 700, 300,false); 
// 第一个参数为NULL时,直接读取图片至绘图窗口
// 第五个参数若为 true,则会调整窗口以适应图片的大小
putimage(int x,int y,&图片变量名); //显示图片,xy是图片左上角的坐标
putimage(0, 0, 900, 600, &img, 115, 0); // 从图像的(115,0)坐标处截取宽900、高600的部分内容显示在窗口(0,0)处
Resize(&img, 600, 400); // 调整IMAGE的尺寸,注意不是缩放

putimage(0, 0, &img, NOTSRCCOPY);,第四个参数是三元光栅操作码
image

GetImageBuffer 函数,可以设置渐变背景色
#include <graphics.h>

int main()
{
	initgraph(600, 400);

	DWORD* pMem = GetImageBuffer();							// 获取当前窗口所指图像缓冲区的指针	
	for (int i = 0; i < 600 * 400; i++)
		pMem[i] = BGR(RGB(0, 0, i * 256 / (600 * 400)));	// 直接对图像缓冲区每个坐标像素赋值(颜色)

	system("pause");
	closegraph();
	return 0;
}

透明贴图

通过PS制作原图的掩码图和前景图,再进行三元光栅操作叠加而成
#include <graphics.h>

int main()
{
	IMAGE imgGuoqi, imgGuohui, imgGuohuiMask, imgGuohuiFg;
	loadimage(&imgGuoqi, _T("guoqi.jpg"), 1000, 600);			// 加载国旗(背景图)
	loadimage(&imgGuohui, _T("guohui.jpg"), 200, 200);			// 加载国徽原图(白色周边)
	loadimage(&imgGuohuiMask, _T("guohui_mask.jpg"), 200, 200);	// 加载国徽掩码图(白色周边+黑色内容)
	loadimage(&imgGuohuiFg, _T("guohui_fg.jpg"), 200, 200);		// 加载国徽前景图(黑色周边+待显示内容)

	initgraph(1000, 600);

	putimage(0, 0, &imgGuoqi);							// 显示国旗
	putimage(0, 0, &imgGuohui);							// 显示国徽原图
	putimage(0, 200, &imgGuohuiMask);					// 显示国徽掩码图
	putimage(0, 400, &imgGuohuiFg);						// 显示国徽前景图

	// 透明贴图
	putimage(200, 0, &imgGuohuiMask, SRCAND);			// 显示掩码图(SRCAND:按位与)
	putimage(200, 0, &imgGuohuiFg, SRCPAINT);			// 显示前景图(SRCPAINT:按位或)

	system("pause");
	closegraph();
	return 0;
}
![image](https://img2024.cnblogs.com/blog/2930138/202507/2930138-20250709101757678-1964597891.png)
已经有透明底图片了,AlphaBlend 函数实现(推荐)
#include <graphics.h>
#pragma comment(lib, "MSIMG32.LIB")						// 链接器在链接过程中包含指定的库文件

void putimage_alpha(int x, int y, IMAGE* img)
{
	int w = img->getwidth();
	int h = img->getheight();
	AlphaBlend(GetImageHDC(NULL), x, y, w, h, GetImageHDC(img), 0, 0, w, h, { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA });
}

int main()
{
	initgraph(1000, 600);

	IMAGE imgGuoqi, imgBaidu;
	loadimage(&imgGuoqi, _T("guoqi.jpg"), 1000, 600);		// 加载国旗(背景图)
	loadimage(&imgBaidu, _T("baidu.png"));					// 加载百度LOGO(PNG格式),baidu.png已经是透明底了!!!
	putimage(0, 0, &imgGuoqi);										// 显示国旗
	putimage(0, 0, &imgBaidu);										// 显示百度LOGO
	putimage_alpha(0, 300, &imgBaidu);								// 显示百度LOGO(透明贴图),这个函数要自己写

	system("pause");
	closegraph();
	return 0;
}

image

图片动画

是一系列静态的动画帧

#include <graphics.h>
#include <cstdio>
#pragma comment(lib, "MSIMG32.LIB")

const int WINDOW_WIDTH = 1000;			//窗口宽度
const int WINDOW_HEIGHT = 600;			//窗口高度
const int FRAME = 60;					//帧数
const int INTERVAL_MS = 15;				//动画帧间隔
const int IMAGE_NUM = 13;				//动画图片数

/*
帧率是每秒屏幕刷新的速度,决定了动画的流畅度,确保显示稳定
动画帧间隔是决定图片切换的速度 时间间隔越短,角色动作越快;时间间隔越长,动作越慢。
INTERVAL_MS是代码层面的图片切换速度,实际显示出来的时间间隔还和帧率有关
因为屏幕刷新后新操作才会被呈现,如果在屏幕刷新前切换是不会被显示的
*/

//显示透明图片
void putimage_alpha(int x, int y, IMAGE* img)
{
	int w = img->getwidth();
	int h = img->getheight();
	AlphaBlend(GetImageHDC(NULL), x, y, w, h, GetImageHDC(img), 0, 0, w, h, { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA });
}
int cnt = 0;

int main()
{
	bool running = true;			//主循环控制
	IMAGE imgBackground;			//背景图片对象
	IMAGE imgPEA[13];				//玩家动画图片
	TCHAR imgPath[256];				//动画图片文件路径
	int imgIndex = 0;				//动画帧索引
	static int timer = 0;			//动画计时器

	loadimage(&imgBackground, _T("image\\background.jpg"));		//加载背景图片
	for (int i = 0; i < IMAGE_NUM; i++)							//加载动画图片
	{
		_stprintf_s(imgPath, _T("image\\pea\\%d.png"), i + 1);	//动画图片路径(格式转换)
		loadimage(&imgPEA[i], imgPath);							//加载动画图片
	}

	initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
	BeginBatchDraw();

	//主循环
	while (running)
	{
		DWORD beginTime = GetTickCount();

		//数据处理
		timer += 5;
		if (timer > INTERVAL_MS)					//定时器超过预定的时间间隔时切换下一张图片
		{
			imgIndex = (imgIndex + 1) % IMAGE_NUM;			//循环切换图片:索引值0-12
			timer = 0;										//重置计时器
		}

		//这是我自己写的,我认为这个程序每三帧切换一次图片,于是采用了cnt计数,运行后视觉效果没啥变化
		//cnt++;
		//if (cnt > 3) {
		//	imgIndex = (imgIndex + 1) % IMAGE_NUM;
		//	cnt = 0;
		//}

		//绘图
		cleardevice();
		putimage(0, 0, &imgBackground);						//绘制背景图片
		putimage_alpha(500, 300, &imgPEA[imgIndex]);		//绘制豌豆图片
		FlushBatchDraw();

		//帧延时处理
		DWORD endTime = GetTickCount();
		DWORD elapsedTime = endTime - beginTime;
		if (elapsedTime < 1000 / FRAME)
			Sleep(1000 / FRAME - elapsedTime);
	}

	EndBatchDraw();
	closegraph();
	return 0;
}

消息处理

ExMessage 是一个结构体

函数:
ExMessage getmessage(BYTE filter=-1) void getmessage(ExMessage *msg,BYTE filter=-1)从消息缓冲区获取一个消息
bool peekmessage(ExMessage *msg,BYTE filter = -1, bool removemsg = true) 从消息缓冲区获取一个消息并立即返回
void flushmessage(BYTE filter = -1) 清空消息缓冲区

参数说明
msg:指向消息结构体 ExMessage 的指针,用来保存获取到的消息。
filter:指定要获取的消息范围,默认 -1 获取所有类别的消息。可以用以下值或值的组合获取指定类别的消息 具体的,EX_MOUSE鼠标消息,EX_KEY按键消息,EX_CHAR字符消息,EX_WINDOW窗口消息
removemsg:在 peekmessage 处理完消息后,是否将其从消息队列中移除。

image

鼠标消息

ExMessage m;
while (true) {
	m = getmessage(EX_MOUSE);
	if (m.message == WM_MOUSEMOVE) {
		//...
	}else if(m.message == WM_LBUTTONDOWN){
        //...    
    }
}
////////////////////////////////////
ExMessage msg;
while (running) {
	// 消息处理		
	while (peekmessage(&msg)) {
		if (msg.message == WM_MOUSEMOVE) {				// 圆的位置随鼠标位置变化
			x = msg.x;
			y = msg.y;
		}
		else if (msg.message == WM_LBUTTONDOWN) {		// 左键按下圆变红色
			setfillcolor(RED);
		}
		else if (msg.message == WM_LBUTTONUP) {			// 左键松开圆变白色
			setfillcolor(WHITE);
		}
		else if (msg.message == WM_RBUTTONDOWN) {		// 右键按下结束主循环
			running = false;
		}
	}
	// 绘图
	cleardevice();					// 清除屏幕
	solidcircle(x, y, r);			// 绘制当前帧内容
	FlushBatchDraw();				// 刷新批量绘图
}

键盘消息

用键盘控制小球
typedef struct Ball
{
	int x;				// 小球圆心坐标x
	int y;				// 小球圆心坐标y
	int r;				// 小球半径
	int dx;				// 小球在x轴方向移动的增量
	int dy;				// 小球在y轴方向移动的增量
	COLORREF color;		// 小球颜色
} Ball;

int main()
{
	bool running = true;
	ExMessage msg;
	Ball ball = { 300, 300, 20, 5, 5, YELLOW };	// 创建小球并初始化
	initgraph(600, 600);
	BeginBatchDraw();

	while (running)
	{
		while (peekmessage(&msg))
		{
			if (msg.message == WM_KEYDOWN) //按键按下状态
			{
				switch (msg.vkcode)					// 判断虚拟键代码
				{
				case 'w'://小写w					// 上键:小球Y坐标减少
				case 'W'://大写W
				case VK_UP://箭头上
					ball.y -= ball.dy;
					break;

				case 's':
				case 'S':
				case VK_DOWN:						// 下键:小球Y坐标增加
					ball.y += ball.dy;
					break;

				case 'a':
				case 'A':
				case VK_LEFT:						// 右键:小球X坐标减少
					ball.x -= ball.dx;
					break;

				case 'd':
				case 'D':
				case VK_RIGHT:						// 右键:小球X坐标增加
					ball.x += ball.dx;
					break;

				case VK_ESCAPE:						// ESC键:结束主循环
					running = false;
					break;
				}
			}			
		}

		cleardevice();							// 清除屏幕
		setfillcolor(ball.color);				// 设置填充颜色
		solidcircle(ball.x, ball.y, ball.r);	// 绘制无边框填充圆;
		FlushBatchDraw();
	}

	EndBatchDraw();
	closegraph();
	return 0;
}
缺陷:小球不能斜向移动,小球越界

虚拟键代码 https://learn.microsoft.com/zh-cn/windows/win32/inputdev/virtual-key-codes

用键盘控制小球(支持斜向运动)
#include <graphics.h>

// 用结构体封装小球属性
typedef struct Ball
{
	int x;						// 小球圆心坐标x
	int y;						// 小球圆心坐标y
	int r;						// 小球半径
	int dx;						// 小球在x轴方向移动的增量
	int dy;						// 小球在y轴方向移动的增量
	COLORREF color;				// 小球颜色
	bool isMoveUp = false;		// 小球是否向四个方向移动
	bool isMoveDown = false;
	bool isMoveLeft = false;
	bool isMoveRight = false;
} Ball;

int main()
{
	bool running = true;
	ExMessage msg;
	Ball ball = { 300, 300, 20, 5, 5, YELLOW };		// 创建小球并初始化
	initgraph(600, 600);
	BeginBatchDraw();

	while (running)
	{
		DWORD beginTime = GetTickCount();			// 记录循环开始时间

		// 消息处理
		while (peekmessage(&msg))
		{
			if (msg.message == WM_KEYDOWN)			// 按下按键处理
			{
				switch (msg.vkcode)
				{
				case 'w':
				case 'W':
				case VK_UP:
					ball.isMoveUp = true;
					break;

				case 's':
				case 'S':
				case VK_DOWN:
					ball.isMoveDown = true;
					break;

				case 'a':
				case 'A':
				case VK_LEFT:
					ball.isMoveLeft = true;
					break;

				case 'd':
				case 'D':
				case VK_RIGHT:
					ball.isMoveRight = true;
					break;

				case VK_ESCAPE:
					running = false;
					break;
				}
			}
			if (msg.message == WM_KEYUP)			// 松开按键处理
			{
				switch (msg.vkcode)
				{
				case 'w':
				case 'W':
				case VK_UP:
					ball.isMoveUp = false;
					break;

				case 's':
				case 'S':
				case VK_DOWN:
					ball.isMoveDown = false;
					break;

				case 'a':
				case 'A':
				case VK_LEFT:
					ball.isMoveLeft = false;
					break;

				case 'd':
				case 'D':
				case VK_RIGHT:
					ball.isMoveRight = false;
					break;
				}
			}
		}

		// 数据处理:根据小球的移动状态设置其坐标
		if (ball.isMoveUp)
			ball.y -= ball.dy;
		if (ball.isMoveDown)
			ball.y += ball.dy;
		if (ball.isMoveLeft)
			ball.x -= ball.dx;
		if (ball.isMoveRight)
			ball.x += ball.dx;
		
		// 绘图
		cleardevice();								// 清除屏幕
		setfillcolor(ball.color);					// 设置填充颜色
		solidcircle(ball.x, ball.y, ball.r);		// 绘制无边框填充圆;
		FlushBatchDraw();

		// 帧延时
		DWORD endTime = GetTickCount();				// 记录循环结束时间
		DWORD elapsedTime = endTime - beginTime;	// 计算循环耗时
		if (elapsedTime < 1000 / 60)				// 按每秒60帧进行补时
			Sleep(1000 / 60 - elapsedTime);
	}

	EndBatchDraw();
	closegraph();
	return 0;
}

该代码中需要帧延时,第一个程序中,有按键操作才会有移动,速度取决于按键速度;
(解释一下长按操作也会一直移动的原因:这是因为 Windows 系统的键盘自动重复机制在起作用,系统默认的重复率通常是每秒 30 次左右,这就限制了小球的最大移动速度)
而第二个程序中如果长按一个键,每次循环都为true都会移动,速度取决于刷新频率。

窗口碰撞检测
typedef struct Ball
{
	int x;						// 小球圆心坐标x
	int y;						// 小球圆心坐标y
	int r;						// 小球半径
	int dx;						// 小球在x轴方向移动的增量
	int dy;						// 小球在y轴方向移动的增量
	COLORREF color;				// 小球颜色
	bool isMoveUp = false;		// 小球是否向四个方向移动
	bool isMoveDown = false;
	bool isMoveLeft = false;
	bool isMoveRight = false;
} Ball;

int main()
{
	bool running = true;
	ExMessage msg;
	Ball ball = { 300, 300, 20, 5, 5, YELLOW };		// 创建小球并初始化
	initgraph(WIN_WIDTH, WIN_HEIGHT);
	BeginBatchDraw();

	while (running)
	{
		DWORD beginTime = GetTickCount();			// 记录循环开始时间

		// 消息处理
		while (peekmessage(&msg))
		{
			if (msg.message == WM_KEYDOWN)			// 按下按键处理
			{
				switch (msg.vkcode)
				{
				case 'w':
				case 'W':
				case VK_UP:
					ball.isMoveUp = true;
					break;

				case 's':
				case 'S':
				case VK_DOWN:
					ball.isMoveDown = true;
					break;

				case 'a':
				case 'A':
				case VK_LEFT:
					ball.isMoveLeft = true;
					break;

				case 'd':
				case 'D':
				case VK_RIGHT:
					ball.isMoveRight = true;
					break;

				case VK_ESCAPE:
					running = false;
					break;
				}
			}
			if (msg.message == WM_KEYUP)			// 松开按键处理
			{
				switch (msg.vkcode)
				{
				case 'w':
				case 'W':
				case VK_UP:
					ball.isMoveUp = false;
					break;

				case 's':
				case 'S':
				case VK_DOWN:
					ball.isMoveDown = false;
					break;

				case 'a':
				case 'A':
				case VK_LEFT:
					ball.isMoveLeft = false;
					break;

				case 'd':
				case 'D':
				case VK_RIGHT:
					ball.isMoveRight = false;
					break;
				}
			}
		}

		// 斜向移动:计算不同方向(包括同时)按下时的速度增量

        //if (ball.isMoveUp)
		//	ball.y -= ball.dy;
		//if (ball.isMoveDown)
		//	ball.y += ball.dy;
		//if (ball.isMoveLeft)
		//	ball.x -= ball.dx;
		//if (ball.isMoveRight)
		//	ball.x += ball.dx;
        // 斜向移动优化
		int directX = ball.isMoveRight - ball.isMoveLeft;
		int directY = ball.isMoveDown - ball.isMoveUp;
		double directXY = sqrt(directX * directX + directY * directY);
		if (directXY != 0)
		{
			double factorX = directX / directXY;	//计算X、Y方向的标准化分量
			double factorY = directY / directXY;
			ball.x += (int)ball.dx * factorX;		//小球坐标 = 方向增速 * 方向的标准化分量
			ball.y += (int)ball.dy * factorY;
		}

		// 边缘检测
		if (ball.y - ball.r <= 0)					// 上
			ball.y = ball.r;
		if (ball.y + ball.r >= WIN_HEIGHT)			// 下
			ball.y = WIN_HEIGHT - ball.r - 1;
		if (ball.x - ball.r <= 0)					// 左
			ball.x = ball.r;
		if (ball.x + ball.r >= WIN_WIDTH)			// 右
			ball.x = WIN_WIDTH - ball.r - 1;

		// 绘图
		cleardevice();								// 清除屏幕
		setfillcolor(ball.color);					// 设置填充颜色
		solidcircle(ball.x, ball.y, ball.r);		// 绘制无边框填充圆;
		FlushBatchDraw();

		// 帧延时
		DWORD endTime = GetTickCount();				// 记录循环结束时间
		DWORD elapsedTime = endTime - beginTime;	// 计算循环耗时
		if (elapsedTime < 1000 / 60)				// 按每秒60帧进行补时
			Sleep(1000 / 60 - elapsedTime);
	}

	EndBatchDraw();
	closegraph();
	return 0;
}

设置窗口标题

HWND hWnd = GetHWnd();						// 获得窗口句柄	
SetWindowText(hWnd, _T("植物大战僵尸"));		// 使用 Windows API 修改窗口名称
posted @ 2025-07-07 09:47  弗阴  阅读(45)  评论(0)    收藏  举报