【基於Flutter&Flame 的飛機大戰開發筆記】利用bloc管理遊戲狀態
theme: cyanosis highlight: xcode
前言
繼續來開發飛機大戰,遊戲內的基本構成都已經實現。剩下的就是面板功能了,譬如生命值、分數,還有之前一直沒有實現的導彈道具。本文將記錄如何利用bloc
來做狀態管理。
筆者將這一系列文章收錄到以下專欄,歡迎有興趣的同學閱讀:
遊戲中的bloc運用
由於本文重點不在理解開發模式,這裡貼一篇文章來介紹一下bloc,可以幫助您對此開發模式理解得更通透一些。
在專案中需要新增依賴equatable
、flame_bloc
、flutter_bloc
yaml
dependencies:
flutter:
sdk: flutter
flame: ^1.2.0
flame_audio: ^1.0.2
equatable: ^2.0.3
flame_bloc: ^1.6.0
flutter_bloc: ^8.0.1
筆者基於bloc
的思想,設計了對於遊戲狀態的幾個類:
- GameStatusBloc
:bloc
層,負責處理UI層傳遞過來的事件event
,並更新狀態。ps:由於飛機大戰暫時沒有複雜邏輯,這裡的處理基本都是收到一個事件然後更新一個狀態。
- GameStatusState
:狀態,這裡表示遊戲的全域性狀態,目前囊括了生命值、分數、遊戲狀態(playing、gameover
...)等。
- GameStatusEvent
:事件,這裡表示遊戲的全域性事件,譬如遊戲開始、遊戲結束、生命值增加或減少等。
以遊戲開始事件為例,看看大概的資料流是怎麼走的:
事件 GameStatusEvent
定義一個事件為遊戲開始,繼承自GameStatusEvent
```dart
abstract class GameStatusEvent extends Equatable {
const GameStatusEvent();
}
class GameStart extends GameStatusEvent { const GameStart();
@override List
狀態 GameStatusState
這裡對遊戲執行狀態有一個列舉GameStatus
的定義
dart
enum GameStatus {
initial, // 初始化
playing, // 遊戲中
gameOver // 遊戲結束
}
GameStatusState
的定義包括生命值、分數、導彈道具數、遊戲執行狀態
dart
class GameStatusState extends Equatable {
final int score;
final int lives;
final GameStatus status;
final int bombSupplyNumber;
。。。
bloc層 GameStatusBloc
GameStatusBloc
定義了接收到事件GameStart
後,如何更新狀態GameStatusState
```dart
class GameStatusBloc extends Bloc
on<GameStart>((event, emit) {
emit(state.copyWith(status: GameStatus.playing));
});
。。。
}
}
``
這裡是將遊戲執行狀態
GameStatus更新為
playing`。
而GameStatusBloc
的物件會被儲存在Game
中,當遊戲開始時,就會呼叫Game#gameStart()
將事件傳送出去。ps:這裡類名被修改成SpaceGame
,與之前的文章有些不同。
```dart
class SpaceGame extends FlameGame with HasDraggables, HasCollisionDetection {
final GameStatusBloc gameStatusBloc;
SpaceGame({required this.gameStatusBloc});
。。。
void gameStart() { gameStatusBloc.add(const GameStart()); }
。。。 } ```
這樣再結合上述的流程圖,一個基於bloc
管理的全域性狀態雛型就出來了。可以注意到上述的GameStatusBloc
是通過構造方法傳遞下來的,接下來看看它真正建立的地方在哪。
結合flutter_bloc
GameStatusBloc
是通過BlocProvider
從Flutter的父Widget
傳遞下去的,這裡使用MultiBlocProvider
支援多個provider
。筆者對之前的程式碼進行了擴充套件,GameView
裡面包含了Flame中的GameWidget
。這樣做主要是想利用Flutter的控制元件來編寫面板展示的邏輯,這個本文不涉及所以可暫不理會。
```dart
void main() {
runApp(const MaterialApp(
home: GamePage(),
));
}
class GamePage extends StatelessWidget { const GamePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: MultiBlocProvider(
providers: [
// GameStatusBloc的建立
BlocProvider
// class GameView
GameWidget(game: SpaceGame(gameStatusBloc: context.read
然後再回去看看Game#onLoad
方法,在Flame中可以通過FlameBlocProvider
將GameStatusBloc
傳遞給子Component
,子Component
可對此進行狀態監聽。這裡使用FlameMultiBlocProvider
,支援多個provider
。
```dart
@override
Future
await add(FlameMultiBlocProvider(providers: [
FlameBlocProvider
上述程式碼可知,這裡的Component樹層級關係與之前有所不同
這樣在FlameMultiBlocProvider
下的子Component
就能監聽到GameStatusState
的變化了。
監聽GameStatusState變化
繼續利用上面的遊戲開始事件為例,筆者在Player#onLoad
中添加了一個進場效果,用的是之前的MoveEffect
。
```dart
// class Player
@override
Future
add(MoveEffect.to(Vector2(position.x, gameRef.size.y * 0.75), EffectController(duration: 1.5, curve: Curves.easeOutSine)) ..onComplete = () { gameRef.gameStart(); });
add(FlameBlocListener``
- 進場效果**完成後**,會呼叫
Game#gameStart(),這樣就與前面的邏輯形成閉環了,經過
bloc的處理,
GameStatusState就更新為
playing了。
- 還記得這裡之前有一個
Timer用於**定時發射子彈**嗎?之前的開啟和停止是依賴
onMount/onRemove的,這裡就通過
FlameBlocListener回撥的遊戲狀態決定了。
- 筆者將
Player改成一個
SpriteAnimationGroupComponent了,主要是方便作
戰機Component被擊毀的效果,這個**與之前的
Enemy`類似**就不多贅述了。【基於Flutter&Flame 的飛機大戰開發筆記】重構敵機
ps:之前的EnemyCreator
定時生成的邏輯也是同理。
最後
本文主要記錄基於bloc
管理飛機大戰的全域性狀態,相關邏輯參考Flame官方的例子:flame/packages/flame_bloc。後續會基於此狀態來新增遊戲面板的邏輯。
- 用華為CameraKit實現預覽和拍照
- 重溫今日頭條螢幕適配方案
- 【基於Flutter&Flame 的飛機大戰開發筆記】展示面板及重新開始選單
- 【基於Flutter&Flame 的飛機大戰開發筆記】利用bloc管理遊戲狀態
- 【基於Flutter&Flame 的飛機大戰開發筆記】子彈升級和補給
- 【基於Flutter&Flame 的飛機大戰開發筆記】重構敵機
- 【基於Flutter&Flame 的飛機大戰開發筆記】子彈發射及碰撞檢測
- 【基於Flutter&Flame 的飛機大戰開發筆記】敵機生成器
- 【基於Flutter&Flame 的飛機大戰開發筆記】搭建專案及建立一架戰機
- 一文搞定移動端接入ncnn模型(包括Android、iOS)
- 在Android上實現Metal的計算Demo
- 移動端執行JS指令碼除錯方案-單元測試
- 為什麼Glide4.x中的AppGlideModule不應該出現在Library中
- CameraX OpenGL預覽的全新版本
-
有關Swift Codable解析成Dictionary
的一些事 - 在iOS應用上進行記憶體監控
- 體驗一下用Metal畫圖
- 在iOS上進行WebP編碼是一種怎樣的體驗之為何cpu佔用如此之高?
- 在iOS上進行WebP編碼是一種怎樣的體驗?