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

DelphiX 首個簡單動畫完整教程:DXTimer 驅動多精靈彈跳動畫與動態增減

     編輯:Delphi

核心架構:遊戲主迴圈三要素

DelphiX 動畫系統基於三大核心組件:

  1. TDXDraw:雙緩衝繪圖表面 (Surface/Primary),提供硬體加速翻轉
  2. TDXImageList:圖片資源管理容器,支援多種格式、透明色、調色板
  3. TDXTimer:高精度遊戲計時器 (Interval=0 表示最高頻率),提供 LagCount 幀率補償

完整範例:多精靈彈跳 + 動態增減

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DXDraws, StdCtrls, DXClass;

type
  TForm1 = class(TForm)
    DXDraw1: TDXDraw;
    DXImageList1: TDXImageList;
    DXTimer1: TDXTimer;
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure DXTimer1Timer(Sender: TObject; LagCount: Integer);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

// 精靈記錄:位置 + 速度
type
  TSpriteRec = record
    X, Y: Integer;      // 當前位置
    VelX, VelY: Integer; // 速度向量 (像素/幀)
  end;

var
  Sprites: array of TSpriteRec;  // 動態陣列
  PicItem: TPictureCollectionItem;

procedure TForm1.FormCreate(Sender: TObject);
const
  ImgPath1 = 'C:\Temp\DelphiX.bmp';
begin
  // 1. 設定 DXDraw 填滿客戶區
  DXDraw1.Align := alClient;
  
  // 2. 設定 DXTimer 最高頻率
  DXTimer1.Interval := 0;  // 0 = 盡可能快 (垂直同步限制)
  DXTimer1.Enabled := True;
  
  // 3. 載入圖片資源
  DXImageList1.DXDraw := DXDraw1;
  DXImageList1.Items.Add;
  PicItem := DXImageList1.Items[DXImageList1.Items.Count - 1];
  PicItem.Picture.LoadFromFile(ImgPath1);
  
  // 4. 初始化 1 個精靈
  SetLength(Sprites, 1);
  Sprites[0].X := Random(DXDraw1.Width - PicItem.Width);
  Sprites[0].Y := Random(DXDraw1.Height - PicItem.Height);
  Sprites[0].VelX := 1;
  Sprites[0].VelY := 1;
  
  // 5. 按鈕設定
  Button1.Caption := '- 減少精靈';
  Button2.Caption := '+ 增加精靈';
  
  Randomize;
end;

// 減少精靈
procedure TForm1.Button1Click(Sender: TObject);
begin
  if Length(Sprites) > 1 then
    SetLength(Sprites, Length(Sprites) - 1);
end;

// 增加精靈
procedure TForm1.Button2Click(Sender: TObject);
var
  NewIdx: Integer;
begin
  NewIdx := Length(Sprites);
  SetLength(Sprites, NewIdx + 1);
  Sprites[NewIdx].X := Random(DXDraw1.Width - PicItem.Width);
  Sprites[NewIdx].Y := Random(DXDraw1.Height - PicItem.Height);
  Sprites[NewIdx].VelX := Random(3) + 1;  // 1-3
  Sprites[NewIdx].VelY := Random(3) + 1;
  // 隨機方向
  if Random(2) = 0 then Sprites[NewIdx].VelX := -Sprites[NewIdx].VelX;
  if Random(2) = 0 then Sprites[NewIdx].VelY := -Sprites[NewIdx].VelY;
end;

// 遊戲主迴圈:每幀執行一次
procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
var
  i: Integer;
  S: ^TSpriteRec;
  LagFactor: Integer;
begin
  // 1. 清除後台緩衝區 (黑色背景)
  DXDraw1.Surface.Fill(0);
  
  // 2. 幀率補償因子:正常幀 LagCount=0 → Factor=1;掉1幀 LagCount=1 → Factor=2
  LagFactor := LagCount + 1;
  
  // 3. 遍歷所有精靈:更新位置 + 邊界檢測 + 繪製
  for i := 0 to High(Sprites) do
  begin
    S := @Sprites[i];
    
    // 邊界檢測與反彈 (乘以 LagFactor 保持速度一致)
    if S.X > DXDraw1.Width - PicItem.Width then
    begin
      S.X := DXDraw1.Width - PicItem.Width;
      S.VelX := -Abs(S.VelX);
    end
    else if S.X < 0 then
    begin
      S.X := 0;
      S.VelX := Abs(S.VelX);
    end;
    
    if S.Y > DXDraw1.Height - PicItem.Height then
    begin
      S.Y := DXDraw1.Height - PicItem.Height;
      S.VelY := -Abs(S.VelY);
    end
    else if S.Y < 0 then
    begin
      S.Y := 0;
      S.VelY := Abs(S.VelY);
    end;
    
    // 更新位置 (乘以 LagFactor)
    Inc(S.X, S.VelX * LagFactor);
    Inc(S.Y, S.VelY * LagFactor);
    
    // 繪製精靈
    PicItem.Draw(DXDraw1.Surface, S.X, S.Y, 0);
  end;
  
  // 4. 可選:顯示幀率與精靈數量
  with DXDraw1.Surface.Canvas do
  begin
    Brush.Style := bsClear;
    Font.Color := clWhite;
    Font.Size := 14;
    TextOut(10, 10, Format('FPS: %.1f | Sprites: %d | Lag: %d', 
      [DXTimer1.FrameRate, Length(Sprites), LagCount]));
    Release;
  end;
  
  // 5. 翻轉前後緩衝區顯示
  DXDraw1.Flip;
end;

end.

DFM 視窗定義

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'DelphiX 簡單動畫 - 多精靈彈跳'
  ClientHeight = 400
  ClientWidth = 600
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object DXDraw1: TDXDraw
    Left = 0
    Top = 0
    Width = 600
    Width = 400
    Align = alClient
    AutoInitialize = True
    AutoSize = True
    Color = clBlack
    Display.FixedBitCount = False
    Display.FixedRatio = True
    Display.FixedSize = True
    Options = [doAllowReboot, doWaitVBlank, doCenter, do3D, doDirectX7Mode, doHardware, doSelectDriver]
    SurfaceHeight = 400
    SurfaceWidth = 600
    TabOrder = 0
    Traces = <>
  end
  object Button1: TButton
    Left = 10
    Top = 360
    Width = 120
    Height = 30
    Anchors = [akLeft, akBottom]
    Caption = 'Button1'
    TabOrder = 1
    OnClick = Button1Click
  end
  object Button2: TButton
    Left = 140
    Top = 360
    Width = 120
    Height = 30
    Anchors = [akLeft, akBottom]
    Caption = 'Button2'
    TabOrder = 2
    OnClick = Button2Click
  end
  object DXImageList1: TDXImageList
    Left = 40
    Top = 40
  end
  object DXTimer1: TDXTimer
    ActiveOnly = True
    Enabled = True
    Interval = 0
    OnTimer = DXTimer1Timer
    Left = 8
    Top = 152
  end
end

關鍵技術點深度解析

1. TDXTimer.Interval := 0 的含義

  • 不使用固定間隔,而是緊跟垂直同步 (VBlank)
  • 每次垂直同步後立即觸發下一幀
  • 實際幀率 = 顯示器刷新率 (通常 60/75/144 Hz)
  • 配合 doWaitVBlank 選項防止畫面撕裂

2. LagCount 幀率補償機制

// LagCount 含義:
// 0 = 正常,本幀按時觸發
// 1 = 掉了 1 幀,本幀需處理 2 幀邏輯
// 2 = 掉了 2 幀,本幀需處理 3 幀邏輯
// ...
// 公式:實際位移 = 基礎速度 * (LagCount + 1)

// 範例:
// 基礎速度 = 2 像素/幀
// 正常情況:每幀移動 2 像素
// 掉 1 幀 (LagCount=1):本幀移動 2 * 2 = 4 像素
// 結果:視覺上速度保持不變,只是畫面跳幀

3. 雙緩衝繪製流程

  1. DXDraw1.Surface.Fill(0) - 清除後台表面 (背景色)
  2. 所有繪製操作針對 DXDraw1.Surface
  3. DXDraw1.Flip - 交換 Surface 與 Primary 指標 (極快,僅指標交換)
  4. Primary 顯示在螢幕上,Surface 成為新的後台繪圖目標

4. 動態陣列管理精靈

  • SetLength(Sprites, N) 調整陣列大小
  • High(Sprites) 獲取最大索引
  • 使用指標 ^TSpriteRec 避免記錄複製開銷
  • 按鈕動態增減展示運行時資源管理

進階擴展:精靈類封裝


type
  TSprite = class
  private
    FX, FY: Single;      // 用浮點數存儲位置,支援亞像素精度
    FVelX, FVelY: Single;
    FImage: TPictureCollectionItem;
    FWidth, FHeight: Integer;
    FActive: Boolean;
  public
    constructor Create(AImage: TPictureCollectionItem; X, Y: Single);
    procedure Update(DeltaTime: Single; ScreenW, ScreenH: Integer);
    procedure Draw(Dest: TDirectDrawSurface);
    property Active: Boolean read FActive write FActive;
    property X: Single read FX write FX;
    property Y: Single read FY write FY;
  end;

constructor TSprite.Create(AImage: TPictureCollectionItem; X, Y: Single);
begin
  inherited Create;
  FImage := AImage;
  FX := X; FY := Y;
  FVelX := RandomRange(-3, 3);
  FVelY := RandomRange(-3, 3);
  if FVelX = 0 then FVelX := 1;
  if FVelY = 0 then FVelY := 1;
  FWidth := AImage.Width;
  FHeight := AImage.Height;
  FActive := True;
end;

procedure TSprite.Update(DeltaTime: Single; ScreenW, ScreenH: Integer);
begin
  if not FActive then Exit;
  
  FX := FX + FVelX * DeltaTime * 60; // DeltaTime 以秒為單位,60 為基準幀率
  FY := FY + FVelY * DeltaTime * 60;
  
  // 邊界反彈
  if FX > ScreenW - FWidth then
  begin
    FX := ScreenW - FWidth;
    FVelX := -Abs(FVelX);
  end
  else if FX < 0 then
  begin
    FX := 0;
    FVelX := Abs(FVelX);
  end;
  
  if FY > ScreenH - FHeight then
  begin
    FY := ScreenH - FHeight;
    FVelY := -Abs(FVelY);
  end
  else if FY < 0 then
  begin
    FY := 0;
    FVelY := Abs(FVelY);
  end;
end;

procedure TSprite.Draw(Dest: TDirectDrawSurface);
begin
  if not FActive then Exit;
  FImage.Draw(Dest, Round(FX), Round(FY), 0);
end;

主迴圈升級版 (使用 DeltaTime)


var
  LastTime: Int64 = 0;
  Freq: Int64 = 0;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // ... 原有初始化 ...
  QueryPerformanceFrequency(Freq);
  QueryPerformanceCounter(LastTime);
end;

procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
var
  CurrentTime: Int64;
  DeltaTime: Single;
  i: Integer;
begin
  // 計算精確時間差
  QueryPerformanceCounter(CurrentTime);
  DeltaTime := (CurrentTime - LastTime) / Freq; // 秒
  LastTime := CurrentTime;
  
  // 限制最大 DeltaTime (防止拖動視窗導致巨大跳躍)
  if DeltaTime > 0.1 then DeltaTime := 0.1;
  
  DXDraw1.Surface.Fill(0);
  
  for i := 0 to High(Sprites) do
  begin
    Sprites[i].Update(DeltaTime, DXDraw1.Width, DXDraw1.Height);
    Sprites[i].Draw(DXDraw1.Surface);
  end;
  
  // 顯示資訊
  with DXDraw1.Surface.Canvas do
  begin
    Brush.Style := bsClear;
    Font.Color := clWhite;
    Font.Size := 14;
    TextOut(10, 10, Format('FPS: %.1f | DT: %.3fms | Sprites: %d', 
      [DXTimer1.FrameRate, DeltaTime * 1000, Length(Sprites)]));
    Release;
  end;
  
  DXDraw1.Flip;
end;

效能優化清單

  • 使用指標遍歷
  • 避免每幀創建物件/字串
  • 批次繪製相同材質精靈
  • 靜態背景預渲染到離屏表面
  • 優化項目說明效果
    doHardware + doDirectX7Mode啟用顯卡硬體加速繪製速度提升 10-100x
    doWaitVBlank垂直同步防撕裂畫面流暢無撕裂
    Interval := 0最高頻率觸發最大化幀率
    預分配陣列容量SetLength 避免頻繁重新分配減少 GC 壓力

    常見問題排查

    問題原因解決
    動畫卡頓/閃爍未啟用雙緩衝或 VSync確認 Flip 機制;啟用 doWaitVBlank
    精靈穿牆/卡邊界位置更新順序錯誤先檢測邊界反彈,再更新位置
    速度隨電腦效能變化未使用 LagCount/DeltaTime 補償套用 LagFactor 或 DeltaTime
    記憶體持續增長動態陣列未釋放/物件洩漏FormDestroy 釋放;使用 FastMM4 檢測
    圖片載入失敗路徑錯誤/格式不支援使用絕對路徑;確認 BMP/JPG/DXG 格式

    總結

    這個「首個簡單動畫」範例包含了 DelphiX 遊戲開發的所有核心要素:TDXDraw 雙緩衝、TDXImageList 資源管理、TDXTimer 幀率補償主迴圈、精靈位置/速度更新、邊界碰撞反彈、動態實體管理。理解 LagCount 補償機制、Flip 雙緩衝原理、Interval:=0 高頻模式,是構建流暢 2D 遊戲/動畫的基石。建議以此為基礎,逐步擴展為精靈類體系、場景管理、碰撞檢測、粒子系統等完整遊戲引擎架構。

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