程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> C#開發WPF/Silverlight動畫及游戲系列教程(Game Tutorial):(三十九)

C#開發WPF/Silverlight動畫及游戲系列教程(Game Tutorial):(三十九)

編輯:關於C#

C#開發WPF/Silverlight動畫及游戲系列教程(Game Tutorial):(三十九)向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;
    }
   }
 }
}

出處:http://alamiye010.cnblogs.com/

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved