Silverlight开发MMORPG游戏研讨(3):图片资源智能预下载策略

书归正传,继续讨论我们的MMORPG游戏。经过前面两节的技术铺垫,我们可以在这个基础上考虑如何实现游戏中动态下载和缓存资源了。针对不同的资源,不同的用处,我们将采用不同的策略。首先是确定资源部署的位置。根据前一篇文章讨论,我们可以把资源部署到web服务器上xap包所在的目录中,即ClientBin目录中。
 
其次是考虑各种需要动态下载的资源。背景音乐和各种音效,策略最为简单,直接引用web服务器地址就好了。具体代码如下:
 
 
   
MediaElement sound = new MediaElement() {
IsHitTestVisible
= false ,
Visibility
= Visibility.Collapsed,
AutoPlay
= true ,
Volume
= 1
};
sound.Source
= new Uri( @" /Media/somemusic.mp3 " , UriKind.Relative);
sound.Position
= TimeSpan.Zero;
sound.Play();

 

 
第一次引用时会有明显延迟,不过这并不影响游戏者的体验,只是短暂的无声时代而已,一般来说可以忍受。
 
图片资源大致上可以分为三类。第一类是被所有场景公用的,必须优先下载的图片,例如窗口界面图片,各种缩略图,以及各种替代图片。这些图片的特点是尺寸较小,可以很快下载下来。所以我们可以把这些图片直接包含到xap包中(即上文介绍的方法2),保证silverlight程序加载后可以直接引用。当然也可以使用方法3,引用web服务器地址。由于这些图片一般都很小,下载时间很短,不会十分影响视觉效果。
 
第二类是场景地图。一般来说尺寸都很大(几百KB)。下载策略是,首先显示缩小的低质量的替代图片,同时后台下载大图片。等待大图片下载完毕后,用大图片替换掉替代图片。深蓝色右手在 教程中介绍了这种方法和代码,作者就不重复了。
 
第三类是动画图片。一般来说每个动画都包含一系列帧,每个帧都不是很大。但是对一个角色而言,所有动画的图片可能几十甚至上百,下载起来需要一定时间。在图片没有下载并缓存到内存之前,动画显示会不正常。动画图片的特点有二:一是数量较大,从而需要相对较长的下载时间。二是如果希望动画正常,图片必须是缓存在内存中而不是浏览器缓存目录中,因为从浏览器缓存目录读取图片文件依然会有较短的时间延迟,造成动画闪烁。
 
如果希望动画正常显示,又希望动态下载图片,就需要有一个合理的图片文件预先下载和缓存的策略。记得在系列文章的第一篇(游戏布局)留下的悬念吗?现在可以解开谜底了,就是采用了精灵图片的预先下载和缓存。目前应用还比较初级,最初的行走动画好像是在溜冰,原因是后续帧图片还没有下载下来,继续保留前一帧图片来替代,尽管差强人意,还是比闪烁或者通用替代图片的视觉效果好一点。而且因为是预先下载,不需要等到显示某动画时才下载图片,所以后面的动画基本流畅。
 
图片文件预先下载和缓存在技术上没有任何难点,只需要注意把图片缓存到内存中,而不是仅仅缓存到浏览器缓存目录。而下载策略才是我们讨论的重点。假设在主程序加载之后,有十个动画的图片需要预先下载,每个动画包括八个帧,那么总共需要下载80个图片。
 
方法1是同时下载这80个图片文件,也就是建立80个WebClient对象,同时发出80个下载连接请求。这种方法逻辑上和实现起来最简单,但是问题很大。第一,同时发起80个下载连接请求给系统造成很大负担。第二,并发80个连接短时内耗费大量带宽资源,给服务器增加很大压力。一句话,两边不讨好。
 
方法2是智能下载策略。这种方法需要一个全局下载队列,保存等待下载的文件的路径。首先根据当前的场景,状态等参数把需要预先下载的动画图片排一个队,那些可能马上用到的动画图片,例如精灵行走,需要排在前面下载。而那些不太可能马上显示的动画,则排在队列后面。有些不可能用到的图片,例如精灵不会的魔法,可以暂时不下载,等精灵学会的时候再下载也不迟。
只要算法得当,高优先级的动画图片会很快被下载并缓存到内存中,而低优先级的图片则在后面慢慢等待下载。为了提高性能,还可以进一步优化算法。例如当需要显示某动画时,发现该动画还没有下载,察看下载队列,发现该动画在初始下载队列中排在后面。这时候可以考虑调整下载顺序,把该动画移动到下载队列的前面。
 
为此专门写了一个类实现排队下载,仅供参考:
 
 
   
public class ImageDownloader
{
#region singleton mode
private static ImageDownloader instance;
private ImageDownloader() { }
public static ImageDownloader Instance
{
get
{
if (instance == null )
{
instance
= new ImageDownloader();
}
return instance;
}
}
#endregion singleton mode

#region private members
private List < string > DownloadQueue = new List < string > ();

private const string ImageRootPath = " Images/ " ;
#endregion private members

public event EventHandler < OpenReadCompletedEventArgs > OnImageDownloaded;

#region functions
/// <summary>
/// Download a image from beginning of the queue
/// </summary>
public void DownloadFirstImageInQueue()
{
if (DownloadQueue.Count > 0 )
{
// download the first image
string imgPath = ImageRootPath + DownloadQueue[ 0 ];
WebClient webClient
= new WebClient();
webClient.OpenReadCompleted
+= new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
Uri imgUri
= new Uri(imgPath, UriKind.Relative);
webClient.OpenReadAsync(imgUri, DownloadQueue[
0 ]);

}
}

void webClient_OpenReadCompleted( object sender, OpenReadCompletedEventArgs e)
{
if (e.Error == null )
{
BitmapImage bitmapImage
= new BitmapImage();
bitmapImage.SetSource(e.Result);
Application.Current.Resources.Add(e.UserState, bitmapImage);


string imagePath = e.UserState.ToString();
// remove it from the queue
DownloadQueue.Remove(imagePath);
if (DownloadQueue.Count > 0 )
{
// start the next download
DownloadFirstImageInQueue();

}
else
{
// inform outside that image is downloaded.
if (OnImageDownloaded != null )
{
OnImageDownloaded(
this , e);
}
}
}
}


/// <summary>
/// Append a block of images to the download queue, and
/// start download if queue was empty
/// </summary>
/// <param name="imgPaths"> a block of images </param>
public void DownloadQueueAppendBlock(List < string > imgPaths)
{
if (DownloadQueue.Count == 0 )
{
// if DownloadQueue is empty, add them and start download
DownloadQueue.AddRange(imgPaths);
DownloadFirstImageInQueue();
}
else
DownloadQueue.AddRange(imgPaths);
}
#endregion functions
}

 

简单说明一下,该类使用了单件模式,保证全局只有一个实例。通过该实例下载图片文件,保证图片被顺序下载而不是并发下载。某个图片文件下载完成后引发事件,通知外部模块。具体应用时可能需要一些改进,这里只是简单示范。
 
实现动画的帧可以放到分离的图片文件中,也可以来自一张组合图片。如果是组合图片,需要下载的图片数量则可能减少几十倍,但是总体大小不会变化太大。这种情况下依然需要确定一个下载顺序,保证最可能用到的动画优先下载。考虑到http get协议包数量会大幅度减少,从而节省大量浏览器到服务器的round trip时间,总下载时间会减少很多。也就是说组合图片的方式会高效许多。
 
如果只有分离的动画图片,又不想费力制作成组合图片,或者已有代码只能处理分离图片,这种情况下可以实现组合图片的下载效果吗?通过前一节介绍,我们已经有了答案:可以把动画图片分类打包(zip),使用上节的方法4动态下载。具体做法就不说了,参考上节的源码很容易做到。
 
作者在这里只是抛砖引玉,提出策略的初步构想,具体实现还要深思熟虑,根据实际情况而定,欢迎大家提出自己的高见。至此关于动态下载的讨论就结束了。时间仓促,很多地方写的不是很具体。如有疑问请留言,我们可以一起商酌。欢迎指正或补充,多多益善。由于在游戏表现方面深蓝色右手已经给出很多教程和示例,作者刚开始接触,就不班门弄斧了。
 
下一个关注的重点该是什么呢?再卖一个关子,目前想法已有,但还不成熟,估计要等一段时间才能告诉大家答案了。谢谢支持!

转载于:https://www.cnblogs.com/erichan/archive/2010/03/25/1694536.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值