更美妙的是,此方法可以與A*尋徑相結合,從而創造出更加優美的角色移動(例如帝國時代中采取的就是它獨特的改進型A*,所以根據游戲自身的特點您可以對A*進行優化,Gameres論壇有很多高手的文章,大家可以參考一下)。接下來大家來回憶一下第七節講到的A*尋徑算法,此方法找到的路徑中如果有經過障礙物倒還好,但是如果沒有障礙物的,那麼此路徑全程中將或多或少會有些折疊的地方(如下圖)。
人類總是希望將東西做得完美導致本節的重點出現了:如何通過神奇的副本地圖來優化A*尋路,讓它更加貼近真實呢?這裡我們需要先理解一個關鍵知識點:主角所處的地圖中所有的點與副本地圖中所有的點都是一一對應(映射)的關系。例如假設主角在地圖中的坐標為(356,248),那麼此坐標對應副本地圖坐標也同樣為(356,248),這樣我們就可以通過函數方法,將主角的坐標點作為參數在副本地圖中找該點的顏色,看看顏色分別是黑的,還是白的,或是黃的等等,從而映射回主角的地圖可知主角當前處於地圖中是障礙物,還是可同行區域,或是傳送點等等。了解了原理後,下面我就用代碼來實現它:
首先我們需要寫出兩種移動方法(具體代碼就不列出來了,在本教程的目錄中有下載):第一種我定義為NormalMove移動方法,它就是我第一節中講到的點與點之間的直線移動,在此方法中我稍微改動了一些,使坐標定位到主角的腳底,並且將移動目標終點記錄到Point Target中。第二我將之定義為AStarMove移動方法,它就是我前面幾節講到的A*尋路方式。設置好後,我們就可以根據從副本地圖中獲取的點的顏色判斷來調用相應的移動模式了。
那麼在主角移動的時候,它的X,Y坐標屬性是時時更新的,因此我們需要一個線程去捕獲它,並且在此線程中時時判斷主角是否采在了黑色點上(障礙物)。那麼這裡我采用了第二節中所講到的CompositionTarget界面線程,注冊了該線程dispatcherTimer1_Tick事件後,接下來就進入關鍵代碼了:
private void Carrier_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Point p = e.GetPosition(Carrier); //假如點到的地方不是障礙物 if (pickColor(Deeper, (int)p.X, (int)p.Y) != Colors.Black) { target = p; NormalMove(p); //直線移動 //AStarMove(p); //A*尋路移動 } } BitmapSource Deeper = new BitmapImage((new Uri(@"Map\Deeper.jpg", UriKind.Relative))); //設置地圖副本 int X, Y; //主角當前的窗口真實坐標(非縮放) Point target; //主角移動的最終目的 private void dispatcherTimer1_Tick(object sender, EventArgs e) { X = Convert.ToInt32(Canvas.GetLeft(Spirit) + SpiritCenterX * GridSize); Y = Convert.ToInt32(Canvas.GetTop(Spirit) + SpiritCenterY * GridSize); //message.Text = "坐標:" + X + " " + Y; message.Text = pickColor(Deeper, X, Y).ToString(); //假如碰到障礙物則采用A*尋路 if (pickColor(Deeper, X, Y) == Colors.Black) { AStarMove(target); } else if (pickColor(Deeper, X, Y) == Colors.Yellow) { //假如是傳送點則條到坐標(200,20) storyboard.Stop(); Canvas.SetLeft(Spirit, 200 - SpiritCenterX * GridSize); Canvas.SetTop(Spirit, 20 - SpiritCenterY * GridSize); } //用白色點記錄移動軌跡 rect = new Rectangle(); rect.Fill = new SolidColorBrush(Colors.Snow); rect.Width = 5; rect.Height = 5; CarrIEr.Children.Add(rect); Canvas.SetLeft(rect, X); Canvas.SetTop(rect, Y); }