首页 > 学院 > 开发设计 > 正文

C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十九)向Silverlight移植①

2019-11-17 04:08:08
字体:
来源:转载
供稿:网友
一、主要改进:

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;

                }

            }

        }

}
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表