从本篇开始,我尝试以5W 的思想来开始我以后对Silverlight 5 的学习和研究。当然,纯粹的5W 思想有些太臃肿了。我希望以“提出问题→解决方案→工作原理→具体实现”这样的思路来进行。提出问题对应why,解决方案对应what,工作原理对应who,具体实现对应where,至于when,我相信凡是热爱游戏的人,何时应用不是一个问题。
问题
Silverlight 5 之前,即使应用了GPU 加速,大约只能百人同屏,如果机器性能优异,可能会更好一点。如何普遍的提高所有客户端2D图像效率,在Silverlight 4时代,有很多致力于Silverlight 游戏的开发者进行了不少的探索,例如深蓝色右手(吐槽:纳美人的右手也是蓝色的…)。Silverlight 5 的到来,是否我们能够在2d 绘制上更快更强呢?
解决方案
Xna 中有SpriteBatch类,它可以以一个有效率的方式绘制图像。
Silverlight 5中目前没有SpriteBatch类,因此我们要自己实现一个。用3D绘制2D,是因为每个图像是矩形的,所以可以用两个三角形表示一个矩形,然后用一张2D图像作为纹理覆盖其上。SpriteBatch类主要辅助进行这个操作。因为你实际上绘制的是三角形,所以使用SpriteBatch类让你可以充分利用硬件加速。
注意: 2D图形对象通常叫做Sprite,精灵不仅是一张固定2d图形,也可以是动态的。在3d中,用2D图像来覆盖在一个表面时,叫做纹理。所以当讨论屏幕上的游戏图形对象时,指的是sprite。当讨论绘制sprite要用的图像时,指的是纹理。
工作原理
这里首先说下Silverlight 的新变化,在Silverlight 4 和Silverlight以前的版本中,只有一个 UI 线程,不像其父亲WPF 不但有UI线程还有独立的渲染线程。而Silverlight 4 中的UI 线程几乎负责一切工作,处理输入和输出,调度动画运行等等,这会影响一定的动画性能。Silverlight 5 beta 版本中,线程架构有一个新的变化,加入了 composition thread。Silverlight 5 中的Composition线程,分担了UI线程的部分工作,负责在GPU 加速功能被开启时,承担GPU 相关操作的工作任务。例如,一个动画设置了GPU-cached,运行时将运行在 composition 线程上。
所以,要充分利用Composition 线程:
在Silverlight 4 中,开启硬件加速和帧信息统计后,会在右上角显示统计信息。
< param name ="EnableFrameRateCounter" value ="true" />
< param name ="EnableGPUAcceleration" value ="true" />
Silverlight 文档里对这4个部分解释为:frameRate videoMemoryUsed GPUEnabledSurfaces intermediateSurfaces
在Silverlight 5 里统计信息变成了 5 个部分:
第一个值变成了composition 线程 帧速。
如果在DrawingSurface_Draw 事件中休眠一定的时间,我们就能发现,第一个值会下降。这说明,DrawingSurface 对象的 Draw 事件是由composition 线程 回调触发的,所以要注意的是,如果在Draw 中不能保证3D 的绘制效率,那么你在 Draw 之外应用了GPU 加速的动画将会变慢。例如你有一个很酷的控件,其动画效果将被影响。
SpriteBatch类 比较简单,模仿Xna 写一个一样的就可以了。我减去了旋转,镜像效果,以及纹理深度。旋转和镜像一般很少用,纹理深度是用来排序绘制顺序,实际上,通过自己“手动”控制绘制顺序可能会更好。
private void DrawingSurface_Draw( object sender, DrawEventArgs e)
{
// Thread.Sleep(50);
e.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, new Microsoft.Xna.Framework.Color( 0 , 0 , 0 , 0 ), 1.0f , 0 );
spriteBatch.Begin(e.GraphicsDevice);
spriteBatch.Draw(myTexture, new Vector2( 0 , 0 ), null , Microsoft.Xna.Framework.Color.White);
spriteBatch.Draw(myTexture, new Microsoft.Xna.Framework.Rectangle( 150 , 0 , 100 , 100 ), null , Microsoft.Xna.Framework.Color.White);
spriteBatch.Draw(myTexture, new Microsoft.Xna.Framework.Rectangle( 250 , 0 , 50 , 50 ), null , Microsoft.Xna.Framework.Color.White);
spriteBatch.Draw(myTexture, new Microsoft.Xna.Framework.Rectangle( 150 , 150 , 200 , 200 ), new Microsoft.Xna.Framework.Rectangle( 0 , 0 , 100 , 100 ), Microsoft.Xna.Framework.Color.White);
spriteBatch.End();
e.InvalidateSurface();
使用Draw 重载方法绘制,得到不同的效果。(资源来自于深蓝色的右手,本资源仅供学习,请勿商用。)