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

WPF/Silverlight 遊戲開發教程 (13):牽引式地圖移動模式 — 雙重實作與虛擬攝影機原理

     編輯:關於C#

核心概念:遊戲視窗即虛擬攝影機

2D 俯視角遊戲(RTS、SLG、ARPG)中,地圖遠大於螢幕。遊戲視窗如同固定支架上的攝影機鏡頭,只能看見鏡頭範圍內的地圖區域。要探索其他區域,必須移動地圖背景,而非移動攝影機。當滑鼠游標進入視窗邊緣 8 個區域(上、下、左、右、左上、右上、左下、右下)時,觸發地圖向相反方向平移,產生「視窗向該方向探索」的視覺錯覺。

地圖移動的兩種實作策略

策略一:實體滾動介質 —— 直觀但效能較低

在視窗邊緣建立 8 個透明 Rectangle(滾動介質),Opacity=0.3 供測試觀察,ZIndex=10001 置頂。UI 執行緒中輪詢 roller.IsMouseOver,若為真則移動地圖:

int scrollSpeed = 5;
private void Timer_Tick(object sender, EventArgs e)
{
    if (roller.IsMouseOver)
        Canvas.SetTop(Map, Canvas.GetTop(Map) - scrollSpeed); // 正下方介質 → 地圖向上移
}

優點:邏輯直觀、程式碼簡單。缺點:大量 UI 元素命中測試、難以處理對角線區域重疊、效能隨介質數量線性增長。

策略二:座標計算法 —— 高效能、推薦用於實戰

不建立額外 UI 元素,直接在 UI 執行緒中根據 Mouse.GetPosition(Carrier) 計算滑鼠所在 8 區域代號(0~7,對應時鐘 0~7 點),再結合地圖邊界檢查決定是否移動:

區域判斷方法

int distance = 80; // 觸發邊緣寬度
private int GetMouseArea()
{
    Point mp = Mouse.GetPosition(Carrier);
    if (mp.X < 0 || mp.Y < 0) return -1;

    // 水平中央區:190~570,垂直中央區:125~375(視窗 800x600 為例)
    if (mp.X >= 190 && mp.X <= 570)
    {
        if (mp.Y <= distance) return 0;       // 上
        if (mp.Y >= 500 - distance) return 4; // 下
    }
    else if (mp.Y >= 125 && mp.Y <= 375)
    {
        if (mp.X <= distance) return 6;       // 左
        if (mp.X >= 760 - distance) return 2; // 右
    }
    // 四個角落區域(邊界條件需仔細處理)
    else if ((mp.X < 190 && mp.Y <= distance) || (mp.Y < 125 && mp.X <= distance)) return 7; // 左上
    else if ((mp.X > 570 && mp.Y <= distance) || (mp.Y < 125 && mp.X >= 760 - distance)) return 1; // 右上
    else if ((mp.X > 570 && mp.Y >= 500 - distance) || (mp.Y > 375 && mp.X >= 760 - distance)) return 3; // 右下
    else if ((mp.X < 190 && mp.Y >= 500 - distance) || (mp.Y > 375 && mp.X <= distance)) return 5; // 左下

    return -1;
}

地圖移動與邊界限制

private void Timer_Tick(object sender, EventArgs e)
{
    double mapLeft = Canvas.GetLeft(Map);
    double mapTop = Canvas.GetTop(Map);
    int area = GetMouseArea();

    switch (area)
    {
        case 0: // 上
            if (mapTop < 0) Canvas.SetTop(Map, mapTop + scrollSpeed);
            break;
        case 1: // 右上
            if (mapTop < 0) Canvas.SetTop(Map, mapTop + scrollSpeed);
            if (Map.Width + mapLeft > ActualWidth) Canvas.SetLeft(Map, mapLeft - scrollSpeed);
            break;
        case 2: // 右
            if (Map.Width + mapLeft > ActualWidth) Canvas.SetLeft(Map, mapLeft - scrollSpeed);
            break;
        case 3: // 右下
            if (Map.Width + mapLeft > ActualWidth) Canvas.SetLeft(Map, mapLeft - scrollSpeed);
            if (Map.Height + mapTop > ActualHeight) Canvas.SetTop(Map, mapTop - scrollSpeed);
            break;
        case 4: // 下
            if (Map.Height + mapTop > ActualHeight) Canvas.SetTop(Map, mapTop - scrollSpeed);
            break;
        case 5: // 左下
            if (Map.Height + mapTop > ActualHeight) Canvas.SetTop(Map, mapTop - scrollSpeed);
            if (mapLeft < 0) Canvas.SetLeft(Map, mapLeft + scrollSpeed);
            break;
        case 6: // 左
            if (mapLeft < 0) Canvas.SetLeft(Map, mapLeft + scrollSpeed);
            break;
        case 7: // 左上
            if (mapTop < 0) Canvas.SetTop(Map, mapTop + scrollSpeed);
            if (mapLeft < 0) Canvas.SetLeft(Map, mapLeft + scrollSpeed);
            break;
    }
}

邊界條件:mapLeft < 0 表示地圖未超出左邊界;Map.Width + mapLeft > ActualWidth 表示地圖右側未超出視窗右邊界。同理垂直方向。

地圖移動後的物體跟隨問題

地圖移動時,所有遊戲物件(主角、NPC、怪物、障礙物)必須同步更新視覺位置:

// 伪代碼:UI 執行緒每幀
foreach (var obj in GameObjects)
{
    Canvas.SetLeft(obj.Visual, obj.LogicX + Canvas.GetLeft(Map));
    Canvas.SetTop(obj.Visual, obj.LogicY + Canvas.GetTop(Map));
}

若物件數量極大(千個以上),逐幀遍歷會造成效能瓶頸。實戰優化方案:

  • 視野剔除:僅渲染螢幕範圍內(含邊緣緩衝)的物件,超出範圍者從 Carrier.Children 移除,進入範圍者加入。
  • 虛擬化面板:使用 VirtualizingPanel 或自訂面板按需實例化視覺元素。
  • 座標批次更新:將邏輯座標與視覺偏移分離,利用 GPU 實例化渲染(需底層圖形 API 支援)。

本教程為教學示範,採用全量更新,後續章節將引入精靈控件統一管理。

總結與下章預告

牽引式地圖移動的核心公式:視覺位置 = 邏輯位置 + 地圖偏移量。策略二(座標計算法)效能優於策略一,適合實戰。下一章將引入第 14 章建立的精靈控件,實現主角在地圖上的自主寻路移動,並解決移動中拖曳地圖的座標同步問題。

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