更新於 2024/12/01閱讀時間約 11 分鐘

【Flame 學習筆記】 FlameGame:建立基於元件的遊戲架構

raw-image
【Flame 學習筆記】系列文章目錄:連結
【Flutter 學習筆記】線上課程教學影片:連結
【Flutter 學習筆記】系列文章目錄:連結

在這篇文章中,我們將深入探討 Flutter 的 Flame 遊戲引擎中的 FlameGame 類別,並了解其如何實現一個基於元件的遊戲架構。這個架構稱為 Flame 元件系統(Flame Component System,簡稱 FCS),它透過一棵元件樹來管理遊戲中的所有元件,並負責調用這些元件的更新和渲染方法。

FlameGame 類別概述

FlameGame 類別是 Flame 引擎的核心,允許開發者透過構造函數中的 children 參數直接添加元件,或使用 addaddAll 方法在其他地方添加元件。通常,我們會將元件添加到一個名為 World 的預設世界中,這個世界可以透過 FlameGame.world 訪問,並且添加元件的方式與其他元件相同。

以下是一個簡單的 FlameGame 實作範例,展示如何在 onLoad 方法中和構造函數中添加兩個元件:

import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flutter/widgets.dart';

/// 一個渲染箱子精靈的元件,大小為 16 x 16。
class MyCrate extends SpriteComponent {
MyCrate() : super(size: Vector2.all(16));

@override
Future<void> onLoad() async {
sprite = await Sprite.load('crate.png');
}
}

class MyWorld extends World {
@override
Future<void> onLoad() async {
await add(MyCrate());
}
}

void main() {
final myGame = FlameGame(world: MyWorld());
runApp(
GameWidget(game: myGame),
);
}

注意事項

如果你在 build 方法中實例化遊戲,則每當 Flutter 樹重建時,遊戲也會被重建,這通常會比你預期的更頻繁。為了避免這種情況,你可以先創建遊戲的實例,然後在小部件結構中引用它,或者使用 GameWidget.controlled 構造函數。

遊戲循環

GameLoop 模組是遊戲循環概念的一個簡單抽象。大多數遊戲都基於兩個方法:

  • render 方法用於繪製當前遊戲狀態的畫布。
  • update 方法接收自上次更新以來的微秒數,並允許你移動到下一個狀態。

GameLoop 被所有 Flame 的遊戲實作所使用。

調整大小

每當遊戲需要調整大小時,例如當方向改變時,FlameGame 會調用所有元件的 onGameResize 方法,並將此信息傳遞給相機和視口。FlameGame.camera 控制應該在視口中心的坐標點,預設為 [0,0](Anchor.center)。

生命週期

FlameGame 的生命週期回調(如 onLoadrender 等)按以下順序被調用:

  1. 每個 Tick 運行
  2. 添加和調整大小時運行
  3. 只運行一次
  4. 如果被移除
  5. 如果重新父化
  6. onGameResize
  7. onLoad
  8. onMount
  9. update
  10. render
  11. onRemove

FlameGame 首次添加到 GameWidget 時,生命週期方法 onGameResizeonLoadonMount 將按此順序被調用。然後,updaterender 將在每個遊戲 Tick 中按序列被調用。如果 FlameGameGameWidget 中移除,則會調用 onRemove。如果 FlameGame 被添加到新的 GameWidget,則序列將從 onGameResize 重複。

注意事項

onGameResizeonLoad 的調用順序與其他元件相反,這是為了在資源加載或生成之前計算遊戲元素的大小。

onRemove 回調可用於清理子元件和緩存數據:

@override
void onRemove() {
// 根據遊戲需求選擇性執行。
removeAll(children);
processLifecycleEvents();
Flame.images.clearCache();
Flame.assets.clearCache();
// 在遊戲被移除時執行的其他代碼。
}

注意事項

FlameGame 中,子元件和資源的清理並不會自動進行,必須明確添加到 onRemove 回調中。

除錯模式

Flame 的 FlameGame 類別提供了一個名為 debugMode 的變數,預設為 false。不過,你可以將其設置為 true 以啟用遊戲元件的除錯功能。請注意,這個變數的值會在元件添加到遊戲時傳遞,因此如果你在運行時更改 debugMode,則不會影響已添加的元件。

有關 debugMode 的更多資訊,請參考 Flame 的除錯文檔。

更改背景顏色

要更改 FlameGame 的背景顏色,你需要重寫 backgroundColor() 方法。在以下範例中,背景顏色被設置為完全透明,以便可以看到 GameWidget 背後的元件。預設背景顏色為不透明的黑色。

class MyGame extends FlameGame {
@override
Color backgroundColor() => const Color(0x00000000);
}

請注意,背景顏色在遊戲運行時無法動態更改,但如果你希望它動態變化,可以繪製一個覆蓋整個畫布的背景。

單一遊戲實例混入

如果你正在開發一個單一遊戲應用程式,可以選擇性地將 SingleGameInstance 混入應用於你的遊戲。這在構建遊戲時是一個常見的場景:有一個全螢幕的 GameWidget,其中只包含一個遊戲實例。

添加這個混入可以在某些情況下提供性能優勢。特別是,元件的 onLoad 方法在元件添加到其父元件時保證會開始執行,即使父元件尚未掛載。因此,對 parent.add(component)await 將始終保證完成元件的加載。

使用這個混入非常簡單:

class MyGame extends FlameGame with SingleGameInstance {
// ...
}

低階遊戲 API

Game 類別是一個低階 API,當你想要實現遊戲引擎的結構功能時可以使用。Game 類別不實現任何更新或渲染功能。

這個類別還包含生命週期方法 onLoadonMountonRemove,這些方法在遊戲被加載、掛載或移除時由 GameWidget(或其他父元件)調用。onLoad 只在類別第一次添加到父元件時被調用,而 onMount(在 onLoad 之後被調用)則在每次添加到新父元件時被調用。onRemove 在類別從父元件移除時被調用。

注意事項

使用 Game 類別可以提供更大的實現自由度,但如果使用它,你將錯過 Flame 中的所有內建功能。

以下是一個 Game 實作的範例:

class MyGameSubClass extends Game {
@override
void render(Canvas canvas) {
// ...
}

@override
void update(double dt) {
// ...
}
}

void main() {
final myGame = MyGameSubClass();
runApp(
GameWidget(
game: myGame,
)
);
}

暫停/恢復/逐步執行遊戲

Flame 遊戲可以通過以下兩種方式暫停和恢復:

  1. 使用 pauseEngine 和 resumeEngine 方法。
  2. 通過更改 paused 屬性。

當遊戲被暫停時,遊戲循環將被有效地暫停,這意味著在恢復之前不會進行任何更新或新的渲染。在遊戲暫停期間,可以使用 stepEngine 方法逐幀推進遊戲執行。這在最終遊戲中可能不太有用,但在開發過程中逐步檢查遊戲狀態時非常有幫助。

背景處理

當應用程式被送到背景時,遊戲將自動暫停,並在應用程式回到前景時恢復。這種行為可以通過將 pauseWhenBackgrounded 設置為 false 來禁用。

class MyGame extends FlameGame {
MyGame() {
pauseWhenBackgrounded = false;
}
}

在當前的 Flutter 穩定版本(3.13)中,這個標誌在非移動平台(包括網頁)上實際上會被忽略。

性能追蹤混入

在優化遊戲時,追蹤每幀更新和渲染所需的時間可能會很有用。這些數據可以幫助檢測代碼中運行較快的區域,並幫助識別渲染時間較長的視覺區域。

要獲取更新和渲染時間,只需將 HasPerformanceTracker 混入添加到遊戲類別中。

class MyGame extends FlameGame with HasPerformanceTracker {
// 可以訪問 `updateTime` 和 `renderTime` 屬性。
}

總結

在這篇文章中,我們深入探討了 Flame 遊戲引擎中的 FlameGame 類別及其元件系統。透過這個系統,開發者可以輕鬆地管理遊戲中的元件,並利用遊戲循環、生命週期回調、除錯模式等功能來構建高效的遊戲應用。


分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.