控制台窗口的绘图

By | 2015年4月25日

最近和别人聊天,聊到了控制台绘图,曾经学了一些windows的API编程,却没想到直接在控制台窗口上进行绘图

今天有时间,便把代码写了出来

#include <Windows.h>
#include <stdio.h>

/************************************************************************/
/* 函数 DrawCircle
/* 参数 HDC hdc 绘图的句柄
/*      int x
/*		int y	圆心的x,y坐标
/*		int r	圆的半径
/*		COLORREF color		圆的填充颜色
/* 功能 在(x, y)处画一个半径为r的圆,用color的颜色填充
/************************************************************************/
void DrawCircle(HDC hdc, int x, int y, int r, COLORREF color)
{
	HBRUSH brush = (HBRUSH)CreateSolidBrush (color);		//新建一个画刷
	SelectObject(hdc, (HGDIOBJ)brush);						//选择画刷
	Ellipse(hdc, x-r, y-r, x+r, y+r);						//画一个圆
	DeleteObject(brush);									//删除画刷
}

int main()
{
	HWND console = GetConsoleWindow();						//获取控制台窗口句柄
	HDC  console_hdc = GetDC(console);						//获取绘图dc
	RECT rect;												//保存绘图区域大小的结构体
	int x,
		y,							//小球的xy坐标
		dx = 3,						//x的增量
		dy = 3;						//y的增量
	int r = 0, g = 0, b = 0;		//小球颜色
	int speed;						//小球速度
	COORD pos={0,4};				//光标位置

	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);

	GetClientRect(console, &rect);							//首先获取绘图区域的大小

	x = 10 + rand() % (rect.right);
	y = 10 + rand() % (rect.bottom);

	printf("当前窗口宽度 %d\n当前窗口高度 %d\n请输入小球颜色(r g b):", rect.right, rect.bottom);
	scanf("%d %d %d", &r, &g, &b);
	printf("请输入小球运动速度(1~10)");
	scanf("%d", &speed);
	//rgb 的范围分别是0~255
	//255 255 255为白色

	if (speed <1 || speed > 10 || r >255 || r < 0 || g >255 || g < 0 || b >255 || g <0)
	{
		printf("无效的输入");
		return 0;
	}

	while (1)
	{
		SetConsoleCursorPosition(hOut,pos);						//设置光标位置
		printf("当前小球位置 x= %d  \ty=%d  ", x, y);
		DrawCircle(console_hdc, x, y, 10, RGB(r, g, b));		//画一个白色的圆
		Sleep(200/speed);
		DrawCircle(console_hdc, x, y, 10, RGB(0, 0, 0));		//画一个黑色的圆(擦除)

		if (x > rect.right - 10 || x < 10)
			dx = -dx;

		if (y > rect.bottom - 10 || y < 10)
			dy = -dy;

		x += dx;
		y += dy;
	}

	ReleaseDC(console, console_hdc);
	return 0;
}

要实现绘图就需要先获取控制台窗口的句柄

句柄说的高大上,其实它就是一个数字而已,它标记了一种资源,根据这个数字,我们就可以找到这个窗口。不然在绘图函数绘图的时候,就可能写到别的窗口上了

获取了控制台句柄之后,我们需要获取HDC,HDC是设备上下文,名字变得更加玄乎了,其实它就是用来绘图的。

GetStdHandle这个API又是获取句柄的,不过这一次不是窗口了,而是标准输出,因为我们程序中需要直接定位光标的位置,因此需要使用到标准输出的句柄

GetClientRect能获取绘图的范围,知道了绘图的范围后,我们能让画出来的圆“碰到”边框后自动的“弹回”

因为printf会在每次画圆之后输出圆的坐标,因此我们需要在每次输出前将光标放到指定的位置

SetConsoleCursorPosition便发挥了这个作用,它将光标定位到第三行第一个字符的位置

DrawCircle是我们自己封装的一个函数,它的具体功能注释中已经明确的写了出来

DrawCircle中我们先新建了一个画刷,因为我们需要用指定的颜色去画圆

SelectObject选择画刷,这样下面的作图就会变成我们所指定的颜色

Ellipse是一个画椭圆的函数,它的后四个参数分别是椭圆的外界矩形的坐标,如果我们使矩形的边长相等,那么它画出来的自然就是圆形了

最后别忘了把画刷用DeleteObject删除,因为画刷也是需要占用资源的

下面就是实现小球的运动过程了,其实实现起来很简单。由于“视觉暂留”效应,我们只需要先画一个圆,再删除,接着立即在他的旁边画一个圆,人眼便会形成一种错觉,小球在运动!

那么怎么擦除呢,很简单啦,在我们画出的圆上再画一个圆,它的颜色和背景颜色相同,这样不就把刚才的圆给覆盖了吗?

小球的运动速度由每次画圆到擦除之前的延时时间决定,根据你的输入,每次延时的值将在200ms – 20ms之间变化

这样,我们便实现了控制台版本的运动的小球程序了

运行效果

运行效果

运行效果

运行效果

发表评论

电子邮件地址不会被公开。 必填项已用*标注