在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
深蓝Silverlight-MMORPG游戏引擎单机部分即将开源;这两节里,我将为大家讲解本教程示例游戏从WPF向Silverlight移植的一些关键性优化与性能提升技巧,以及新增加的内容等等。接下来的时间里,我将接着前面的教程,通过Silverlight平台继续为大家演义,目标只有一个:誓将Silverlight游戏引擎完美到底! 一、主要改进: 1)Silverlight3.0上的右键实现: //注册右键事件 HtmlPage.Document.AttachEvent("oncontextmenu", Game_MouseRightButtonDown); //鼠标右键事件 private void Game_MouseRightButtonDown(object sender, HtmlEventArgs e) { e.PreventDefault(); //取消右键弹出菜单 ……逻辑部分 } 通过上述方法还必须配合<param name="Windowless" value="true" />或System.Windows.Interop.Settings.Windowless = true才能实现右键功能。另外需要特别说明的是,此方法并非官方所提供的解决方案,而是第三方间接的实现方式。因此,在使用前,您必须解为Silverlight解禁右键将付出的代价:①Windowless = true将降低程序整体性能;②无法使用输入法;③无法被所有的浏览器所兼容,例如在Google Chrome中,虽然可以激发出右键功能,但是取消不了弹出右键菜单。综上,在Silverlight3.0中,您还是得谨慎再谨慎的考虑是否使用右键。 2)撤消精灵及其他所有控件中的x,y,z坐标定位用关联属性,取而代之的是一个名为Coordinate的关联属性,其完整定义如下: /// <summary> /// 获取或设置控件坐标(关联属性) /// </summary> public Point Coordinate { get { return (Point)GetValue(CoordinateProperty); } set { SetValue(CoordinateProperty, value); } } public static readonly DependencyProperty CoordinateProperty = DependencyProperty.Register( "Coordinate", typeof(Point), typeof(QXSprite), new PropertyMetadata(ChangeCoordinateProperty) ); private static void ChangeCoordinateProperty(DependencyObject d, DependencyPropertyChangedEventArgs e) { QXSprite obj = (QXSprite)d; if (obj.Visibility == Visibility.Visible) { Point p = (Point)e.NewValue; obj.SetValue(Canvas.LeftProperty, p.X - obj.CenterX); obj.SetValue(Canvas.TopProperty, p.Y - obj.CenterY); obj.SetValue(Canvas.ZIndexProperty, Convert.ToInt32(p.Y)); } } Coordinate的类型为Point,因此,我将原先精灵移动用的DoubleAnimation动画类型替换成了PointAnimation;这样,不论是在代码结构还是性能上均得到很大的优化。更改控件坐标时,只需修改它的Coordinate = new Point(x,y)即可,系统会判断该关联属性的值是否发生改变而激发ChangeCoordinateProperty方法,从而更新该控件最终在画面中的LeftProperty、TopProperty和ZIndexProperty。没错,关联属性就是这么强大。 3)A*移动的优化。我已留下接口,根据不同的参数设置,可以启动不同效率、不同路径长短、不同精确度的A*寻路,这里我给大家推荐两种现成的方案,第一种—程序默认A*寻路方案,此方案找到的路径最精确,但性能消耗最高;另一种方案可以实现最高效的寻路,但得到的路径并非最短: PathFinderFast pathFinderFast = new PathFinderFast(varyObstruction) { HeavyDiagonals = false, HeuristicEstimate = 100, }; 我在Silverlight引擎中封装的A*寻路DLL,是根据教程第七节的老外A*改编而成。因此,您完全可以将之作为一个调试器,调试不同的搭配方案,然后将参数赋予pathFinderFast里:
例如上图,我通过模拟测试,发现最终找到路径所消耗的时间为0.0071秒,假如我已对此设置所产生的路径长度与性能感到满意,那么接下来要做的就是将此方案的配置记录下来: Diagonals = true ; Heavy Diagonals = true ; Henuristic = 5 ; Formula = Max(DX,DY) ; Use Tie Breaker = false ; Search Limit = 40000 ; 寻路对象使用的是FastPathFinder。 OK,最后来在Silverlight引擎中,我就可以这样来启动A*寻路: PathFinderFast pathFinderFast = new PathFinderFast(varyObstruction) { Diagonals = true, HeavyDiagonals = true, HeuristicEstimate = 5, Formula = HeuristicFormula.MaxDXDY, TieBreaker = false, SearchLimit = 40000, }; 嘿嘿,其实使用A*是可以如此简单的,不是吗? 二、主要优化: 1) 地图切片实现了最优化加载方法。即不需要额外做多余判断,也无需每次对切片容器进行Clear。只需按从0到8的顺序对这9个切片重新赋值Source即可,性能真的很优哦: private void ChangeMapSection() { …… countSection = 0; for (int x = startSectionX; x <= endSectionX; x++) { for (int y = startSectionY; y <= endSectionY; y++) { mapSection[countSection].Source = Super.GetImage(string.Format("/Image/Map/{0}/Surface/{1}_{2}.jpg", mapCode, x, y)); Canvas.SetLeft(mapSection[countSection], x * mapSectionWidth); Canvas.SetTop(mapSection[countSection], y * mapSectionHeight); countSection++; } …… } 2)改进了 “托盘式”主位地图移动模式。首先我想向一些朋友道歉,一时找不到是哪篇文章后面评论中有提到对一个Canvas进行移动而不是遍历所有精灵,这样可以提升逻辑方面的性能;我当时有测试过,为什么一直坚持不行,因为我没转过弯,主角和其他所有对象是完全可以放在一个Canvas里的,这也意味着它们的ZIndex顺序照样可以很好的处理,同时实现“托盘式”地图移动模式。最终在QQ群里“内Cool超人”的感化下,我才得以觉醒。这样,虽然画面性并无提升,但是,配合上Coordinate坐标关联属性的回调方法使用,可以去掉循环遍历地图上所有对象位置,在逻辑上大大的提升了性能。 3)隐藏远离画面窗口的精灵对象。这是基于Web游戏所必须做的处理,它将大大减少不必要元素的呈现及逻辑运算: …… //隐藏及显示区域范围内精灵 if ((Math.Abs(sprite.Coordinate.X - Leader.Coordinate.X) > this.ActualWidth / 2) || (Math.Abs(sprite.Coordinate.Y - Leader.Coordinate.Y) > this.ActualHeight / 2)) { sprite.Visibility = Visibility.Collapsed; sprite.Timer.Stop(); }else { if (!sprite.Timer.IsEnabled) { sprite.Visibility = Visibility.Visible; sprite.Timer.Start(); } …… } …… 在间隔0.5秒的辅助计时器事件中进行类似如上判断,当某个精灵超出了主角可视范围,即在我们屏幕窗口所能看到的区域以外,则将之隐藏掉,并停止它的切帧动作,否则反之。这对提升游戏整体性能起着决定性关键作用。如果是网络版,我们则可以拓展出2级范围,其中1级范围即为上述范围;而2级范围则为:当某个已被隐藏的精灵远离主角到了更遥远的地方,则我们将之移除掉,从而减少逻辑且实现不必要资源的及时释放与回收。
4)改进了时时障碍物系统。整个游戏有两个障碍物数组(可以记录0-255,0代表障碍物,除0外的所有其他字节均代表无障碍。这里我使用1标识无任何对象可通行区域,10-19用来标识传送点。如果以后需要加入新的地形效果拓展,那么同样可以使用类似设定:例如20用来标识可通行水域,21标识可通行沙漠等等;这样,现当主角在这些区域中移动时,会发出相应的脚步声,使游戏效果更为逼真)。动态障碍物系统实现代码如下,首先定义一个固定数组和一个动态数组: byte[,] fixedObstruction, varyObstruction; fixedObstruction是地图加载后永远不变的地图信息描述载体,它记录了地图中肯定无法通过的地形及传送点的位置等等。varyObstruction是时时的动态地图信息,会根据所有精灵时时的位置来填充障碍物。 在每次A*移动时,我们通过先去掉精灵脚底的障碍物区域(HoldWidth和HoldHeight),然后启动A*寻路,找到路径后再补回精灵的脚底障碍物区域: …… SetSpriteObstruction(sprite, 1); AStarMove(sprite, GetSpriteEdge(enemy)); sprite.UseAStarMove = true; SetSpriteObstruction(sprite, 0); …… 其中SetSpriteObstruction方法为: /// <summary> /// 设置精灵占位障碍物对应值 /// </summary> private void SetSpriteObstruction(QXSprite sprite, byte sign) { int x = (int)(sprite.Coordinate.X / gridSizeX); int y = (int)(sprite.Coordinate.Y / gridSizeY); for (int m = x - sprite.HoldWidth; m <= x + sprite.HoldWidth; m++) { for (int n = y - sprite.HoldHeight; n <= y + sprite.HoldHeight; n++) { if (fixedObstruction[m, n] != 0) { varyObstruction[m, n] = sign;
2023-10-27 2022-08-15 2022-08-17 2022-09-23 2022-08-13 |
请发表评论