赞
踩
本文作者为奇舞团大前端CodeFarmer
笔者在公司前前后后做了有小一年Flutter 开发,从入门到后面业务方变动,到暂时放弃Flutter。对于Flutter争议不提,我们得承认Flutter 是一款很优秀的跨端解决方案,到前段时间的Flutter3.0的提出,3.0对游戏做了很友好的支持,笔者又重新开始以游戏为切入点 去上手Flutter。所以我们探索一下Flutter3.0 对于游戏的支持力度,是否可以低成本写出一个自己的小游戏呢?
一套代码多端运行(Flutter 特性):可以想想开发一款游戏,既能争安卓市场,苹果市场还能挣Web市场的钱,是不是很好?
比较流畅的仿原生环境,与纯原生环境相比流畅度无太大的降低;
游戏很挣钱,apple store 收入70%来自游戏;
Flutter 3.0 新出了对广告、应用内购买、Firebase、Play 服务和游戏中心等服务的预构建集成加快游戏开发;(方便发布游戏,3.0对游戏支持很友好)。
Flutter 侧重2d游戏,3D游戏 参考其他技术,如 unity3d
以Mac 电脑为例,去准备Flutter 环境
Flutter 3.0 SDK 下载
下载以下安装包以获取 Flutter SDK 的最新稳定版本:
Intel芯片 | M1 芯片 | |
---|---|---|
flutter_macos_3.0.1-stable.zip | flutter_macos_arm64_3.0.1-stable.zip |
解压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:name="com.google.android.gms.ads.APPLICATION_ID"
- android:value="ca-app-pub-1234567890123456~1234567890"/>
要启用游戏服务,请先在 iOS 上设置Game Center ,在 Android 上设置Google Play 游戏服务
让用户登录游戏中心 (iOS) 或 Google play 游戏服务 (Android)。在进行任何操作(例如发送分数或解锁成就)之前,应该先登录。
GamesServices.signIn();
检查当前用户是否登录游戏服务(ios游戏中心或者Google play 游戏服务)
GamesServices.isSignedIn;
让用户退出 ios游戏中心/Goole Play 服务。调用后,将无法再对该用户的帐户进行任何操作。
GamesServices.signOut();
GamesServices.showAchievements();
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));
增加安卓成就的步骤
- final result = await GamesServices.increment(achievement: Achievement(androidID: 'android_id', steps: 50));
- print(result);
GamesServices.showAccessPoint(AccessPointLocation.topLeading);
GamesServices.hideAccessPoint();
final playerID = GamesServices.getPlayerID();
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 游戏添加音频功能的模块。
- 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 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 引擎:(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)
- END -
奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。