现在书的进度进入都最后一章的Windows编程,嘛,和作者的态度差不多,Windows编程实在是枯燥乏味,但是也是必须去了解一些基础的知识,不然看到别人的程序就懵逼了。
最后一章我分为两个章节来讨论,首先是GDI图形绘制(相比于使用GPU硬件加速的D3D肯定要慢)、锁帧效果,之后是作者做的游戏控制台程序,说不定我能玩出点花样来,现在还没有看。
好了,让我们进入正题吧。
点,所有图形的基础,想要绘制一个图形就得要知道怎么绘制一个点。让我们来看看代码(代码放在GameMain函数处,不清楚的请看一下我写的Windows编程第二节,即游戏循环框架的基本实现):
- HDC hdc = GetDC(hwnd);
- int x = rand() % 400;
- int y = rand() % 400;
- COLORREF color = RGB(rand() % 255, rand() % 255, rand() % 255);
- SetPixel(hdc, x, y, color);
- Sleep(100);
- ReleaseDC(hwnd, hdc);
画点很简单吧,但是点太小了吧,根本看不清啊,来个大点的:
- HDC hdc = GetDC(hwnd);
- int x = rand() % 400;
- int y = rand() % 400;
- COLORREF color = RGB(rand() % 255, rand() % 255, rand() % 255);
- int size = 4;
- for (int i = 0; i < size; i)
- for (int j = 0; j < size; j)
- SetPixel(hdc, x i, y j, color);
- Sleep(100);
- ReleaseDC(hwnd, hdc);
效果:
嗯,很棒!有点了,然后就是要画线了,画线的话首先要学到一个画笔的系统,在画之前首先就要设置画笔,这跟画点有点不一样,画点什么都不用管,看代码:
- int x = rand() % 400;
- int y = rand() % 400;
- int x2 = rand() % 400;
- int y2 = rand() % 400;
- COLORREF color = RGB(rand() % 255, rand() % 255, rand() % 255);
-
- HDC hdc = GetDC(hwnd);
- HPEN pen = CreatePen(PS_SOLID, 1, color);
- SelectObject(hdc, pen);
-
- MoveToEx(hdc, x, y, NULL);
- LineTo(hdc, x2, y2);
-
- Sleep(100);
-
- DeleteObject(pen);
- ReleaseDC(hwnd, hdc);
效果:
以上就实现了画线的效果,需要注意的是画边框之类的线条结构,基本都是要设置HPEN铅笔的,而实心结构则需要设置画刷HBRUSH。
下面讲随机画矩形的示例,这边即用到了画笔画出矩形的边框,又用画刷填充矩形:
-
- int x = rand() % 400;
- int y = rand() % 400;
-
-
- HDC hdc = GetDC(hwnd);
-
- COLORREF color = RGB(rand() % 255, rand() % 255, rand() % 255);
- HPEN pen = CreatePen(PS_SOLID, 1, color);
- SelectObject(hdc, pen);
-
- color = RGB(rand() % 255, rand() % 255, rand() % 255);
- HBRUSH brush = CreateSolidBrush(color);
- SelectObject(hdc, brush);
-
-
- Rectangle(hdc, x, y, x 10, y 10);
-
- Sleep(100);
-
-
- DeleteObject(pen);
- DeleteObject(brush);
- ReleaseDC(hwnd, hdc);
非常好!这样我们就能随机绘制正方形了。细心的玩家可能注意到了,在设置铅笔和画刷的时候使用的函数都是一样的,都是SelectObject、DeleteObject。我想这个方法肯定不止用在这,否则也不会叫做Object了。具体我就不探究了。
什么,你想画圆?也很简单,之前不是学了画矩形嘛,在Windows中画圆其实就是绘制矩形的内切圆,参数是一模一样的,只需要把绘制矩形的代码写成:
- Ellipse(hdc, x, y, x 10, y 10);
就是这种弧线锯齿很严重,非常想学抗锯齿的知识!
接下来是多边形,给大家一个画六边形的代码,具体更多有趣的玩法,请自己尝试尝试。
下面是代码:
- POINT poly[6] = { {x 10, 0 y}, {x 30, 0 y}, {x 40, 17.3 y}, {x 30, 34.6 y}, {x 10, 34.6 y}, {x 0, 17.3 y} };
- Polygon(hdc, poly, 6);
把绘制矩形的代码替换成以上代码,就能获得一下效果:
其实想想看,关键绘制的代码就那么一两句,而且hdc、pen、brush之类的非常常用,所以真正编写的时候设置成全局的变量会比较舒服吧,这样实际的代码会非常的简洁,以后要改什么功能也不必关注这类初始化、销毁的细节。
最后作者问了一个问题,如果你绘制时点的顺序是混乱的,绘制多边形的时候很容易绘制出凹多边形,那么如何判断一个多边形是否是凸多边形呢?
我一开始的想法有点简单了,后来百度查了一下了解到了凸多边形的定义,就是任意两个临近的点画一条直线,所有其他的点都在直线的一侧就可以判断出是凸多边形了。
具体代码实现给大家讲一个思路,循环临近的所有两点,根据其他点的x坐标,算出临近两点画出直线对应的y坐标,这个y坐标是在直线上的,跟判断点的y坐标对比一下,看看其他的各点是否都大于(或都小于)y坐标。
最后讲一下实现锁帧的效果,当然也是GameMain处的代码:
- DWORD start_time = GetTickCount();
-
- while ((GetTickCount() - start_time) < 1000);
这里的代码就增强的Sleep的效果,没想到锁帧这么容易吧?
对了,突然想起来忘记比较重要的定时器了,嗯……简单介绍一下:
- UNIT SetTimer(HWND hWnd,
- UINT nIDevent,
- UINT nElapse,
- TIMERPROC lpTimerFunc);
待定时完成,就会触发处理事件WinProc,事件的类型是WM_TIMER,然后根据参数wparam就能判断出定时器的id,进行相应的处理。
KillTimer(hwnd, timer)是删除定时器的函数。具体例子就不说了,其实跟cocos的schdule非常像。
简单却很重要,就是这个东西吧。