4.4.2 强制重绘
在将后台缓冲区中的图像呈现到游戏窗体中时,很多时候并不只是显示移动矩形区域,而是需要强行将整个区域进行呈现。例如下列实例:
● 当游戏首次启动时 如果不对整个窗体进行重绘,那么可能会因为那些初始静态对象绘制失败而终止程序。并且会发现绘制在Windows Mobile的Today页面上,而不是在空白窗口中。
● 当游戏被重置时 在这个阶段,所有的游戏对象都要发生明显的变动。执行一次完整的重绘才能确保每个对象都能正确显示。
● 当游戏窗体大小变化时 如果窗体大小发生改变(例如,由于设备方向由横屏变为
竖屏),就需要重新绘制,对整个游戏画面进行重新显示,才能适应新的窗体大小。
我们通过调用CGameEngineGDIBase.ForceRepaint方法来实现重绘,该方法要做三件事情:
● 将一个名为_forceRerender的类变量设置为true。下次调用Render函数时会检查该变量,确保对整个后台缓冲区进行重建。
● 将一个名为_forceRepaint的类变量设置为true。下次调用Present函数时会检查该变量,确保将整个后台缓冲区进行重建。
● 使整个游戏窗体失效。
4.4.3 性能影响
Bounce项目中所引用的游戏引擎项目已经包含了上面所介绍的优化渲染时所需要的代码,当项目运行时会显示帧率,因此我们可以看到执行效率优化后的效果。为了与未优化渲染时进行对比,我们只要在CGameEngineGDIBase.Advance方法起始的地方插入代码来调用ForceRepaint函数即可。
在设备上分别运行优化后的代码与未优化的代码,通过在不同的设备上进行测试,我得到了下列对比结果:
● HTC Touch Pro2 (WVGA) 未优化时31帧/s,优化后45帧/s(性能提高了45%)。
● i-mate PDA2 (QVGA) 未优化时32帧/s,优化后40帧/s(性能提高了25%)。
从这个数据可以看出在屏幕越大性能提高越多(可以预料到)。与本示例相比,那些移动区域小的游戏会有更加明显的提升。
4.5 引擎的其他功能
本游戏引擎已经为我们提供了一个简单而且经过了优化的图形框架,还可以让它执行一些其他的任务。接下来我们就看看还可以添加哪些其他功能。
4.5.1 与设备进行交互
在第1章中曾经讨论过,要记得我们是运行在一个多任务的设备中,能够执行各种各样其他功能,其中一些传统的功能要比游戏更重要。因此,我们需要能够处理设备上各种可能发生的交互事件。在本节中将介绍这个遗留的主题,以保证我们的游戏可以与设备和谐地进行交互。
1. 最小化及还原
玩家在任何时刻都可能会决定单击X按钮将游戏最小化,或者用其他的方式退出我们的游戏。同样,其他应用程序也可能会突然获得焦点,从而迫使游戏最小化(例如来电)。当发生这些情况时,我们需要做两件事:
● 停止游戏的运行。暂停任何操作,尽可能减少CPU使用率。
● 等待游戏重新激活,然后使游戏继续,并且强制对屏幕进行一次重绘,这样所有对象都会显示。
将游戏进程挂起是由游戏本身负责的,因为游戏引擎无法知道设备何时有来电。我们已经在窗体的RenderLoop函数中看到了如何处理这种情况。
然而,引擎可以在其Advance方法中确认游戏窗体是否拥有焦点。如果没有,它可以暂停游戏的运行,为操作系统让出资源。这些发生在CGameEngineGDIBase.Advance方法中,如程序清单4-19所示。
程序清单4-19 在Advance方法中检测焦点
/// <summary>
/// Advance the simulation by one frame
/// </summary>
public override void Advance()
{
// If the game form doesn't have focus, sleep for a moment and
// return without any further processing
if (!GameForm.Focused)
{
System.Threading.Thread.Sleep(100);
return;
}
[... – the rest of the Advance procedure continues here ...]
}
通过调用Sleep函数将应用程序进程挂起1/10秒,允许操作系统执行其他等待执行的任务。由于游戏实际进入了循环休眠状态,因此CPU使用率会非常低。不过,我仍然推荐对主游戏窗体本身检测是否拥有焦点而不是依靠该方法。
当游戏窗体重新激活时,需要强制执行一次重绘,以完整地渲染背景。这需要在CGameEngineGDIBase类的构造函数中为游戏窗体添加Activated事件处理程序。要注意,是在引擎基类中添加该事件处理程序而不是在窗体本身中添加,因此我们不能像通常那样使用窗体属性窗口,而必须以编程的方式添加:
// Add an Activated handler for the game form
gameForm.Activated += new System.EventHandler(GameFormActivated);