初探 Flutter 跨端遊戲開發
本文作者爲奇舞團大前端 CodeFarmer
背景
筆者在公司前前後後做了有小一年 Flutter 開發,從入門到後面業務方變動,到暫時放棄 Flutter。對於 Flutter 爭議不提,我們得承認 Flutter 是一款很優秀的跨端解決方案,到前段時間的 Flutter3.0 的提出,3.0 對遊戲做了很友好的支持,筆者又重新開始以遊戲爲切入點 去上手 Flutter。所以我們探索一下 Flutter3.0 對於遊戲的支持力度,是否可以低成本寫出一個自己的小遊戲呢?
Why?爲什麼要做 Flutter 遊戲開發?
-
一套代碼多端運行(Flutter 特性):可以想想開發一款遊戲,既能爭安卓市場,蘋果市場還能掙 Web 市場的錢,是不是很好?
-
比較流暢的仿原生環境,與純原生環境相比流暢度無太大的降低;
-
遊戲很掙錢,apple store 收入 70% 來自遊戲;
-
Flutter 3.0 新出了對廣告、應用內購買、Firebase、Play 服務和遊戲中心等服務的預構建集成加快遊戲開發;(方便發佈遊戲,3.0 對遊戲支持很友好)。
-
Flutter 側重 2d 遊戲,3D 遊戲 參考其他技術,如 unity3d
Flutter3.0 環境準備
-
以 Mac 電腦爲例,去準備 Flutter 環境
-
Flutter 3.0 SDK 下載
- 下載以下安裝包以獲取 Flutter SDK 的最新穩定版本:
-
解壓 SDK
cd ~/development unzip ~/Downloads/flutter_macos_arm64_3.0.1-stable.zip
-
添加環境變量:(關於 Mac 環境變量 不累述: 參考)
export PATH="$PATH:`pwd`/flutter/bin" open ~/.bash_profile source ~/.bash_profile
- 查看Flutter環境完整性: flutter doctor flutter doctor
環境常見問題
-
問題 1:CocoaPods 環境依賴安裝 cocoapods
sudo gem install cocoapods Error: To set up CocoaPods for ARM macOS, run: arch -x86_64 sudo gem install ffi
arch -x86_64 sudo gem install ffi、 Building native extensions. This could take a while... Successfully installed ffi-1.15.5 Parsing documentation for ffi-1.15.5 Done installing documentation for ffi after 3 seconds 1 gem installed
-
問題 2:Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses
flutter doctor --android-licenses
完整環境如下:非必須,缺失部分環境不影響開發
[✓] Flutter (Channel stable, 3.0.1, on macOS 12.4 21F79 darwin-arm, locale
zh-Hans-CN)
[✓] Android toolchain - develop for Android devices (Android SDK version
32.1.0-rc1)
[✓] Xcode - develop for iOS and macOS (Xcode 13.4)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.2)
[✓] IntelliJ IDEA Ultimate Edition (version 2020.3.2)
[✓] VS Code (version 1.67.2)
[✓] Connected device (4 available)
[✓] HTTP Host Availability
• No issues found!
第一個遊戲模板
這個 Flutter 示例遊戲 repo 預先集成了應用內購買、移動廣告 SDK 和許多其他成功遊戲的模塊;
cd flutterdemo
git clone https://github.com/flutter/samples.git
Flutter 中的入門遊戲,具有移動端(iOS 和 Android)遊戲的所有到發佈基本集成,包括以下功能:
-
聲音
-
音樂
-
主菜單畫面
-
設置
-
廣告 (AdMob)
-
應用內購買
-
遊戲服務(遊戲中心和 Google Play 遊戲服務)
-
崩潰報告 (Firebase Crashlytics)
lib
├── src
│ ├── ads//廣告
│ ├── app_lifecycle//生命週期
│ ├── audio//音頻
│ ├── crashlytics//崩潰日誌
│ ├── game_internals//
│ ├── games_services//遊戲服務
│ ├── in_app_purchase//應用內購買
│ ├── level_selection//等級
│ ├── main_menu//menu
│ ├── play_session//
│ ├── player_progress//用戶進度
│ ├── settings//設置
│ ├── style
│ └── win_game//勝利
├── ...
└── main.dart
cd samples/game_template/
flutter run
Multiple devices found:
[1]: ANA AN00 (NAB5T20525007949)
[2]: iPhone 12 (159CF48A-D131-4187-9E51-391759D8ADC8)
[3]: macOS (macos)
[4]: Chrome (chrome)
Warning: CocoaPods not installed. Skipping pod install.
CocoaPods is used to retrieve the iOS and macOS platform side's plugin code
that responds to your plugin usage on the Dart side.
Without CocoaPods, plugins will not work on iOS or macOS.
For more info, see https://flutter.dev/platform-plugins
To install see
https://guides.cocoapods.org/using/getting-started.html#installation for
instructions.
啓動工程
flutter clean
flutter pub get
flutter run
通過以上模板,我們發現關鍵引入信息如下
games_services: ^2.0.7 # 成就和排行榜
google_mobile_ads: ^1.1.0 # 廣告
in_app_purchase: ^3.0.1 # 應用內購買
廣告 id 切換:ios/Runner/Info.plist android/app/src/main/AndroidManifest.xml
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-1234567890123456~0987654321</string>
<meta-data
android:
android:value="ca-app-pub-1234567890123456~1234567890"/>
games_services
- 要啓用遊戲服務,請先在 iOS 上設置 Game Center ,在 Android 上設置 Google Play 遊戲服務
登錄
讓用戶登錄遊戲中心 (iOS) 或 Google play 遊戲服務 (Android)。在進行任何操作(例如發送分數或解鎖成就)之前,應該先登錄。
GamesServices.signIn();
判斷登錄
檢查當前用戶是否登錄遊戲服務(ios 遊戲中心或者 Google play 遊戲服務)
GamesServices.isSignedIn;
登出
讓用戶退出 ios 遊戲中心 / Goole Play 服務。調用後,將無法再對該用戶的帳戶進行任何操作。
GamesServices.signOut();
顯示成就界面
GamesServices.showAchievements();
顯示排行榜 - 入參需要 ios_leaderboard_id 和 android_leaderboard_id
GamesServices.showLeaderboards(iOSLeaderboardID: 'ios_leaderboard_id', androidLeaderboardID: 'android_leaderboard_id');
提交分數
提交分數到排行榜
/**
入參需要android_leaderboard_id和ios_leaderboard_id
*/
GamesServices.submitScore(score: Score(androidLeaderboardID: 'android_leaderboard_id',
iOSLeaderboardID: 'ios_leaderboard_id',
value: 5));
解鎖成就
/**
android_id
ios_id
percentComplete` 成就的完成百分比,這個參數在iOS的情況下是可選的
`steps` Android 的成就步驟
*/
GamesServices.unlock(achievement: Achievement(androidID: 'android_id',
iOSID: 'ios_id',
percentComplete: 100,
steps: 2));
增加步驟 (Android Only)
增加安卓成就的步驟
final result = await GamesServices.increment(achievement: Achievement(androidID: 'android_id', steps: 50));
print(result);
顯示接入點 (iOS Only)
GamesServices.showAccessPoint(AccessPointLocation.topLeading);
隱藏接入點 (iOS Only)
GamesServices.hideAccessPoint();
獲取 Player id
final playerID = GamesServices.getPlayerID();
獲取 Player name
final playerName = GamesServices.getPlayerName();
小結
以上介紹了 Flutter3.0 和 3.0 對遊戲友好的支持,可以方便的打通移動端,方便的接入廣告等服務,可以讓開發者更專注遊戲本身開發,而非 廣告、音頻控制、用戶排名,應用支付等,下面我們介紹一下 Flutter 遊戲的核心,常用的遊戲引擎和使用。
遊戲引擎
Flame engine:https://github.com/flame-engine/flame/blob/main/i18n/README-ZH.md
Flame 引擎的目的是爲使用 Flutter 開發的遊戲會遇到的常見問題提供一套完整的解決方案,Flame 利用了 Flutter 的強大功能,並提供了一種輕量級的方法來爲所有平臺開發 2-D 遊戲。
目前 Flame 提供了以下功能:
-
遊戲循環 (game loop)
-
組件 / 對象系統 (FCS)
-
特效與粒子效果
-
碰撞檢測
-
手勢和輸入支持
-
圖片、動畫、精靈 (sprite) 以及精靈組
-
一些簡化開發的實用工具類
除了以上的功能以外,你可以使用一些橋接 Flame 的 package 來增強引擎本身的功能。 通過這些橋接 package,你可以訪問 Flame 的組件、幫助程序, 或是與其他 package 進行綁定,從而達到平滑集成的效果。 目前我們有以下的橋接 package(Flame 引擎是模塊化的,允許用戶選擇他們想要使用的 API):
-
Flame – 核心包,提供遊戲循環、基本碰撞檢測、Sprite 和組件。
-
flame_audio 橋接 AudioPlayers :可同時播放多個音頻。
-
flame_bloc 橋接 Bloc :BloC 狀態管理。
-
flame_fire_atlas 橋接 FireAtlas :爲遊戲創建紋理圖集。
-
flame_forge2d 橋接 Forge2D :基於 Box2D 的物理引擎,具有高級碰撞檢測的物理引擎,從 Box2D 移植到與 Flame 一起使用
-
flame_lint - 引擎的代碼格式規則 (
analysis_options.yaml
)。 -
flame_oxygen 橋接 Oxygen :輕量的實體 - 組件 - 系統 (ECS)。
-
Oxygen 是一個用 Dart 編寫的輕量級實體組件系統框架,專注於性能和易用性。 Oxygen 在設計上是不可知的,您想要使用的任何遊戲引擎都可以與 Oxygen 一起使用。
目標 Oxygen 深受 ECSY 的啓發,因此它具有相同的設計原則。 Oxygen 的主要目標是輕巧、高性能和易於使用。 藉助 API 嘗試並幫助您充分利用 ECS 設計模式,而不會限制您構建邏輯。
-
flame_rive 橋接 Rive :創建可交互的動畫。https://rive.app/get-started/
-
RiveAnimation.asset('assets/truck.riv');
-
flame_svg 橋接 flutter_svg :在 Flutter 中繪製 SVG。
-
final String assetName = 'assets/image.svg'; final Widget svg = SvgPicture.asset( assetName, semanticsLabel: 'Acme Logo' );
-
flame_tiled 橋接 Tiled :二維平面的地圖編輯器。
-
下載
-
flame_audio– 爲 Flame 遊戲添加音頻功能的模塊。
2D 遊戲小例子
-
dependencies: flutter: sdk: flutter flame: 1.1.1
-
runApp(const App());-> GameWidget(game)& 一個自定義pad 佈局
class Joypad extends StatefulWidget { //自定義pad 略 可以參考:https://pub.dev/packages/control_pad }
-
class Player extends SpriteComponent with HasGameRef{ @override Future<void> onLoad() async { super.onLoad(); // TODO 1 sprite = await gameRef.loadSprite('player/player.png'); position = gameRef.size / 2; } }
Plyaer:
-
class MyGame extends FlameGame { final Player _player = Player(); @override Future<void> onLoad() async { add(_player); // empty } }
-
//Joypad(onDirectionChanged: onJoypadDirectionChanged) 方向控制pad void onJoypadDirectionChanged(Direction direction) { // TODO 2 game.onJoypadDirectionChanged(direction); }
-
game->player-> 更新方向
-
//Player update 更新頻率爲 16毫秒左右 @override void update(double delta) { super.update(delta); //移動小孩 movePlayer(delta); print('update 更新時間---》${(DateTime.now().microsecondsSinceEpoch - _timeNow)}'); _timeNow = DateTime.now().microsecondsSinceEpoch; }
60hz=16 毫秒刷新
- 關於移動速度和方向
void movePlayer(double delta) {
// TODO
switch (direction) {
case Direction.up:
moveUp(delta);
break;
case Direction.down:
moveDown(delta);
break;
case Direction.left:
moveLeft(delta);
break;
case Direction.right:
moveRight(delta);
break;
case Direction.none:
break;
}
}
final double _playerSpeed = 300.0;
void moveUp(double delta) {
position.add(Vector2(0, -(delta * _playerSpeed)));
}
void moveDown(double delta) {
position.add(Vector2(0, delta * _playerSpeed));
}
void moveRight(double delta) {
position.add(Vector2(delta * _playerSpeed, 0));
}
void moveLeft(double delta) {
position.add(Vector2(-delta * _playerSpeed, 0));
}
如果遊戲視圖的直徑爲 2500×2500 像素,則您的玩家從座標 x:1250, y:1250 的中間開始。 調用 moveDown
會爲玩家的 Y 位置增加大約 300 像素,用戶在向下方向握住手柄時,會導致精靈向下移動遊戲視口。
-
地圖
//地圖也是精靈,所以加載方式跟精靈一樣 class MyMap extends SpriteComponent with HasGameRef { @override Future<void>? onLoad() async { sprite = await gameRef.loadSprite('player/rayworld_background.png'); size = sprite!.originalSize; return super.onLoad(); } } //地圖加載 class MyGame extends FlameGame { final Player _player = Player(); final MyMap _map = MyMap(); @override Future<void> onLoad() async { await add(_map);//添加地圖 add(_player); } }
-
添加會動的精靈 player
player_spritesheet
Player extends SpriteAnimationComponent with HasGameRef{ @override Future<void> onLoad() async { super.onLoad(); _loadAnimations().then((_) => {animation = _standingAnimation}); } Future<void> _loadAnimations() async { final spriteSheet = SpriteSheet( image: await gameRef.images.load('player/player_spritesheet.png'), srcSize: Vector2(29.0, 32.0),//1個精靈的像素大小 ); _runDownAnimation = spriteSheet.createAnimation(row: 0, stepTime: _animationSpeed, to: 4); _runLeftAnimation = spriteSheet.createAnimation(row: 1, stepTime: _animationSpeed, to: 4); _runUpAnimation = spriteSheet.createAnimation(row: 2, stepTime: _animationSpeed, to: 4); _runRightAnimation = spriteSheet.createAnimation(row: 3, stepTime: _animationSpeed, to: 4); _standingAnimation = spriteSheet.createAnimation(row: 0, stepTime: _animationSpeed, to: 1); } void movePlayer(double delta) { switch (direction) { case Direction.up: //動畫方向切換 animation = _runUpAnimation; moveUp(delta); break; case Direction.down: animation = _runDownAnimation; moveDown(delta); break; case Direction.left: animation = _runLeftAnimation; moveLeft(delta); break; case Direction.right: animation = _runRightAnimation; moveRight(delta); break; case Direction.none: break; } } }
至此我們的利用 Flame 做的一個遊戲入門就結束了
當然遊戲開發很複雜,想象力最重要!
Bonfire
Bonfire 引擎:(RPG 類) 可以創造 Flutter.2D 遊戲的引擎,基於 Flame
引用 & 資源
FlutterGame:https://flutter.dev/games games-toolkit
文檔:https://docs.flutter.dev/resources/games-toolkit
遊戲資源 1:https://itch.io/game-assets
遊戲資源 2:https://itch.io/ Flutter
遊戲地圖:https://pub.dev/packages/level_map)
關於奇舞團
奇舞團是 360 集團最大的大前端團隊,代表集團參與 W3C 和 ECMA 會員(TC39)工作。奇舞團非常重視人才培養,有工程師、講師、翻譯官、業務接口人、團隊 Leader 等多種發展方向供員工選擇,並輔以提供相應的技術力、專業力、通用力、領導力等培訓課程。奇舞團以開放和求賢的心態歡迎各種優秀人才關注和加入奇舞團。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/ww5E6zAWbEKquI__zg002w