织梦CMS - 轻松建站从此开始!

罗索

Pocket PC的游戏编程指导教程(共十章)3

罗索客 发布于 2008-12-19 14:30 点击:次 
第三章:离屏技术(也就是我们常说的屏幕缓冲区) 导读 在我们学习离屏(OFF SCREEN)技术之前,让我们看看ON SCREEN是什么?为什么没有任何游戏程序使用这种方法?此外,还有另外一个基础问题,我们怎么在窗口的客户区绘制文字、图片或者是图画? 通常,在屏幕上显示
TAG:

第三章:离屏技术(也就是我们常说的屏幕缓冲区)

导读

在我们学习离屏(OFF SCREEN)技术之前,让我们看看ON SCREEN是什么?为什么没有任何游戏程序使用这种方法?此外,还有另外一个基础问题,我们怎么在窗口的客户区绘制文字、图片或者是图画?

通常,在屏幕上显示任何东西的方法是使用GDIs(图形设备接口)和APIs(应用程序接口)。Windows的窗口区域被划分成两个重要部分:客户区和非客户区(比如菜单、标题栏和边界框)。大多数窗口是可以移动的。因此它所显示的内容也跟着窗口自己的左上角一起关联移动。GDIs和APIs帮助我们管理这种关联。

Windows的GDI是用来对所有硬件设备提供一种硬件无关支持的程序。因为各个厂家的硬件技术是不同的,所以用这些相同的代码是无法获得硬件的最大性能。实现它们的目的只是保证支持。然而,很多有些开发者想要获得硬件设备的最大性能,他们不用GDIs和APIs,而是直接访问硬件。当然这些方法也可以工作,但却依赖于使用的硬件,它们可能无法在全部的设备上工作。

Windows CE的显示技术又如何呢?有人都能改变显示适配器吗?当然不能,因为它是一种嵌入式系统。通常硬件厂商不会在系统中包含优化代码。然而,这种显示速度已经能够应付某些游戏类型了。

Windows GDIs

设备正文的句柄,通常表示为hDC,是一个连接GDI程序的32位的整数值。通常,大多数窗口有它自己的DC(设备文本),并且你可以通过它的hWnd(窗口句柄)获得它的DC。方法如下:
HDC hDC;
hDC = GetDC(hWnd);

要调用GDIs和APIs,例如画一条线,你需要将hDC作为这些GDI函数的第一个参数。

MoveToEx(hDC,0,0,NULL); //将作图点移动到(0,0)
LineTo(hDC,240,300); //从作图点画线到(240,300)
做完这些,你还必须要从内存中释放hDC

ReleaseDC(hWnd,hDC);

这里还有一个特殊的绘图工作,那就是在收到WM_PAINT消息时,这意味着系统要求你画出你的客户区域,这些代码通常在MainWndProc中完成。下面的代码就的功能就是在窗口的正中央显示文字"hello"。另外,在WM_PAINT消息处理中,除了GetDC,还有另一种更方便的从窗口获取hDC的方法,那就是BeginPaint和EndPaint。

LRESULT MainWndProc(HWND hWnd, UINT message, WPARAM uParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hDC;
RECT rcClient;
switch(message)
{
case WM_PAINT:
hDC = BeginPaint(hWnd,&ps);
GetClientRect(hWnd,&rcClent);
DrawText(hDC,L"Hello",5,
&rcClient,DT_CENTER|DT_VCENTER);
EndPaint(hWnd,&ps);
break;
case WM_LBUTTONDOWN:
DestroyWindow(hWnd);
break;
default:
DefWindowProc(hWnd, message, uParam, lParam);
}
return 0;
}
这些代码中GetClientRect用来获得整个客户区域的矩形范围,DrawText用来在屏幕上这个举行范围的中心画出文字"hello"。

当然我们也可以在别的地方进行绘制,你可以再试试下面这些代码。

LRESULT MainWndProc(HWND hWnd, UINT message, WPARAM uParam, LPARAM lParam)
{
HDC hDC;
int nXpos;
int nYpos;

switch(message)
{

case WM_LBUTTONDOWN:
nXpos = LOWORD(lParam);
nYpos = HIWORD(lParam);
hDC = GetDC(hWnd);
MoveToEx(hDC,nXpos-4,nYpos-4,NULL);
LineTo(hDC,nXpos+4,nYpos+4);
MoveToEx(hDC,nXpos+4,nYpos-4,NULL);
LineTo(hDC,nXPos-4,nYpos+4);
ReleaseDC(hWnd,hDC);
break;

case WM_KEYDOWN:
DestroyWindow(hWnd);
break;

default:
DefWindowProc(hWnd, message, uParam, lParam);
}

return 0;
}
上面这种直接在屏幕(设备文本)上绘图的方法,就叫做ON SCREEN。在下面,我将给你演示为着这种方法不适合运用在游戏程序中。让我们看看下面这些长一些的代码。

static HBRUSH hbrRed;
static HPEN hpeBlue;

static void _drw_object(HDC hDC, int nX, int nY)
{
HGDIOBJ hOldPen, hOldBrush;

hOldPen = SelectObject(hDC,hpeBlue);
hOldBrush = SelectObject(hDC,hbrRed);
Ellipse(hDC,nX-20,nY-20,nX+20,nY+20);
SelectObject(hDC,hOldBrush);
SelectObject(hDC,hOldPen);
}

LRESULT MainWndProc(HWND hWnd, UINT message, WPARAM uParam, LPARAM lParam)
{
HDC hDC;

switch(message)
{
case WM_LBUTTONDOWN:
hDC = GetDC(hWnd);
_drw_object(hDC,LOWORD(lParam),HIWORD(lParam));
ReleaseDC(hWnd,hDC);
break;

case WM_MOUSEMOVE;
hDC = GetDC(hWnd);
PatBlt(hDC,0,0,240,320,WHITENESS);
_drw_object(hDC,LOWORD(lParam),HIWORD(lParam));
ReleaseDC(hWnd,hDC);
break;

case WM_LBUTTONUP:
hDC = GetDC(hWnd);
PatBlt(hDC,0,0,240,320,WHITENESS);
ReleaseDC(hWnd,hDC);
break;

case WM_CREATE:
hbrRed = CreateSolidBrush(RGB(255,0,0));
hpeBlue = CreatePen(0,0,RGB(0,0,255);
break;

case WM_DESTROY:
DeleteObject(hbrRed);
DeleteObject(hpeBlue);
PostQuitMessage(0);
break;

case WM_KEYDOWN:
DestroyWindow(hWnd);
break;

default:
DefWindowProc(hWnd, message, uParam, lParam);
}

return 0;
}
现在,试试用笔点击屏幕并在拖动看看。

计算机将首先将屏幕填充为白色,将原本的圆形物体擦除,然后在新的位置画上。因为填充屏幕要画掉很多的时间,因此我们将看到旧有图形被长时间的擦除,然后新图形在别的位置出现。这个过程就在我们的眼睛中形成了闪烁。如果有多个需要绘制的物体,这种闪烁将更加明显。

离屏技术

离屏技术的优点就恰恰是避免屏幕的闪烁。它的方法就是:创建一个隐藏的屏幕,一个虚拟的屏幕,或者是在可显区域外的屏幕。然后,我们将所要画的任何东西都先画在这个屏幕上。在这个期间,真正的屏幕是不会变化的。当绘制结束后,在再将整个虚拟屏幕上的内容拷贝到真正的屏幕,因为这个时间很短,内容变化不大时,几乎看不到任何闪烁。

现在,就让我们看看它是如何在Pocket PC上实现的?有三个重要的步骤:

1. 创建离屏的虚拟屏幕。
2. 在离屏虚拟屏幕上绘图。
3. 将离屏虚拟屏幕的内容拷贝到真正的屏幕。


static HBRUSH hbrRed;
static HPEN hpeBlue;

static HDC hOffscreenDC;
static HBITMAP hOffscreenBuffer;
static int nOffscreenCX, nOffscreenCY;

//创建离屏表面
void InitOffscreen(int nWidht, int nHeight)
{
HDC hDesktopDC;

//获取桌面的设备文本
hDesktopDC = GetDC(0);
//创建和桌面相同的设备文本,也就是虚拟屏幕的设备文本
hOffscreenDC = CreateCompatibleDC(hDesktopDC);
//创建和桌面相同的位图(缓冲内存)
hOffscreenBuffer = CreateCompatibleBitmap(hDesktopDC,nWidth,nHeight);
//将内存缓冲选入虚拟屏幕的设备文本
SelectObject(hOffscreenDC,hOffscreenBuffer);
nOffscreenCX = nWidth;
nOffscreenCY = nHeight;
//释放桌面的设备文本
ReleaseDC(0,hDesktopDC);
//将整个屏幕画为白色
PatBlt(hOffscreenDC,0,0,nWidth,nHeight,WHITENESS);
}

//释放离屏表面
void DeinitOffsceen(void)
{
//释放虚拟屏幕的设备文本
DeleteDC(hOffscreenDC);
//删除虚拟屏幕的内存对象
DeleteObject(hOffscreenBuffer);
}

//更新屏幕(将离屏虚拟屏幕的内容拷贝到真正的屏幕)
void UpdateDisplay(HWND hWnd)
{
HDC hDC;

hDC = GetDC(hWnd);
//将虚拟屏幕位块传送到窗口hDC的屏幕上
BitBlt(hDC,0,0,nOffscreenCX,
nOffscreenCY,h0ffscreenDC,0,0,
SCRCPY);
ReleaseDC(hWnd,hDC);
}

//绘制圆形物体
static void _drw_object(HDC hDC, int nX, int nY)
{
HGDIOBJ hOldPen, hOldBrush;

hOldPen = SelectObject(hDC,hpeBlue);
hOldBrush = SelectObject(hDC,hbrRed);
Ellipse(hDC,nX-20,nY-20,nX+20,nY+20);
SelectObject(hDC,hOldBrush);
SelectObject(hDC,hOldPen);
}

//窗口过程
LRESULT MainWndProc(HWND hWnd, UINT message, WPARAM uParam, LPARAM lParam)
{
switch(message)
{
case WM_LBUTTONDOWN:
_drw_object(hOffscreenDC,LOWORD(lParam),HIWORD(lParam));
UpdateDisplay(hWnd);
break;

case WM_MOUSEMOVE;
PatBlt(hOffscreenDC,0,0,240,320,WHITENESS);
_drw_object(hOffscreenDC,LOWORD(lParam),HIWORD(lParam));
UpdateDisplay(hWnd);
break;

case WM_LBUTTONUP:
PatBlt(hOffscreenDC,0,0,240,320,WHITENESS);
UpdateDisplay(hWnd);
break;

case WM_CREATE:
hbrRed = CreateSolidBrush(RGB(255,0,0));
hpeBlue = CreatePen(0,0,RGB(0,0,255);
InitOffscreen(240,320);
break;

case WM_DESTROY:
DeleteObject(hbrRed);
DeleteObject(hpeBlue);
DeinitOffscreen();
PostQuitMessage(0);
break;

case WM_KEYDOWN:
DestroyWindow(hWnd);
break;

default:
DefWindowProc(hWnd, message, uParam, lParam);
}

return 0;
}


这些代码显示了一个以GDI为基础的离屏表面,它是怎么工作的呢?

让我们解释这些代码,InitOffscreen函数中,使用简单的GDIs和APIs——CreateCompatibleDC和CreateCompatibleBitmap创建一个虚拟屏幕。

DC,也就是设备文本,是一种调用设备例程(或者是设备驱动例程)的方法。因为我已经在前面的Windows的GDIs部分大概介绍了它,这里我们来学习更多有关它的其他方面。设备文本也有三个主要的类型:它们是显示设备文本、打印设备文本、和内存设备文本。如果我们在打印文本上画线,设备文本将会调用打印机驱动程序中的画线程序来完成这一过程。如果是在显示文本上画线,调用的自然也就是显示驱动程序中的例程。如:

hDC = CreateDC("Printer",0,0,0); // 创建缺省的打印文本
hDC = CreateDC("Display",0,0,0); // 创建的显示文本
hDC = GetDC(hWnd); // 获得窗口客户区的显示文本
hDC = GetWindowDC(hWnd); // 获得整个窗口的显示文本(包含非客户区)

换句话说,GDIs可以理解为是一个通过DC选择器来实现的硬件设备的通路。这个通路可以让你以共同的方法控制图形设备,包含一些非原始的设备。

要了解更多有关Windows图形设备接口(GDI)的和设备驱动的概念,请参考MSDN。

而什么是内存设备文本呢?可以这么说,它就是虚拟屏幕!CompatibleDC函数创建一个和源设备文本一致的新的设备文本,但它只存在于内存中,并不关联设备。下一步,我们需要分配一个内存块,也就是用来储存图象的屏幕缓冲区。Windows的位图对象就是实现这个的一种方法。我们可以用CreateCompatibleBitmap来创建一个和屏幕相同象素位数,相同颜色格式、相同调色板数目的位图,通过SelectObject将它选入内存设备文本。这样,一个离屏的虚拟屏幕就建成了。

之后,内存设备文本就可以象普通的显示设备文本一样使用了,我们可以将它用在各种GDIs和APIs中,在它上面做图,将它画到其他设备文本上,等等。如,在UpdateDisplay中,我们就用位块传送函数BitBlt将它拷贝到了窗口的客户区域。这个函数在各种设备文本上以相同的形式,非常快速的复制以矩形区域为单位的数据。

在例子程序中,WM_CREATE消息只是在我们的主窗口在内存中被创建时,发送到我们的窗口函数,在整个程序的生存周期内只有一次,因此适合做一些初始化的工作。而WM_DESTROY消息则在窗口别销毁时被发送,可以放入对应内存释放的工作。而WM_MOUSEMOVE
、WM_LBUTTONDOWN和WM_LBUTTONUP是鼠标事件消息。我们将在笔尖接触屏幕、笔尖移过屏幕和笔尖离开屏幕时分别收到它们。

(ixmy)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/200812/7199.html]
本文出处: 作者:ixmy
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容