DirectX 7 時代提供 DirectDraw 進行 2D 高效繪圖,雖已被 Direct2D/Direct3D 取代,但其 API 設計清晰、透過 DxVBLib (DirectX 7 for Visual Basic 型別庫) 可在 C# 輕鬆呼叫。本文示範:建立 DirectDraw 介面、協作層級設定、Primary/Offscreen Surface 建立、Clipper 裁切、Blt/BltFast 複製、ColorKey 透明色、鍵盤驅動 Sprite 動畫等核心技術。
專案準備與參考
- 安裝 DirectX 7 SDK (或更新版 SDK 含向後相容)。
- 專案 → 加入參考 → COM → DirectX 7 for Visual Basic Type Library (命名空間
DxVBLib)。
- 移除不需的參考,保留
System, System.Windows.Forms, DxVBLib。
核心物件模型
| 介面 | 用途 |
DirectX7 | 根工廠,DirectDrawCreate() |
DirectDraw7 | 裝置管理、協作層級、Surface 建立 |
DirectDrawSurface7 | 繪圖緩衝區 (Primary/Back/Offscreen) |
DirectDrawClipper | 裁切區域限制繪圖範圍 |
DDSURFACEDESC2 | Surface 建立參數 (Capability, 寬高、像素格式) |
RECT | 來源/目的矩形區域 |
DDCOLORKEY | 透明色範圍 |
初始化流程
private DxVBLib.DirectX7 dx = new DxVBLib.DirectX7();
private DxVBLib.DirectDraw7 dDraw;
private DxVBLib.DirectDrawSurface7 primarySurf, offscreenSurf, spriteSurf;
private DxVBLib.DirectDrawClipper clipper;
private void InitDirectDraw()
{
// 1. 建立 DirectDraw 介面
dDraw = dx.DirectDrawCreate(""); // GUID 空字串使用預設裝置
// 2. 設定協作層級:視窗模式 (非獨佔)
dDraw.SetCooperativeLevel(this.Handle.ToInt32(),
DxVBLib.CONST_DDSCLFLAGS.DDSCL_NORMAL);
// 全螢幕獨佔需:DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE | DDSCL_ALLOWMODEX
// 3. 建立 Primary Surface (螢幕前景緩衝)
var descPrimary = new DxVBLib.DDSURFACEDESC2();
descPrimary.lFlags = DxVBLib.CONST_DDSURFACEDESCFLAGS.DDSD_CAPS;
descPrimary.ddsCaps.lCaps = DxVBLib.CONST_DDSURFACECAPSFLAGS.DDSCAPS_PRIMARYSURFACE;
primarySurf = dDraw.CreateSurface(ref descPrimary);
// 4. 建立 Clipper 限制繪圖於 Client 區域
clipper = dDraw.CreateClipper(0);
clipper.SetHWnd(this.Handle.ToInt32());
primarySurf.SetClipper(clipper);
// 5. 從檔案載入 Offscreen Surface (背景圖)
var descBack = new DxVBLib.DDSURFACEDESC2();
offscreenSurf = dDraw.CreateSurfaceFromFile("background.bmp", ref descBack);
// 6. 從檔案載入 Sprite 圖集 (多張動作連在一張圖)
var descSprite = new DxVBLib.DDSURFACEDESC2();
spriteSurf = dDraw.CreateSurfaceFromFile("actor_move.bmp", ref descSprite);
// 7. 設定 Sprite 透明色 (ColorKey) - 假設黑色背景 RGB(0,0,0)
var ck = new DxVBLib.DDCOLORKEY();
ck.colorSpaceLowValue = 0;
ck.colorSpaceHighValue = 0x000000; // BGR 格式,視像素格式調整
spriteSurf.SetColorKey(DxVBLib.CONST_DDCKEYFLAGS.DDCKEY_SRCBLT, ref ck);
}
繪圖迴圈 (OnPaint / Timer 驅動)
private DxVBLib.RECT srcRect = new DxVBLib.RECT();
private DxVBLib.RECT destRect = new DxVBLib.RECT();
private int spriteFrame = 0;
private const int FRAME_WIDTH = 140; // 每格寬度
private const int FRAME_HEIGHT = 120; // 每格高度
private void Render()
{
// 1. 計算目的區域 (視窗客戶區置中)
dx.GetWindowRect(this.Handle.ToInt32(), ref destRect);
// 調整 destRect 為客戶區座標 (需扣除標題列/邊框)
// 2. 繪製背景
srcRect.Top = 0; srcRect.Left = 0;
srcRect.Right = descBack.lWidth; srcRect.Bottom = descBack.lHeight;
primarySurf.Blt(ref destRect, offscreenSurf, ref srcRect,
DxVBLib.CONST_DDBLTFLAGS.DDBLT_WAIT);
// 3. 繪製 Sprite 當前幀
srcRect.Top = 0; srcRect.Left = spriteFrame * FRAME_WIDTH;
srcRect.Right = srcRect.Left + FRAME_WIDTH;
srcRect.Bottom = FRAME_HEIGHT;
// 角色置中顯示
int cx = (destRect.Right - destRect.Left) / 2;
int cy = (destRect.Bottom - destRect.Top) / 2;
destRect.Left = cx - FRAME_WIDTH / 2;
destRect.Top = cy - FRAME_HEIGHT / 2;
destRect.Right = destRect.Left + FRAME_WIDTH;
destRect.Bottom = destRect.Top + FRAME_HEIGHT;
// 關鍵:DDBLT_KEYSRC 啟用 ColorKey 透明複製
primarySurf.Blt(ref destRect, spriteSurf, ref srcRect,
DxVBLib.CONST_DDBLTFLAGS.DDBLT_WAIT | DxVBLib.CONST_DDBLTFLAGS.DDBLT_KEYSRC);
}
鍵盤控制 Sprite 動畫
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
const int MOVE_STEP = 10;
switch (keyData)
{
case Keys.Left:
destRect.Left -= MOVE_STEP; destRect.Right -= MOVE_STEP;
spriteFrame = (spriteFrame + 1) % 4; // 假設 4 幀左移動畫
break;
case Keys.Right:
destRect.Left += MOVE_STEP; destRect.Right += MOVE_STEP;
spriteFrame = (spriteFrame + 1) % 4; // 右移動畫
break;
case Keys.Up:
destRect.Top -= MOVE_STEP; destRect.Bottom -= MOVE_STEP;
break;
case Keys.Down:
destRect.Top += MOVE_STEP; destRect.Bottom += MOVE_STEP;
break;
}
this.Invalidate(); // 觸發 OnPaint
return base.ProcessCmdKey(ref msg, keyData);
}
protected override void OnPaint(PaintEventArgs e)
{
Render();
base.OnPaint(e);
}
private void Form_Resize(object sender, EventArgs e)
{
this.Invalidate(); // 視窗大小改變重繪
}
資源釋放
protected override void Dispose(bool disposing)
{
if (disposing)
{
spriteSurf?.Release(); // COM 介面需 Release
offscreenSurf?.Release();
primarySurf?.Release();
clipper?.Release();
dDraw?.Release();
// dx 為 RCW,由 GC 管理
}
base.Dispose(disposing);
}
DirectDraw 關鍵旗標速查
| 旗標 | 用途 |
| DDSCL_NORMAL | 視窗模式、非獨佔、GDI 相容 |
| DDSCL_FULLSCREEN | 全螢幕模式 |
| DDSCL_EXCLUSIVE | 獨佔模式 (需搭配 FULLSCREEN) |
| DDSCL_ALLOWMODEX | 允許 ModeX (320x240x256 等特殊模式) |
| DDBLT_WAIT | 阻塞等待 Blitter 可用 |
| DDBLT_KEYSRC | 啟用來源 ColorKey 透明複製 |
| DDBLTFAST_WAIT | BltFast 等待模式 |
| DDSCAPS_PRIMARYSURFACE | 主要顯示 Surface |
| DDSCAPS_OFFSCREENPLAIN | 系統記憶體離屏 Surface |
| DDSCAPS_VIDEOMEMORY | 強制建立於顯示記憶體 |
現代化替代方案
- Direct2D (Windows 7+ /
SharpDX/Vortice):硬體加速 2D 向量/位圖繪圖,現代標準。
- Direct3D 11/12 SpriteBatch:
SharpDX.Toolkit、MonoGame 封裝,跨平台遊戲開發首選。
- Windows.UI.Composition / Win2D:UWP/WinUI 高效能合成引擎。
- SkiaSharp:跨平台 2D 繪圖庫 (Google Skia 封裝),支援 .NET 6+ 全平台。
常見問題
| 現象 | 原因 | 解決 |
| 畫面閃爍 | 無雙緩衝、Primary 直接繪製 | 建立 Back Surface 雙緩衝,或 Flip 鏈 |
| ColorKey 不生效 | 像素格式不符、High/Low Value 設定錯 | 檢查 Surface 格式 (GetSurfaceDesc),BGR vs RGB |
| Blt 失敗 | Surface 損失、裝置遺失 | 實作 RestoreSurfaces() 重建 |
| x64 專案無法參考 DxVBLib | DirectX 7 VB 型別庫僅 32-bit | 專案設定 Prefer 32-bit 或 x86 平台 |
DirectDraw 雖屬歷史技術,但其 Surface、Blitter、Clipper、ColorKey、協作層級等核心概念貫穿現代 GPU 繪圖管線。透過 DxVBLib 在 C# 體驗這些基礎,有助於理解 Direct2D/Direct3D 更高層抽象背後的運作原理。
- 上一篇文章: C# 透過 WMI 遠端重啟電腦完整實作指南
- 下一篇文章: C# 處理滑鼠與鍵盤事件完整指南
Copyright ©
程式碼之家 All Rights Reserved