首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

DelphiX 實現流暢旋轉動畫:TDXTimer 驅動的幀率無關旋轉技術

     編輯:Delphi

核心概念:TDXTimer 與幀率無關動畫

傳統 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.

關鍵技術點深度解析

1. TDXTimer.Interval := 0 的特殊含義


// TDXTimer 內部邏輯簡化
if Interval = 0 then
  // 使用 QueryPerformanceCounter 盡可能高頻觸發
  // 實際間隔取決於垂直同步 (VBlank) 與繪圖耗時
else
  // 固定間隔模式
  // 但仍比 TTimer 精確得多
end;

設為 0 時,計時器會在每次垂直同步後立即觸發下一幀,實現「垂直同步鎖定」的最高幀率(通常 60/75/144 FPS)。

2. LagCount 參數:幀率補償的關鍵

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)

3. 角度表示:0-255 而非 0-360

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;

4. DrawRotate 參數完整對照表

參數名稱說明範例值
Dest目標表面通常為 DXDraw1.SurfaceDXDraw1.Surface
X, Y繪製中心座標目標表面上的旋轉中心點Width div 2, Height div 2
Width, Height繪製尺寸可縮放繪製,負值可鏡像翻轉PicItem.Width, PicItem.Height
Pattern圖樣索引動畫幀編號0 (單圖) 或 FrameIndex
CenterX, CenterY旋轉樞紐比例相對圖片寬高的 0.0-1.00.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;

效能優化建議

  1. 啟用硬體加速:確保 DXDraw1.Options 包含 doHardware 和 doDirectX7Mode
  2. 減少 Surface 鎖定:批次繪製時避免頻繁 Canvas.Lock/Unlock
  3. 使用系統記憶體表面:PicItem.SystemMemory := True 可加快 CPU 端操作(如像素處理)
  4. 避免每幀創建物件:預先分配記錄/陣列,循環複用
  5. 監控 FrameRate: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 旋轉動畫系統。

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