XNA Unleashed 部分翻译(1)

 

    要成功地编写出游戏,我们得注意要完成的各种工作的开销。在写代码之前,我们需要有努力的最终目标,就像Stephen Covey在他的书里说的,我们要“Begin with the end in mind.”。这对个人成长是很关键的,当考虑到性能时,这句话也同样重要。

    本章中,我们将测试代码在.NET框架上运行的速度,以及代码在XBOX使用的.NET Compact框架上的速度。

 

测量

    在现实世界中,测量总是和地点相关。而在涉及性能时,

在我们开始写代码之前,我们必须定一个基准量。

我们的目标是至少让游戏能以每秒n帧的速率运行,帧速率是指每秒钟在屏幕上的绘制帧的次数。当今游戏的标准是60fps

游戏循环是更新对象并在屏幕上渲染出来的循环,它同时还处理诸如输入、音效、AI(人工智能)和物理计算。每次迭代都在渲染出一帧,所以我们的目标就是要在每秒调用渲染循环60次。XNA为我们提供了这个循环。

在发布托管DirectX之后,关于如何在Windows环境下提供最佳的游戏循环有过几次讨论,XNA很可能诞生自当初的那些讨论,XNA小组把XNA提供给我们。XNA框架的游戏循环提供一个Update方法和一个Draw方法让我们重写。当建立一个游戏主类时,得从Microsoft.Xna.Framework.Game类继承,除了UpdateDraw外,该类还提供了其它一些可供重写的方法。

80-20原则

一百多年前,一位名叫Vilfredo Pareto意大利的经济学家声称,意大利百分之八十的财富集中在意大利百分之二十的人手中,他还发现其他国家同样也同样如此。这个现象被称作80-20原则。关于这个原则还有其他几种说法,像“百分之八十的工作是由百分之二十的人完成的”。成功的领导用他们百分之八十的时间来培养百分之二十的员工。尽管不知道这个原则是如何运作的,但它确实存在。当回到程序性能的问题上时,它同样存在,百分之二十的代码需要优化,因为它是关系到程序性能的最关键部分。

在写代码时必须注意到性能问题,但也不要陷到性能优化中,Tony Hoare 常引用一句话“Premature optimization is the root of all evil”,这句话是说性能测试不重要。可是没有比在软件开发接近尾声时才发现程序性能表现差劲更糟糕的事了,性能测试十分重要。

不管开发什么程序,不要掉进认为性能优化是罪恶的根源的这种陷阱中。事实不是这样的,这句话有点像人们引用圣经中的话“金钱是罪恶的根源”。金钱不是罪恶的根源,人对金钱的“爱”才是罪恶的根源。

定基准

我们从新建一个Window游戏工程开始,工程名为PerformanceBenchmark。向其中加一个帧速率计数器,同时更新窗口标题来显示帧速率。

在构造方法里加入这些属性:

//Do not synch our Draw method with the Vertical Retrace of our monitor(使Draw方法不与显示器的垂直回扫同步)

graphics.SynchronizeWithVerticalRetrace = false;

//Call our Update method at the default rate of 1/60 of a second.(以默认1/60秒的间隔调用Update方法)

IsFixedTimeStep = true;

 

模板已经为我们做好了GraphicsDeviceManager对象,我们设定它的SynchronizeWithVerticalRetrace属性值为false,该属性的默认值是true,它使Draw方法的调用与显示器刷新保持同步。显示器的刷新率如果是60Hz,就表示显示屏每1/60秒刷新一次,或者说显示屏每秒刷新60次。XNA默认将该属性设为true(就是说每秒调用Draw方法60次)以避免画面不平稳,而且这也是我们需要的。但是为了测试改变一小段代码会对帧速率产生怎样的影响时,如果我们还是让程序以每秒60帧的速率显示,我们就看不到改变代码所带来的影响。(应该让程序尽可能快的重复调用Draw方法,而不是每秒就调60次),否则只有到一些代码使Draw方法运行缓慢,以至于没法做到60次每秒时才会发现出了问题。

我们同样把IsFixedTimeStep设为truetrue是该属性的默认值。该属性告诉XNA在绘制完成后立即调用Update方法(false)或者每当到达固定时间间隔时调用Update方法(true)。固定时间间隔存放在一个名为TargetElapsedTime的属性中,默认为1/60秒,我们可以修改它的值。在当前的这个程序中,我们没有必要让框架不停地调用Update方法,因为在当前程序里我们没在Update方法里做任何事情。

现在在Game1.cs文件里加入这些成员:

private float fps;

private float updateInterval = 1.0f ;

private float timeSinceLastUpdate = 0.0f ;

private float framecount = 0;

 

 

最后,在Draw方法里加入计算帧速率的代码:

float elapsed = (float)gameTime.ElapsedRealTime.TotalSeconds;

framecount++;

timeSinceLastUpdate += elapsed;

if (timeSinceLastUpdate > updateInterval)

{

    fps = framecount / timeSinceLastUpdate; //mean fps over updateIntrval

    Window.Title = “FPS: “ + fps.ToString() + “ - RT: “ +

    gameTime.ElapsedRealTime.TotalSeconds.ToString() + “ - GT: “ +

    gameTime.ElapsedGameTime.TotalSeconds.ToString();

    framecount = 0;

    timeSinceLastUpdate -= updateInterval;

}

    在上述代码中,我们做的第一件事情就是保存从上次调用Draw方法开始到现在所经过的时间。然后做帧计数,再检查是否已经过updateInterval所指定的时间,这里updateInterval被设为1秒。一旦过了这个updateInterval指定的间隔,只要将这段时间间隔内绘制的帧数除以时间间隔就能得到FPS,然后以FPS作为窗口的标题。

    我们写ElapsedGameTime的同时写出了ElpaseRealTime,用来计算FPS。来看看这两个属性的作用。记住SynchronizeWithVerticalRetrace属性决定了Draw方法被调用的频率,IsFixedTimeStep属性决定了Update方法被调用的频率。我们可以看到,ElapsedRealTime属性是用来表示调用Draw方法的间隔的,而ElapsedGameTime属性Update方法的间隔的。

    最后,重置帧计数器和timeSinceLastUpdate。运行程序

 

【质疑:graphics.SynchronizeWithVerticalRetrace = false;按该属性的字面意思,只是用于设置刷新画面是是否等待显示器的垂直回扫,以避免画面“闪动”,这个属性是否能让程序以60Hz的频率调用Draw方法呢。我调了一下上面的程序,该属性不管是true还是false,画面的刷新频率都是60Hz】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值