Silverlight 2.5D RPG游戏技巧与特效处理:(十三)资源动态下载组件

    Silverlight网游可媲美客户端的核心组件之一便是资源动态下载。Silverlight为我们提供了稳健且丰富的WebClient,虽然它本身无法实现并发操作,然而凭借其强大的特性我们可轻松编写出一款资源下载器,满足网游设计中各式各样的资源获取需求。

如何构建这条资源下载通道是游戏设计之初我们应该考虑的问题。你希望它是一条普普通通的马路人来车往随意穿行游走;还是希望它是一条标示清晰、分道合理、次序井然的高速公路?

当然,你会说越快越好,最好永远不堵车,路窄时就一个挨一个慢慢走;路宽了便分道扬镳。

我们不妨将下载通道比作道路,能并排几辆车通过即为“并行模式”;只能一辆一辆的通过则为“串行模式”。很明显,并行的速度要比串行快得多,不过消耗和设计方面则要比串行多且复杂,各有利弊。另外,下载通道如果仅是单纯的串行,则速度太慢且浪费资源;而如果仅是单纯的并行,过多的并发时快时慢、有大有小极易导致UI卡死以及下载堵塞。

再次回到我们的高速公路上,不知你是否有过这样的体会,行驶时常会见到很多大货车以60公里/小时的速度顺次行驶在慢车道上,而小车们则通常会以80-100公里/小时不等的速度在快车道与超车道上彼此间穿行。

    人类自身的活动规律来检验理论的合理性非常具有参考价值,由此可见兼具“串行”与“并行”的游戏资源动态下载器才是我们所追求的设计目标。比如游戏中随时可能大量涌现的动画(animation)资源,我们可将它们放在串行队列中依次获取;而角色装备、地图区块等更迫切需要呈现给玩家的资源则可放在并行通道中并发下载。

    依据以上分析,思路清晰后仅仅只需百来行代码便可轻松编写出一套完整的资源动态下载组件- SerialDownloaderParallelDownloader,它们共用一个完成资源表,且串行下载集成了优先机制(DownloadPriority),并行下载也根据需要封装了并行队列模式(QueueParallelDownloader)

09130700_5vyL.gif DownloadBase
     ///   <summary>
    
///  下载器基类
    
///   </summary>
     public   class  DownloadBase {

        
protected   readonly   static  List < string >  loadedUri  =   new  List < string > ();

        
///   <summary>
        
///  获取已下载完成的地址
        
///   </summary>
         public   static  List < string >  LoadedUri {  get  {  return  loadedUri; } }

        
///   <summary>
        
///  下载失败(错误)次数
        
///   </summary>
         public   static   int  Error {  get protected   set ; }

    }
09130700_5vyL.gif SerialDownloader
     ///   <summary>
    
///  下载优先级
    
///   </summary>
     public   enum  DownloadPriority {
        
///   <summary>
        
///  最高
        
///   </summary>
        Highest  =   ,
        
///   <summary>
        
///  高
        
///   </summary>
        High  =   1 ,
        
///   <summary>
        
///  普通
        
///   </summary>
        Normal  =   2 ,
        
///   <summary>
        
///  低
        
///   </summary>
        Low  =   3 ,
        
///   <summary>
        
///  最低
        
///   </summary>
        Lowest  =   4 ,
    }

    
///   <summary>
    
///  串行资源下载器
    
///   </summary>
     public   class  SerialDownloader : DownloadBase {

        
///   <summary>
        
///  资源下载完成
        
///   </summary>
         public   static   event  OpenReadCompletedEventHandler OpenReadCompleted;

        
///   <summary>
        
///  资源下载进度中触发
        
///   </summary>
         public   static   event  DownloadProgressChangedEventHandler DownloadProgressChanged;

        
///   <summary>
        
///  当前进度百分比
        
///   </summary>
         public   static   int  ProgressPercentage {  get private   set ; }

        
static  WebClient webClient  =   null ;
        
readonly   static  List < string >  loadingUri  =   new  List < string > ();
        
readonly   static  List < string >  waitingUri  =   new  List < string > ();
        
///   <summary>
        
///  获取当前正在下载的地址
        
///   </summary>
         public   static  List < string >  LoadingUri {  get  {  return  loadingUri; } }
        
///   <summary>
        
///  获取等待下载地址队列
        
///   </summary>
         public   static  List < string >  WaitingUri {  get  {  return  waitingUri; } }

        
///   <summary>
        
///  为Image图片控件设置图像源
        
///   </summary>
        
///   <param name="image"> 目标图片 </param>
        
///   <param name="uri"> 图像源地址 </param>
        
///   <param name="isWaiting"> 是否等待下载完成后再赋值 </param>
         public   static   void  SetImageSource(Image image,  string  uri, DownloadPriority priority,  bool  isWaiting) {
            
if  (loadedUri.Contains(uri)) {
                image.Source 
=  GlobalMethod.GetWebImage(uri);
            } 
else  {
                image.Source 
=   null ;
                AddUri(uri, priority);
                
if  (isWaiting) {
                    DispatcherTimer timer 
=   new  DispatcherTimer() { Interval  =  TimeSpan.FromMilliseconds( 2000 ) };
                    EventHandler handler 
=   null ;
                    timer.Tick 
+=  handler  =  (s, e)  =>  {
                        
if  (loadedUri.Contains(uri)) {
                            timer.Stop();
                            timer.Tick 
-=  handler;
                            image.Source 
=  GlobalMethod.GetWebImage(uri);
                        }
                    };
                    timer.Start();
                }
            }
        }

        
///   <summary>
        
///  添加预备下载地址
        
///   </summary>
        
///   <param name="uri"> 图像源地址 </param>
         public   static   void  AddUri( string  uri, DownloadPriority priority) {
            
if  ( ! waitingUri.Contains(uri)) { waitingUri.Insert(( int )((( int )priority  /  4d)  *  waitingUri.Count), uri); }
            
if  (loadingUri.Count  ==   ) {
                webClient 
=   new  WebClient();
                webClient.DownloadProgressChanged 
+=   new  DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);
                webClient.OpenReadCompleted 
+=   new  OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
                webClient.OpenReadAsync(
new  Uri(GlobalMethod.WebPath(uri), UriKind.Relative), uri);
                loadingUri.Add(uri);
            }
        }

        
static   void  webClient_OpenReadCompleted( object  sender, OpenReadCompletedEventArgs e) {
            WebClient wc 
=  sender  as  WebClient;
            wc.DownloadProgressChanged 
-=  webClient_DownloadProgressChanged;
            wc.OpenReadCompleted 
-=  webClient_OpenReadCompleted;
            
string  uri  =  e.UserState.ToString();
            
if  (e.Error  !=   null ) {
                
// 断网处理,5秒后重试
                Error ++ ;
                GlobalMethod.SetTimeout(
delegate  {
                    loadingUri.Remove(uri);
                    AddUri(uri, DownloadPriority.Highest);
                }, 
5000 );
            } 
else  {
                loadingUri.Remove(uri);
                waitingUri.Remove(uri);
                loadedUri.Add(uri);
                
if  (waitingUri.Count  >   ) { AddUri(waitingUri[ ], DownloadPriority.Highest); }
                
if  (OpenReadCompleted  !=   null ) { OpenReadCompleted(sender, e); }
            }
        }

        
static   void  webClient_DownloadProgressChanged( object  sender, DownloadProgressChangedEventArgs e) {
            ProgressPercentage 
=  e.ProgressPercentage;
            
if  (DownloadProgressChanged  !=   null ) { DownloadProgressChanged(sender, e); }
        }

    }
09130700_5vyL.gif ParallelDownloader
    ///   <summary>
    
///  并行资源下载器
    
///   </summary>
     public   sealed   class  ParallelDownloader : DownloadBase {

        
///   <summary>
        
///  资源下载进度中触发
        
///   </summary>
         public   event  DownloadProgressChangedEventHandler DownloadProgressChanged;

        
///   <summary>
        
///  资源下载完成
        
///   </summary>
         public   event  OpenReadCompletedEventHandler OpenReadCompleted;

        
///   <summary>
        
///  当前进度百分比
        
///   </summary>
         public   static   int  ProgressPercentage {  get private   set ; }

        
readonly   static  List < string >  loadingUri  =   new  List < string > ();
        
readonly   static  List < string >  waitingUri  =   new  List < string > ();
        
///   <summary>
        
///  获取当前正在下载的地址
        
///   </summary>
         public   static  List < string >  LoadingUri {  get  {  return  loadingUri; } }
        
///   <summary>
        
///  获取等待下载地址队列
        
///   </summary>
         public   static  List < string >  WaitingUri {  get  {  return  waitingUri; } }

        
///   <summary>
        
///  下载资源文件
        
///   </summary>
        
///   <param name="uri"> 资源相对地址 <</param>
        
///   <param name="userToken"> 资源参数 </param>
        
///   <param name="waitingTime"> 如果正在被下载,等待检测时间(单位:毫秒) </param>
         public   void  OpenReadAsync( string  uri,  object  userToken,  bool  isWaiting,  int  waitingTime) {
            
if  (loadedUri.Contains(uri)) {
                Download(uri, userToken);
            } 
else  {
                
if  (loadingUri.Contains(uri)) {
                    
// 假如该资源正被下载中,则需要等待,每隔1秒检测一次是否已下载完成
                     if  (isWaiting) {
                        DispatcherTimer timer 
=   new  DispatcherTimer() { Interval  =  TimeSpan.FromMilliseconds(waitingTime) };
                        EventHandler handler 
=   null ;
                        timer.Tick 
+=  handler  =  (s, e)  =>  {
                            
if  (loadedUri.Contains(uri)) {
                                timer.Stop();
                                timer.Tick 
-=  handler;
                                Download(uri, userToken);
                            }
                        };
                        timer.Start();
                    }
                } 
else  {
                    
if  ( ! waitingUri.Contains(uri)) { waitingUri.Add(uri); }
                    loadingUri.Add(uri);
                    Download(uri, userToken);
                }
            }
        }

        
///   <summary>
        
///  开始下载
        
///   </summary>
        
///   <param name="uri"> 资源相对地址 </param>
        
///   <param name="userToken"> 资源参数 </param>
         void  Download( string  uri,  object  userToken) {
            OpenReadCompletedEventHandler openReadCompletedHandler 
=   null ;
            DownloadProgressChangedEventHandler progressChangedHandler 
=   null ;
            WebClient webClient 
=   new  WebClient();
            webClient.DownloadProgressChanged 
+=  progressChangedHandler  =  (s, e)  =>  {
                ProgressPercentage 
=  e.ProgressPercentage;
                
if  (DownloadProgressChanged  !=   null ) { DownloadProgressChanged( this , e); }
            };
            webClient.OpenReadCompleted 
+=  openReadCompletedHandler  =  (s, e)  =>  {
                WebClient wc 
=  s  as  WebClient;
                wc.DownloadProgressChanged 
-=  progressChangedHandler;
                wc.OpenReadCompleted 
-=  openReadCompletedHandler;
                
if  (e.Error  !=   null ) {
                    
// 断网处理,5秒后重试
                    Error ++ ;
                    GlobalMethod.SetTimeout(
delegate  {
                        Download(uri, userToken);
                    }, 
5000 );
                } 
else  {
                    waitingUri.Remove(uri);
                    loadingUri.Remove(uri);
                    
if  ( ! loadedUri.Contains(uri)) { loadedUri.Add(uri); }
                    
if  (OpenReadCompleted  !=   null ) { OpenReadCompleted( this , e); }
                }
            };
            webClient.OpenReadAsync(
new  Uri(uri, UriKind.Relative), userToken);
        }
    }

经过测试,它们的运行非常稳健,当然这也少不了WebClient的功劳,尤其体现在其中的抗网速阻塞、抗断网处理机制(5秒重试)。大家不妨在游戏资源下载过程中打开迅雷并全速全线程下载上传一堆东西,或者直接将网卡禁用,此时观察游戏中资源下载状况;然后关闭迅雷并启用网卡再观察所有的一切下载均立刻恢复正常。同时也证明了,规划合理的串行和并行如同设计一条完美的高速公路,它是成就高性能Silverlight网游引擎所不可或缺的功能模块之一。

没错,Silverlight在网游开发中拥有如此优越的特性,不久的将来大家将看到更多的网游奇迹因Silverlight而诞生,一起见证。

本节源码请到目录中下载

在线演示地址:http://silverfuture.cn


原文链接: http://www.cnblogs.com/alamiye010/archive/2011/05/02/2034467.html

转载于:https://my.oschina.net/chen106106/blog/43598

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值