返回文章列表

FireMonkey開發記憶遊戲與3D開發應用

本文介紹使用 FireMonkey 框架開發記憶遊戲,包含遊戲初始化、介面建立、點選處理、圖片大小調整以及設定和成績儲存。同時也探討 FireMonkey 的 3D 功能,包括跨平台 3D 渲染、TContext3D 的使用以及如何結合 2D 和 3D 元件進行開發,提供程式碼範例和詳細說明。

遊戲開發 跨平台開發

FireMonkey 框架為跨平台應用程式開發提供了強大的支援,本文將示範如何使用 FireMonkey 開發記憶遊戲,並探討其 3D 渲染能力。遊戲開發部分涵蓋了圖片隨機排列、點選事件處理、匹配邏輯判斷以及遊戲設定和成績儲存等核心功能。我們將解析程式碼,闡述 FireMonkey 如何有效管理 UI 元件和遊戲邏輯。接著,文章將探討 FireMonkey 在 3D 開發方面的應用,包含跨平台 3D 渲染機制、TContext3D 類別的使用,以及如何直接操作 3D API 或使用預建元件進行 3D 繪圖,並提供程式碼範例說明如何繪製立方體和觸發渲染事件。

遊戲開發實戰:使用FireMonkey開發記憶遊戲

在本文中,我們將探討如何使用FireMonkey框架開發一款記憶遊戲。該遊戲涉及隨機排列的圖片配對,玩家需要在最短的時間內找出所有匹配的圖片對。

遊戲初始化與設定

首先,我們來看看遊戲的初始化過程。在GameStart方法中,系統會隨機打亂圖片索引列表,以確保每次遊戲開始時,圖片的排列順序都是不同的。

// 隨機打亂索引列表
for var I := 0 to SHUFFLE_TIMES - 1 do
    for var J := 0 to TilesCount - 1 do
    begin
        var K := Random(TilesCount);
        var Temp := Indices[K];
        Indices[K] := Indices[J];
        Indices[J] := Temp;
    end;
// 為每個索引加2,因為0和1是特殊值
for var I := 0 to TilesCount-1 do
    Indices[I] := Indices[I] + 2;

內容解密:

這段程式碼首先透過雙重迴圈隨機交換Indices陣列中的元素,以達到打亂的目的。接著,將每個索引值加2,這是因為索引值0和1具有特殊含義(可能是用於表示未翻開或特殊狀態的牌),因此實際使用的索引從2開始。

建立遊戲介面

接下來,GameStart方法會根據打亂後的索引列表建立遊戲介面上的圖片元件。

for var I := 0 to TilesCount-1 do
begin
    var G := TGlyph.Create(GrdltTiles);
    g.Parent := GrdltTiles;
    g.Images := DMGameOfMem.ImageListMain;
    g.ImageIndex := 1; // 初始化為隱藏狀態
    g.Tag := indices[i]; // 儲存實際圖片索引
    g.HitTest := True;
    g.OnClick := OnGlyphClick;
    G.Margins.Top := TILE_MARGIN;
    g.Margins.Left := TILE_MARGIN;
    g.Margins.Bottom := TILE_MARGIN;
    g.Margins.Right := TILE_MARGIN;
end;

內容解密:

這段程式碼動態建立了TGlyph元件,並將其新增到遊戲介面中。每個元件初始顯示為隱藏狀態(ImageIndex := 1),並在Tag屬性中儲存了實際對應的圖片索引。點選事件繫結到OnGlyphClick方法,用於處理玩家的點選操作。

處理玩家點選

當玩家點選某張圖片時,系統會檢查是否為有效的點選,並根據情況進行處理。

procedure TFormMain.OnGlyphClick(Sender: TObject);
begin
    if Sender is TGlyph then
    begin
        var G := TGlyph(Sender);
        if G.ImageIndex > 0 then // 確保不是已經被移除的牌
        begin
            G.ImageIndex := G.Tag; // 顯示被點選的牌
            // 處理匹配邏輯
            if FVisibleGlyph <> nil then // 如果已經有一張牌被翻開
            begin
                if G.Tag = FVisibleGlyph.Tag then // 如果兩張牌匹配
                begin
                    G.ImageIndex := 0; // 移除兩張牌的顯示
                    FVisibleGlyph.ImageIndex := 0;
                    FVisibleGlyph := nil;
                    Dec(FPairsLeft); // 減少未匹配的牌數
                    if FPairsLeft = 0 then // 如果所有牌都已匹配
                        GameEnd; // 結束遊戲
                end
                else // 如果兩張牌不匹配
                begin
                    FVisibleGlyph.ImageIndex := 1; // 隱藏之前翻開的牌
                    FVisibleGlyph := G; // 更新當前翻開的牌
                end;
            end
            else // 如果是第一張被翻開的牌
                FVisibleGlyph := G; // 儲存當前翻開的牌
        end;
    end;
end;

內容解密:

這段程式碼處理了玩家點選圖片的操作。如果點選的是有效的圖片(未被移除),則顯示該圖片。如果是第二張被翻開的圖片,則檢查是否與第一張匹配。如果匹配,則移除兩張圖片的顯示;如果不匹配,則隱藏第一張圖片,並更新當前翻開的圖片為第二張。

調整圖片大小

為了適應不同裝置和螢幕方向,系統會動態調整圖片的大小。

procedure TFormMain.AdjustTileSize;
const
    ADJUST_FACTOR = 0.9;
begin
    var TileArea := GrdltTiles.Width * GrdltTiles.Height / CurrPairsCount / 2;
    var TileSize := (sqrt(TileArea) - 2 * TILE_MARGIN) * ADJUST_FACTOR;
    GrdltTiles.ItemHeight := TileSize;
    GrdltTiles.ItemWidth := TileSize;
end;

內容解密:

這段程式碼根據目前的遊戲難度(CurrPairsCount)和遊戲介面的大小,計算出每張圖片的最佳大小,並設定給GrdltTiles,以確保圖片能均勻分佈在螢幕上。

儲存遊戲設定和成績

最後,系統會將玩家的設定和最佳成績儲存到INI檔案中。

procedure TDMGameOfMem.SaveScore(GameTime: TTime; APairsCount: Integer);
begin
    FIniFile.WriteTime(StrSCORES, StrLEVEL + APairsCount.ToString, GameTime);
    FIniFile.UpdateFile;
end;

function TDMGameOfMem.ReadScore(APairsCount: Integer): TTime;
begin
    Result := FIniFile.ReadTime(StrSCORES, StrLEVEL + APairsCount.ToString, -1);
end;

內容解密:

這兩段程式碼分別用於儲存和讀取玩家的最佳成績。成績以時間的形式儲存在INI檔案中,檔案的路徑和名稱是在程式啟動時動態生成的。儲存成績時,會根據當前的遊戲難度生成對應的鍵值,以區分不同難度的最佳成績。

建構遊戲主畫面與設定頁面

在前面的章節中,我們已經瞭解瞭如何利用 Delphi 和 FireMonkey 開發 2D 遊戲。本章節將繼續探討如何完成遊戲的設定頁面以及實作遊戲的儲存和讀取功能。

資料模組的擴充

首先,我們需要在資料模組(Data Module)中新增方法來清除所有分數、儲存和讀取目前的遊戲等級。以下為相關程式碼:

procedure TDMGameOfMem.ClearAllScores;
begin
  FIniFile.EraseSection(StrSCORES);
  FIniFile.UpdateFile;
end;

procedure TDMGameOfMem.SaveCurrLevel(APairsCount: Integer);
begin
  FIniFile.WriteInteger(StrSETTINGS, StrCURRLEVEL, APairsCount);
  FIniFile.UpdateFile;
end;

function TDMGameOfMem.ReadCurrLevel: Integer;
begin
  Result := FIniFile.ReadInteger(StrSETTINGS, StrCURRLEVEL, 4);
end;

內容解密:

  • ClearAllScores 方法用於清除 INI 檔案中的所有分數記錄,透過 EraseSection 方法刪除特定區段的內容。
  • SaveCurrLevel 方法儲存目前的遊戲等級到 INI 檔案中,WriteInteger 方法用於寫入整數值。
  • ReadCurrLevel 方法讀取目前儲存的遊戲等級,預設值為 4。

主畫面的事件處理

接下來,我們需要在主畫面的 OnCreate 事件中讀取最後使用的難度等級,並在下拉式選單的 OnChange 事件中儲存目前選擇的難度等級:

procedure TFormMain.FormCreate(Sender: TObject);
begin
  CurrPairsCount := DMGameOfMem.ReadCurrLevel;
end;

procedure TFormMain.CmbbxLevelChange(Sender: TObject);
begin
  DMGameOfMem.SaveCurrLevel(CurrPairsCount);
end;

內容解密:

  • FormCreate 事件中,我們呼叫 ReadCurrLevel 方法來取得儲存的遊戲等級。
  • CmbbxLevelChange 事件中,我們呼叫 SaveCurrLevel 方法來儲存目前選擇的遊戲等級。

遊戲設定頁面的實作

我們的遊戲幾乎完成了,但還缺少一個頁面來顯示最高分數。以下為設定頁面的實作步驟:

  1. 在設定表單上放置一個標籤元件並對齊至客戶端,修改其 Text 屬性為「最高分數」。
  2. 新增一個按鈕元件到工具列上,對齊至右側,修改其 Name 屬性為 BtnClearText 屬性為「清除所有」。
  3. 新增一個清單方塊元件到表單上,對齊至客戶端,並調整其 Margins 屬性以增加邊距。
  4. 在清單方塊中新增多個清單專案,並修改其 TextDetail 屬性以顯示不同難度等級的最佳時間。

相關程式碼如下:

procedure TFormSettings.FormShow(Sender: TObject);
begin
  ReadTopScores;
end;

procedure TFormSettings.btnClearClick(Sender: TObject);
begin
  DMGameOfMem.ClearAllScores;
  ReadTopScores;
end;

procedure TFormSettings.ReadTopScores;
  procedure ShowScore(Lbi: TListBoxItem; Level: integer);
  begin
    var T: TTime := DMGameOfMem.ReadScore(Level);
    if T > 0 then
      Lbi.ItemData.Detail := GameTimeToStr(T)
    else
      Lbi.ItemData.Detail := '';
  end;
begin
  ShowScore(lbi04, 4);
  ShowScore(lbi06, 6);
  ShowScore(lbi08, 8);
  ShowScore(lbi10, 10);
  ShowScore(lbi12, 12);
  ShowScore(lbi14, 14);
  ShowScore(lbi16, 16);
  ShowScore(lbi18, 18);
end;

內容解密:

  • ReadTopScores 方法用於讀取並顯示不同難度等級的最高分數。
  • ShowScore 程式用於更新特定清單專案的詳細資訊,顯示對應難度等級的最佳時間。

FireMonkey 的 3D 功能簡介

在接下來的章節中,我們將進一步探討 FireMonkey 的 3D 功能,包括跨平台的 3D 渲染、直接使用 Context3D、以及混合使用 2D 和 3D 元件等主題。

FireMonkey 為開發者提供了強大的跨平台開發能力,不僅限於 2D,也包括了豐富的 3D 程式設計介面和元件。利用 FireMonkey,我們可以建立完全跨平台的 GPU 加速 GUI,並且根據每個平台的特定 3D API。

本章節的目標是學習 FireMonkey 的 3D 程式設計,並透過範例展示不同的實作方法。無論是使用預建的 3D 元件還是直接操作底層的 3D API,FireMonkey 都提供了靈活且強大的工具來實作跨平台的 3D 圖形應用。

FireMonkey 在 3D 開發中的應用

FireMonkey 提供了一個強大且易於使用的框架,用於建立複雜的、根據 GPU 的 3D 使用者介面。透過使用可重用的視覺元件,開發者可以專注於業務邏輯的實作,而無需花費大量時間編寫底層的 3D API 程式碼。

跨平台的 3D 渲染支援

FireMonkey 支援多種 3D API,包括行動裝置上的 OpenGL ES、Windows 上的 DirectX 和 Mac 上的完整 OpenGL。這些 API 具有不同的介面和抽象層,但 FireMonkey 提供了一個統一的介面,使得開發者可以使用一套程式碼函式庫來建立跨平台的 3D 應用程式。

TContext3D 類別:3D 渲染的核心

TContext3D 是 FireMonkey 中用於 3D 渲染的核心類別。它定義了一系列虛擬的繪圖方法,這些方法在不同的目標平台上會有不同的實作。這種架構與 FireMonkey 的 2D 渲染類別似,後者使用抽象的 TCanvas 類別,並在不同的作業系統上提供不同的實作。

使用 TContext3D 直接進行 3D 渲染

要使用 TContext3D 直接進行 3D 渲染,首先需要建立一個新的 Delphi 多裝置專案,並選擇 3D 應用程式範本。然後,可以在表單的 OnRender 事件中存取 TContext3D 物件,並使用其方法進行 3D 繪圖。

procedure TFormCubeInCode.Form3DRender(Sender: TObject; Context: TContext3D);
begin
  Context.BeginScene;
  try
    // 在此存取 Context 的 3D 方法和屬性
    Context.DrawCube(Point3D(0,0,0), Point3D(10,10,10), 1, TAlphaColorRec.Black);
  finally
    Context.EndScene;
  end;
end;

程式碼解密:

  • Context.BeginSceneContext.EndScene 用於標記 3D 場景的開始和結束。
  • Context.DrawCube 用於繪製一個立方體,第一個引數是立方體的中心點,第二個引數是立方體的大小,第三個引數是線寬,最後一個引數是繪製的顏色。

為了讓 OnRender 事件被呼叫,需要使用一個 TTimer 元件。在 Timer 的 OnTimer 事件中呼叫表單的 Invalidate 方法,以強制重新渲染表單。

procedure TFormCubeInCode.Timer1Timer(Sender: TObject);
begin
  Invalidate;
end;

程式碼解密:

  • Invalidate 方法用於使表單無效,從而觸發 OnRender 事件。

使用元件進行 3D 渲染

除了直接使用 TContext3D 進行 3D 渲染外,FireMonkey 也提供了許多可重用的元件,用於簡化 3D 應用程式的開發。這些元件可以讓開發者快速建立複雜的 3D 場景,而無需編寫大量的程式碼。