FireMonkey 框架的跨平台特性和豐富的 UI 元件,讓開發者可以輕鬆地建立具有高度互動性的應用程式。本文以記憶遊戲的開發為例,探討瞭如何運用 FireMonkey 實作多點觸控和手勢辨識功能。文章首先介紹了手勢事件的處理流程,並以旋轉手勢為例,展示瞭如何根據手勢資訊更新 UI 元素的狀態。接著,文章講解了如何利用 OnTouch 事件處理多點觸控,並以在畫布上繪製連線為例,說明瞭多點觸控的應用方式。在遊戲開發的部分,文章詳細說明瞭如何使用 TImageList 元件有效管理圖片資源,以及如何設計可適應不同螢幕尺寸的遊戲介面。最後,文章還介紹了遊戲計時器的實作方法,以及如何格式化遊戲時間並顯示在 UI 上。
使用FireMonkey開發多點觸控與手勢辨識應用
FireMonkey是一種跨平台的UI框架,讓開發者能夠輕鬆建立具有豐富互動性的應用程式。在本章中,我們將探討如何利用FireMonkey實作手勢辨識與多點觸控功能,並以「Game of Memory」遊戲為例,展示相關技術的實際應用。
手勢辨識
在FireMonkey中,手勢辨識是透過TGestureEvent事件處理的。下面是一個典型的TGestureEvent事件處理程式宣告:
TGestureEvent = procedure(Sender: TObject;
const EventInfo: TGestureEventInfo;
var Handled: Boolean) of object;
內容解密:
Sender: 觸發事件的物件。EventInfo: 包含手勢相關資訊的結構,例如手勢ID、位置和其他屬性。Handled: 布林變數,用於指示事件是否已被處理。
我們可以透過檢查EventInfo.GestureID來確定特定的手勢型別。例如,對於旋轉手勢(igiRotate),我們可以調整物件的旋轉角度。以下是一個範例實作:
procedure TFormSunGestures.FormGesture(Sender: TObject;
const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
if EventInfo.GestureID = igiRotate then
begin
if (TInteractiveGestureFlag.gfBegin in EventInfo.Flags) then
FLastAngle := CircleSun.RotationAngle
else if EventInfo.Angle <> 0 then
CircleSun.RotationAngle := FLastAngle - (EventInfo.Angle * 180) / Pi;
end;
end;
內容解密:
- 檢查手勢ID是否為
igiRotate,確認是否為旋轉手勢。 - 如果是旋轉手勢開始(
gfBegin),儲存當前的旋轉角度到FLastAngle。 - 如果旋轉角度不為零,根據事件的角度資訊更新
CircleSun的旋轉角度。
多點觸控
多點觸控允許使用者同時使用多個手指與應用程式互動。在FireMonkey中,我們可以透過OnTouch事件處理多點觸控。以下是一個範例:
procedure TFormTouch.FormTouch(Sender: TObject;
const Touches: TTouches; const Action: TTouchAction);
var
Count: Integer;
begin
FBitmap.Canvas.BeginScene;
try
FBitmap.Clear(TAlphaColors.White);
Count := Length(Touches);
if Count > 1 then
for var I := 0 to Count-2 do
FBitmap.Canvas.DrawLine(
Touches[i].Location,
Touches[i+1].Location,
TAlphaColorRec.Red);
finally
FBitmap.Canvas.EndScene;
end;
PaintBox1.Repaint;
end;
內容解密:
- 取得觸控點的數量(
Count)。 - 如果有多於一個觸控點,遍歷這些點並使用
DrawLine方法在相鄰點之間繪製紅線。 - 清除畫布並重新繪製,以反映最新的觸控狀態。
Game of Memory遊戲開發
「Game of Memory」是一款經典的記憶配對遊戲。玩家需要翻開相同的圖片來消除它們,最終目標是在最短的時間內消除所有圖片。
遊戲設計
- 遊戲介面:使用FireMonkey的2D UI功能建立遊戲板和控制按鈕。
- 圖片管理:利用
TImageList元件管理遊戲中的圖片資源。 - 遊戲邏輯:實作圖片的隨機分配、翻開與消除邏輯。
使用TImageList管理圖片
在遊戲中,我們使用TImageList來集中管理圖片資源。這不僅便於存取,也有利於跨多個表單或專案重複使用這些資源。
// 在資料模組中新增TImageList並載入圖片
內容解密:
- 將所有遊戲相關的圖片資源新增到
TImageList中。 - 在需要時,從
TImageList中檢索圖片並在遊戲中使用。
使用影像與設計使用者介面
在開發「Game of Memory」應用程式時,我們需要有效地管理和使用影像資源。本章節將探討如何使用 TImageList 元件來儲存和管理影像,以及如何設計一個能夠適應不同裝置和螢幕大小的使用者介面。
使用 TImageList 管理影像
TImageList 元件是一個非常有用的工具,用於儲存和管理應用程式中使用的影像。它包含兩個主要的影像集合:Source 和 Destination。Source 集合儲存了所有新增到影像列表元件的影像,這些影像可以是不同的格式,如 BMP、PNG、JPEG、GIF 和 TIFF 等。Destination 集合則包含了根據 Source 集合中的影像建立的影像,這些影像將在應用程式中使用。
設定 TImageList
- 將
TImageList元件拖曳到資料模組上,並將其Name屬性更改為ImageListMain。 - 雙擊
ImageListMain元件以開啟影像列表編輯器,在此您可以管理 Source 和 Destination 集合中的影像。 - 將所需的影像新增到 Source 集合中,然後根據需要將它們新增到 Destination 集合中。
在我們的範例中,我們新增了 18 個隨機選擇的國旗影像,它們的大小約為 80x80 畫素。前兩個影像具有特殊用途:第一個影像是完全白色的,用於在瓷磚被移除時顯示;第二個影像包含瓷磚的背面,用於顯示隱藏的影像。
設計多裝置使用者介面
在設計「Game of Memory」的主表單時,我們需要考慮到應用程式將在不同大小和方向的裝置上執行。因此,我們需要使用 FireMonkey 的佈局和對齊功能來建立一個能夠適應不同螢幕大小的使用者介面。
對齊、錨定和邊距
FireMonkey 提供了多種對齊選項,例如 Client、Top、Bottom、Left 和 Right,用於控制元件在其父容器中的位置。TAnchors 屬性允許您將元件的某一側錨定到其父容器,從而在父容器大小改變時控制元件的大小和位置。TMargins 屬性則用於指定元件與其父容器之間的距離。
佈局元件
FireMonkey 提供了多種佈局元件,如 TLayout 和 TGridLayout,用於組織和管理使用者介面中的控制項。在我們的遊戲中,我們使用 TGridLayout 元件來建立一個瓷磚網格。將 TGridLayout 元件拖曳到主表單上,將其 Name 屬性更改為 GridLayoutTiles,並將其對齊到 Client 以佔據整個螢幕。
控制網格佈局
使用 ItemHeight 和 ItemWidth 屬性可以控制網格佈局中每個專案的大小。
程式碼範例:建立 TImageList
// 建立 TImageList 元件
var
ImageList: TImageList;
begin
ImageList := TImageList.Create(Self);
ImageList.Name := 'ImageListMain';
// 新增影像到 Source 集合
ImageList.AddImage(LoadBitmapFromFile('image1.png'));
ImageList.AddImage(LoadBitmapFromFile('image2.png'));
// ...
end;
內容解密:
此範例展示瞭如何在程式碼中建立一個 TImageList 元件,並將其命名為 ImageListMain。接著,我們可以使用 AddImage 方法將影像新增到 Source 集合中。這些影像可以是從檔案載入的點陣圖。
圖表示例:FireMonkey 佈局元件
圖表翻譯:
此圖展示了 FireMonkey 中的佈局元件層次結構。TLayout 是最通用的佈局元件,而 TGridLayout 是用於建立網格佈局的特定元件。網格佈局中可以包含多個專案,如 Item1、Item2 等。
圖表內容解密:
此圖表說明瞭 FireMonkey 中不同佈局元件之間的關係。TLayout 是基礎佈局元件,而 TGridLayout 等特定佈局元件則繼承自 TLayout。這些佈局元件用於組織和管理使用者介面中的控制項。
透過使用 TImageList 和 FireMonkey 的佈局功能,我們可以建立一個強大且適應性強的使用者介面,以滿足不同裝置和螢幕大小的需求。
使用FireMonkey開發記憶遊戲介面
在這個章節中,我們將使用TGlyph元件來顯示來自資料模組中影像列表的點陣圖。雖然我們也可以使用TImage元件,但那樣就需要為每個TImage直接載入點陣圖,這樣不如使用一套點陣圖來得有效率。讓我們開始吧:
設定主表單的背景顏色與新增TGlyph控制項
- 將主表單的背景顏色改為白色。展開Fill屬性,然後將Color改為白色,並將Kind設為Solid。
- 在表單的使用單元(uses clause)中加入uDMGameOfMem單元,並將TGlyph控制項拖曳到網格佈局(grid layout)上。將其Images屬性指向DMGameOfMem.ImageListMain,並將ImageIndex設為1。這樣,我們應該能在Glyph控制項上看到影像列表中Destination集合的第二張圖片,這代表一個隱藏的瓷磚。將Glyph控制項的四個邊距(margins)都設為2,以便在網格中的不同瓷磚之間保持一定的距離。
新增工具列按鈕與其他控制項
- 將一個TSpeedButton元件拖曳到工具列上,將其Name屬性設為SpdbtnPlay,對齊到左邊,並將StyleLookup屬性設為playtoolbutton。水平調整按鈕大小,使其變小。這樣會固定按鈕的寬度,使其與高度相同。接著,將一個TLabel元件拖曳到工具列上,將其Name設為LblScore,Text設為「Game of Memory」,然後對齊到客戶端(Client)。
- 將一個TComboBox元件拖曳到工具列上,對齊到右邊。在這裡,我們將控制網格的大小。將該組合框的Name屬性設為CmbbxLevel。在其Items屬性中,輸入八個難度級別,從4對開始,一直到18對,如圖5.11所示。為了避免列表過長,難度級別以兩對為單位遞增。
TComboBox的Items屬性設定範例程式碼
// 在CmbbxLevel的Items屬性中新增難度級別
CmbbxLevel.Items.Add('4 對');
CmbbxLevel.Items.Add('6 對');
// ... 繼續新增直到18對
設定遊戲的計時器與相關功能
- 將一個TTimer元件拖曳到表單上,將其名稱改為TimerGame,並將Interval設為50。當計時器啟用時,遊戲正在進行;當計時器未啟用時,遊戲停止。在Score標籤上,我們將顯示遊戲的時間。為此,我們需要一個函式來格式化經過的時間。
格式化遊戲時間的函式
function GameTimeToStr(Value: TTime): string;
var
H, Min, Sec, Msec: Word;
S: string;
begin
DecodeTime(Value, H, Min, Sec, Msec);
S := '時間:';
if H > 0 then
S := S + H.ToString + '小時 ';
if Min > 0 then
S := S + Min.ToString + '分鐘 ';
S := S + Sec.ToString + '.' + Pad3Zeros(Msec.ToString) + '秒';
Result := S;
end;
function Pad3Zeros(Value: string): string; inline;
var
I: integer;
begin
I := Length(Value);
if I = 3 then
Result := Value
else if I = 2 then
Result := '0' + Value
else if I = 1 then
Result := '00' + Value
else
Result := '000';
end;
圖表翻譯:遊戲時間格式化流程圖
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title FireMonkey開發多點觸控手勢記憶遊戲
package "Docker 架構" {
actor "開發者" as dev
package "Docker Engine" {
component [Docker Daemon] as daemon
component [Docker CLI] as cli
component [REST API] as api
}
package "容器運行時" {
component [containerd] as containerd
component [runc] as runc
}
package "儲存" {
database [Images] as images
database [Volumes] as volumes
database [Networks] as networks
}
cloud "Registry" as registry
}
dev --> cli : 命令操作
cli --> api : API 呼叫
api --> daemon : 處理請求
daemon --> containerd : 容器管理
containerd --> runc : 執行容器
daemon --> images : 映像檔管理
daemon --> registry : 拉取/推送
daemon --> volumes : 資料持久化
daemon --> networks : 網路配置
@enduml
圖表翻譯: 此圖示呈現了遊戲時間格式化的流程。首先檢查時間中的小時數,如果大於0,則新增到結果中。接著檢查分鐘數,同樣如果大於0則新增到結果。最後,無論前面的檢查結果如何,都會新增秒數和毫秒數到結果中,並傳回最終的格式化結果。
新增私有欄位與實作遊戲邏輯
- 在表單類別中新增一個名為FGameStart的私有欄位,用於儲存遊戲開始的時間。在OnTimer事件中加入以下程式碼:
procedure TFormMain.TimerGameTimer(Sender: TObject);
begin
var Delta := Now - FGameStart;
var S := GameTimeToStr(Delta);
LblScore.Text := S;
end;
GameStart方法實作範例
procedure TFormMain.GameStart;
const
SHUFFLE_TIMES = 10;
var
Indices: array of integer;
begin
// 移除網格中的所有glyph控制項(如果有的話)
GrdltTiles.DeleteChildren;
var PairsCount := CurrPairsCount;
var TilesCount := PairsCount * 2;
FVisibleGlyph := nil;
FPairsLeft := PairsCount;
// 初始化索引列表,隨機化並加入偏移量2
SetLength(Indices, TilesCount);
for var I := 0 to PairsCount-1 do
begin
Indices[I] := I;
Indices[I + PairsCount] := I;
end;
// ... 繼續實作隨機化與其他邏輯
#### 內容解密:
此段程式碼初始化了GameStart方法,首先清除了網格佈局中的所有子控制項,然後根據當前的配對數量設定了瓷磚的總數。接著,初始化了一個索引列表,用於儲存瓷磚對應的影像索引,並將這個列表隨機化,以實作瓷磚的隨機排列。
重點回顧
本章節介紹瞭如何使用FireMonkey建立記憶遊戲的主介面,包括設定背景顏色、新增TGlyph控制項、工具列按鈕和其他控制項。此外,還實作了遊戲的計時器功能和相關邏輯,為遊戲的核心功能打下了基礎。透過本章節的學習,讀者應該能夠掌握使用FireMonkey進行介面設計和基本邏輯實作的能力。