控制台窗口的绘图

By | 2015年4月25日

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#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之间变化

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

运行效果

运行效果

运行效果

运行效果

发表评论

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