首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

WPF/Silverlight 遊戲開發教程 (16):牽引式地圖移動模式 — A* 尋路整合與座標系轉換

     編輯:關於C#

背景回顧:牽引式地圖移動核心公式

上一章建立精靈控件的 X、Y 依賴屬性,並在 UI 執行緒中以:

Canvas.SetLeft(Spirit, Spirit.X + Canvas.GetLeft(Map));
Canvas.SetTop(Spirit, Spirit.Y + Canvas.GetTop(Map));

實現精靈跟隨地圖移動。其中 Map 的 Canvas.Left/Top 隨滑鼠拖曳變化,Spirit.X/Y 代表角色在地圖局部座標系中的位置。要讓角色在地圖上「走路」,只需更新 Spirit.X/Y,UI 執行緒即自動同步顯示位置。

A* 尋路邏輯改造:從 Canvas 繫結轉為精靈屬性繫結

原始做法(固定地圖)

早期章節地圖不動,直接對 Canvas.Left/Top 建立 Storyboard 動畫,屬性路徑為 PropertyPath("(Canvas.Left)")。

新做法(牽引式地圖)

改為對精靈控件的 X、Y 依賴屬性建立動畫:

Storyboard.SetTargetProperty(keyFramesAnimationX, new PropertyPath("X"));
Storyboard.SetTargetProperty(keyFramesAnimationY, new PropertyPath("Y"));

動畫不再直接操控視覺位置,而是產生「角色在地圖座標系中的軌跡序列」,UI 執行緒負責疊加地圖偏移量渲染。

座標系轉換輔助屬性

int GridSize = 20;        // 邏輯格子大小
int SpiritCenterX = 4;    // 角色腳底相對圖片左側格數
int SpiritCenterY = 5;    // 角色腳底相對圖片頂部格數

// 遊戲邏輯座標系(格子單位)
int GameSystemX => (int)(Spirit.X / GridSize) + SpiritCenterX;
int GameSystemY => (int)(Spirit.Y / GridSize) + SpiritCenterY;

Spirit.X/Y 以像素為單位,除以 GridSize 換算為格子座標,再加上腳底偏移量,得到 A* 演算法所需的網格節點索引。

AStarMoveTo 核心改造碼

private void AStarMoveTo(Point p) 
{
    // 起點:角色當前遊戲座標
    int start_x = GameSystemX;
    int start_y = GameSystemY;
    Start = new System.Drawing.Point(start_x, start_y);

    // 終點:滑鼠點擊處(相對地圖圖片座標)換算為格子座標
    int end_x = (int)p.X / GridSize;
    int end_y = (int)p.Y / GridSize;
    End = new System.Drawing.Point(end_x, end_y);

    if (path == null) return; // 路徑不存在

    // ... A* 尋路取得 path (List<Point>) ...

    // 建立 X 軸關鍵幀動畫
    var keyFramesX = new DoubleAnimationUsingKeyFrames();
    keyFramesX.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * cost));
    Storyboard.SetTarget(keyFramesX, Spirit);
    Storyboard.SetTargetProperty(keyFramesX, new PropertyPath("X"));

    // 建立 Y 軸關鍵幀動畫
    var keyFramesY = new DoubleAnimationUsingKeyFrames();
    keyFramesY.Duration = new Duration(TimeSpan.FromMilliseconds(path.Count * cost));
    Storyboard.SetTarget(keyFramesY, Spirit);
    Storyboard.SetTargetProperty(keyFramesY, new PropertyPath("Y"));

    for (int i = 0; i < framePosition.Count; i++)
    {
        var kfX = new LinearDoubleKeyFrame();
        // 平滑銜接:第 0 幀用當前 Spirit.X,避免跳幀
        kfX.Value = i == 0 ? Spirit.X : (framePosition[i].X - SpiritCenterX) * GridSize;
        kfX.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(cost * i));
        keyFramesX.KeyFrames.Add(kfX);

        var kfY = new LinearDoubleKeyFrame();
        kfY.Value = i == 0 ? Spirit.Y : (framePosition[i].Y - SpiritCenterY) * GridSize;
        kfY.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(cost * i));
        keyFramesY.KeyFrames.Add(kfY);
    }

    storyboard.Children.Add(keyFramesX);
    storyboard.Children.Add(keyFramesY);
    storyboard.Begin();
}

關鍵細節:平滑銜接邏輯

i == 0 ? Spirit.X : ... 確保動畫起始幀精確對齊角色當前像素位置,而非網格中心。若省略此判斷,角色在移動中途被重新指派新目標時,會瞬間「傳送」到最近的網格中心,造成視覺跳動。此技巧同樣適用於 Y 軸。

滑鼠點擊觸發尋路

private void Carrier_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    Point p = e.GetPosition(Map); // 取得點擊處在 Map 圖片上的相對座標
    AStarMoveTo(p);
}

或使用 TranslatePoint 從 Carrier 座標轉換至 Map 座標,效果相同。TranslatePoint 在碰撞檢測、射程計算等幾何運算中更具優勢。

運行效果驗證

  • 滑鼠任意點擊地圖,角色沿 A* 最短路徑避開障礙物移動
  • 移動過程中拖曳地圖,角色與障礙物完美跟隨,無錯位
  • 移動中途再次點擊新目標,角色平滑轉向新路徑,無跳幀

此時已具備「大地圖自由拖曳」+「角色自主寻路移動」的雙重機制,為後續回合制戰棋、即時戰略等玩法奠定基礎。

下章預告

下一章將解決長期懸而未決的大問題:精靈八方向動作切換。將結合第 17 章的圖集素材,實現根據移動向量自動切換 8 方向待機/跑步/攻擊等動畫,真正實現「八面玲瓏」的完美精靈。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved