傳統 TTimer 精度有限(約 10-15ms),且受訊息佇列擁塞影響大。DelphiX 的 TDXTimer 基於高效能計時器(QueryPerformanceCounter),支援微秒級精度與 Interval = 0 表示「盡可能快」的模式,非常適合遊戲主迴圈。結合 LagCount 參數可實現幀率補償,確保動畫在不同效能硬體上速度一致。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DXDraws, DXClass, ComCtrls;
type
TForm1 = class(TForm)
DXDraw1: TDXDraw;
DXImageList1: TDXImageList;
DXTimer1: TDXTimer;
procedure FormCreate(Sender: TObject);
procedure DXTimer1Timer(Sender: TObject; LagCount: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
var
PicItem: TPictureCollectionItem;
Angle: Cardinal; // 0-255 對應 0-360°
procedure TForm1.FormCreate(Sender: TObject);
const
ImgPath1 = 'C:\Temp\DelphiX.bmp';
begin
// 讓 DXDraw 填滿客戶區
DXDraw1.Align := alClient;
// 關鍵:Interval := 0 表示最高頻率觸發
DXTimer1.Interval := 0;
DXTimer1.Enabled := True;
// 綁定繪圖目標
DXImageList1.DXDraw := DXDraw1;
// 載入圖片
PicItem := TPictureCollectionItem(DXImageList1.Items.Add);
PicItem.Picture.LoadFromFile(ImgPath1);
Angle := 0;
end;
procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
begin
// 1. 清除背景 (黑色)
DXDraw1.Surface.Fill(0);
// 2. 繪製旋轉圖片
// 參數:目標表面、中心X、中心Y、寬、高、圖樣索引、中心點比例X、中心點比例Y、角度(0-255)
PicItem.DrawRotate(DXDraw1.Surface,
DXDraw1.Width div 2, DXDraw1.Height div 2, // 畫面中心
PicItem.Width, PicItem.Height, // 原始大小
0, // 圖樣索引
0.5, 0.5, // 旋轉中心 = 圖片中心
Byte(Angle)); // 當前角度
// 3. 角度遞增 (每幀 +1,約 1.4°/幀)
Inc(Angle);
// Angle 自動溢位回 0 (Cardinal 0-255 對應 Byte)
// 4. 翻轉前後緩衝區顯示
DXDraw1.Flip;
// 5. 可選:在標題顯示幀率
// Caption := Format('FPS: %.1f | Angle: %d', [DXTimer1.FrameRate, Angle]);
end;
end.
// TDXTimer 內部邏輯簡化
if Interval = 0 then
// 使用 QueryPerformanceCounter 盡可能高頻觸發
// 實際間隔取決於垂直同步 (VBlank) 與繪圖耗時
else
// 固定間隔模式
// 但仍比 TTimer 精確得多
end;
設為 0 時,計時器會在每次垂直同步後立即觸發下一幀,實現「垂直同步鎖定」的最高幀率(通常 60/75/144 FPS)。
procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
var
Speed: Single;
begin
// LagCount = 錯過的幀數 (0 = 正常,1 = 掉1幀,以此類推)
// 正常情況下 LagCount = 0
// 基礎速度:每秒旋轉 90 度 = 64 單位/秒 (256/4)
// 假設目標 60 FPS,每幀應增加 64/60 ≈ 1.066
Speed := 64 / 60; // 單位/幀
// 幀率補償:若掉幀,按比例增加角度增量
Angle := Angle + Round(Speed * (LagCount + 1));
// ... 繪製代碼 ...
end;
實用公式:實際增量 = 基礎增量 × (LagCount + 1)
DelphiX 使用 8 位元組表示角度:
0 = 0°64 = 90°128 = 180°192 = 270°255 ≈ 358.6°轉換函數:
function DegToDXAngle(Degrees: Single): Byte;
begin
Result := Round(Degrees * 256 / 360) mod 256;
end;
function DXAngleToDeg(Angle: Byte): Single;
begin
Result := Angle * 360 / 256;
end;
| 參數 | 名稱 | 說明 | 範例值 |
|---|---|---|---|
| Dest | 目標表面 | 通常為 DXDraw1.Surface | DXDraw1.Surface |
| X, Y | 繪製中心座標 | 目標表面上的旋轉中心點 | Width div 2, Height div 2 |
| Width, Height | 繪製尺寸 | 可縮放繪製,負值可鏡像翻轉 | PicItem.Width, PicItem.Height |
| Pattern | 圖樣索引 | 動畫幀編號 | 0 (單圖) 或 FrameIndex |
| CenterX, CenterY | 旋轉樞紐比例 | 相對圖片寬高的 0.0-1.0 | 0.5, 0.5 (中心旋轉) |
| Angle | 旋轉角度 | 0-255 對應 0-360° | Byte(Angle) |
type
TRotatingSprite = record
PicItem: TPictureCollectionItem;
X, Y: Integer; // 位置
Angle: Cardinal; // 當前角度
Speed: Single; // 角度/秒 (DX單位/秒)
Scale: Single; // 縮放比例
end;
var
Sprites: array of TRotatingSprite;
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
Item: TPictureCollectionItem;
begin
// ... 初始化 DXDraw, DXTimer ...
SetLength(Sprites, 5);
for i := 0 to High(Sprites) do
begin
Item := TPictureCollectionItem(DXImageList1.Items.Add);
Item.Picture.LoadFromFile(Format('C:\Temp\Sprite%d.bmp', [i]));
Sprites[i].PicItem := Item;
Sprites[i].X := Random(DXDraw1.Width);
Sprites[i].Y := Random(DXDraw1.Height);
Sprites[i].Angle := Random(256);
Sprites[i].Speed := 32 + Random(64); // 45°-135°/秒
Sprites[i].Scale := 0.5 + Random * 1.5; // 0.5x - 2.0x
end;
end;
procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
var
i: Integer;
Delta: Single;
S: ^TRotatingSprite;
begin
DXDraw1.Surface.Fill(0);
// 計算時間增量 (LagCount+1 幀)
Delta := (LagCount + 1) / 60.0; // 假設 60 FPS 基準
for i := 0 to High(Sprites) do
begin
S := @Sprites[i];
// 更新角度
S.Angle := (S.Angle + Round(S.Speed * Delta)) mod 256;
// 繪製
S.PicItem.DrawRotate(DXDraw1.Surface,
S.X, S.Y,
Round(S.PicItem.Width * S.Scale),
Round(S.PicItem.Height * S.Scale),
0, 0.5, 0.5, Byte(S.Angle));
end;
DXDraw1.Flip;
end;
DXDraw1.Options 包含 doHardware 和 doDirectX7ModeCanvas.Lock/UnlockPicItem.SystemMemory := True 可加快 CPU 端操作(如像素處理)DXTimer1.FrameRate 屬性可即時獲取實際幀率| 現象 | 原因 | 對策 |
|---|---|---|
| 動畫卡頓、不流暢 | 垂直同步未啟用或繪圖耗時過長 | 啟用 doWaitVBlank;簡化繪圖邏輯;檢查 doHardware |
| 旋轉速度隨電腦效能變化 | 未使用 LagCount 補償 | 採用 Speed * (LagCount + 1) 公式 |
| 圖片旋轉時抖動/邊緣鋸齒 | 雙線性過濾未啟用或中心點非整數 | 確保 CenterX/Y=0.5;檢查 DirectX 設備過濾模式 |
| Angle 溢位導致反向旋轉 | 使用有符號整數儲存角度 | 統一用 Cardinal 或 Byte,配合 mod 256 |
TDXTimer 配合 DrawRotate 是 DelphiX 實現高效能旋轉動畫的核心組合。關鍵在於:Interval:=0 最大化幀率、LagCount 實現幀率補償、Byte 角度運算避免溢位。掌握這三點,即可構建流暢、跨平台一致的 2D 旋轉動畫系統。