在第八节、第三节、第二节中,我曾向大家详细的分析了Silverlight在内存释放、性能提升及源码保护等 方面的相关处理。随着游戏教程的不断深入,自身各方面经验的不断累积、总结与升华;至今日,我对这3方 面的认识又有了更深层次的理解。作为前3篇的续,本节我将继续对Silverlight开发技巧进行深度挖掘,用行 动来证明对技术的追求永不止步。
一)内存释放
首先,希望大家强烈认识到Silverlight程序是托管的,除特别明显或强烈的需要外(例如OpenFileDialog 、SaveFileDialog等继承自IDisposable接口的类),望大家不要轻易介入GC.Collect(),这是微软设计.NET的 初衷,我们仅能且必须做的就是:信任。
其次,需要所有精通.NET开发语言的朋友再次确认自己是否对数据类型有深刻理解。以C#为例,在编写代 码时尤其要分清值类型与引用类型,包括它们的定义、组成及原理。因为值类型对象在赋值后其内存可以很快 释放掉。以Struct(结构)和Class(类)为例,如果不需要实现继承,使用值类型可接口的Struct将得到比Class 更高的性能。另外,在书写类内部时,尽量不要使用私有类对象;同时要注意如果一个类一直被其他类所引用 着,内存同样无法被释放。在与其他Silverlight开发者交流中经常会听到类似这样的抱怨:Storyboard太耗 内存了,Storyboard性能太低下了,Storyboard太……无语了。俗话说:成也萧何,败也萧何,这些症状均是 Storyboard类被滥用的结果。这里的“滥”包含两层意思:1、不会用;2、用过度。一个好工具要物尽其用, 则必须精通它的原理与细节。Storyboard是否无意中被放进了循环里?是否出现了委托泄漏?Storyboard在 Completed后其他对象是否仍在引用着它?Storyboard是否忘记停止啦?等等。包括DispatcherTimer也一样, Silverlight在为我们制作各种流畅动画效果提供便利工具的同时,我们是否也应该让代码做到更严谨些呢?
小结:Silverlight内存释放最终还得看自己,简单说就是:只要够精通,够专业,没有释放不掉的内存。 作者也在朝这个方向努力着。
二)性能提升
1)在第三节中我曾有提到利用BackgroundWorker在后台线程中执行相对耗时的操作进而减少Silverlight 界面线程的负担。其实,我们同样还可以通过异步的方式提高画面的流畅度,比如:
this.Dispatcher.BeginInvoke(() => {
//TODO…
});
Dispatcher是WPF/Silverlight机制的核心,深入理解它有助你编写更高性能的代码。
2)在动画及游戏开发中尽量减少不必要的呈现、重绘(例如在Canvas.SetLeft前先判断Canvas.GetLeft是 否与Value值相同);隐藏掉不在屏幕中的图象载体(obj.Visibility = Visibility.Collapsed),对图象进行 切割,减少窗体中的图片数量,对相同或类似图片尽量做到复用,监控好委托的订阅与取消订阅(订阅N次将执 行N次操作,如果发生在循环里极易造成内存泄露甚至程序崩溃,请初学者认识到这点)等等,虽然均是些细节 ,但效果往往却非常明显。
3)调整好最大FPS,Silverlight中的MaxFrameRate可以通过 Application.Current.Host.Settings.MaxFrameRate 进行动态调整,此技巧在游戏及动画设计中如果能应用 得当,有时会取得意想不到的效果。
4)Silverlight中制作动画常用的方法大致有5种:DispatcherTimer、Storyboard、Thread、 CompositionTarget.Rendering、Storyboard as Trigger。5 methods to create game loop: which is the best?这篇文章中很详细的列举及讨论了该5种方法在性能、稳定性等各方面的比较,结论加个人体会大致上是 DispatcherTimer与Storyboard占据了Top2的地位。下面是该演示的Demo及其作者的总结翻译(原文在Demo的链 接中,仅供大家参考,个人感觉有些评价并不太符合实际),为大家今后制作Silverlight动画提供更好的依据 :
方法 | 优点 | 弊端 | 什么时候使用合适?(建议) | 什么时候最好不要 使用(建议) |
DispatcherTimer | 相对稳定 | 最小识别率约15毫秒(个人理解为 频率过快将导致跳帧或无效,即极限 为15豪秒) |
缓慢移动的物体, 联合Storyboard一同控制动画 |
在处理一个移动 非常快的物体对象时, 建议使用 Storyboard as Trigger 或 CompositionTarget .Rendering提高帧速率。 |
Storyboard | 平滑的动画 | 没有复杂的运动效果,例如行星、 游戏等的无规则Path运动 (这结论也太…… ^^||) |
简单的移动模式方面 | 如果想通过更多的 后台代码去控制动画, 建议还是使用 CompositionTarget .Rendering。 (这建议真 的很晕……) |
Thread | 基于线程,均衡的每帧 运行。在多核中效果更好 |
在很短的间隔内不稳定(2帧间) | 需要大量计算,耗CPU的处理 | 如果你的电脑只有 1个CPU,或者需要 更快的动画效果, 还是请选择其他的方法 |
CompositionTarget .Rendering |
非常稳定,保证会运行到位 | 无法进行细节控制,因为频率 是固定的,基于画面刷新触发, 公式:1000/<frames-per- secspecified- in-html> [msec] |
复杂的逻辑,如游戏中的一些 循环等逻辑(原话看得不是很懂……) |
一些琐碎的处理, 例如游戏中的云朵 从左飘到右, 则建议 使用Storyboard代替。 |
Storyboard as Trigger | 类似DispatcherTimer,在300FPS下运行流畅 | 和DispatcherTimer一样 | 类似DispatcherTimer | 和DispatcherTimer 一样 |
三)源文件保护
在第二节中我分析了几种保护Silverlight源码的方法,代码混淆是最终解决方案。但随着Silverlight应 用越来越广泛且丰富,以增强用户体验为目标,我们通常会将图片等素材从xap压缩包中分离出来,作为动态 资源进行按需下载。然而这将直接导致所有素材裸露于Temporary Internet Files中,源码保住了,但是又泄 露了素材资源,结果is so bad。
解决方案:以下载图片为例,在Silverlight游戏设计教程第九节结尾我曾详细的讲解了BitmapImage的诸 多细节,将BitmapImage的CreateOptions属性设定为BitmapCreateOptions.None,图片将分成两份,一份存于 内存中,一份存于Temporary Internet Files;然后再利用第二节方法一提到的禁止关键文件或文件夹页面缓 存,我们将让用户永远也无法触摸到Silverlight中的任何图片素材,而所有资源素材一经下载后将永驻内存 直至页面关闭。不过副作用还是有的,没了浏览器缓存,每次进入游戏都需要重新下XAP及相关资源;图片呈 现方面也稍微有些延迟。当然了,每次2、3百K的XAP+按需获取资源所增加的服务器流量负荷相对于完全泄露 项目资源来说一毛九牛。
以上为近阶段个人在Silverlight开发方面的心得,希望能对大家有所帮助,如有不对之处还望指正。