在第八节、第三节、第二节中,我曾向大家详细的分析了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开发方面的心得,希望能对大家有所帮助,如有不对之处还望指正。
出处:http://alamiye010.cnblogs.com/
本系列目录及源码下载:点击进入(欢迎加入WPF/Silverlight小组 WPF/Silverlight博客团队)
本文版权归作者和博客园共有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面显著位置给出原文连接,否则保留追究法律责任的权利。