服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

使用 Flutter 与 Firebase 制作 I/O 弹球游戏

日期: 来源:code小生收集编辑:Flutter社区

以下内容来自公众号code小生,关注每日干货及时送达

文/ Very Good Ventures 团队,5 月 11 日发表于 Flutter 官方博客

为了今年的 Google I/O 大会,Flutter 团队使用 Flutter 以及 Firebase 构建了一款经典的弹球游戏。下面将会介绍我们是如何通过 Flame 游戏引擎将 I/O 弹球游戏[1] 带到 Web 端的。

游戏开发要点

使用 Flutter 打造用户交互类型的游戏是一个很棒的选择,例如拼图或者文字游戏这样的游戏。Flame[2] 是一个在 Flutter 上构建的 2D 游戏引擎,当涉及到使用游戏循环的游戏时它会非常有用。I/O 弹球游戏使用了 Flame 提供的一系列特性,例如动画、物理引擎、碰撞检测等等,同时还借助了 Flutter 框架的基础架构。如果你能用 Flutter 构建应用,你就获得 Flame 构建游戏所需的基础。

游戏循环

通常来说应用屏幕在没有用户交互事件的时候都会保持视觉静止状态。游戏中则是相反的—— UI 会持续的渲染,而且游戏状态会不断变化。Flame 提供了一个 game widget,它内部管理了一个游戏循环,所以能恒定且高效地进行渲染。Game 类包含了游戏组件以及其逻辑的实现,然后被交给 widget 树中的 GameWidget。在 I/O 弹球游戏中,游戏循环反映了弹球在游戏场的位置以及状态,然后如果球与物体碰撞或跌出比赛则需要给出必要的反馈。
@override
void update(double dt) {
  super.update(dt);
  final direction = -parent.body.linearVelocity.normalized();
  angle = math.atan2(direction.x, -direction.y);
  size = (_textureSize / 45) * 
    parent.body.fixtures.first.shape.radius;
}

使用 2D 组件渲染 3D 空间

在做 I/O 弹球游戏的时候,其中遇到的一个挑战即是如何使用 2D 元素渲染一个 3D 的交互体验。组件需要知道在屏幕上渲染的前后顺序。例如,当小球发射到斜坡上时,它的顺序会向前,这样就会让它看起来出现在斜坡的顶部。
弹球、弹射活塞、挡板以及 Chrome 小恐龙等等这些元素都是可活动的,这意味着它应该遵循真实世界的物理规则。而且弹球也需要根据它在板子上的位置改变其大小。当弹球滚到顶部时,它应该越来越小,以让它看着离用户更远。此外,重力还会让弹球调整角度,让它能在斜坡上更快地落下。
/// Scales the ball's body and sprite according to its position on the board.
class BallScalingBehavior extends Component with ParentIsA<Ball> {
  @override
  void update(double dt) {
    super.update(dt);
    final boardHeight = BoardDimensions.bounds.height;
    const maxShrinkValue = BoardDimensions.perspectiveShrinkFactor;
    final standardizedYPosition = parent.body.position.y +   (boardHeight / 2);
    final scaleFactor = maxShrinkValue +
        ((standardizedYPosition / boardHeight) * (1 - maxShrinkValue));
parent.body.fixtures.first.shape.radius = (Ball.size.x / 2) * scaleFactor;
final ballSprite = parent.descendants().whereType<SpriteComponent>();
    if (ballSprite.isNotEmpty) {
      ballSprite.single.scale.setValues(
        scaleFactor,
        scaleFactor,
      );
    }
  }
}

Forge 2D 的物理引擎

I/O 弹球游戏很大程度依赖了 Flame 团队维护的 forge2d[3] package。这个 package 将开源的 Box2D 物理引擎[4] 移植到 Dart 中,以便可以轻松集成到 Flutter。我们使用 forge2d 增强游戏中的物理特性,例如物体(夹板)在游戏场上的之间的碰撞检测。使用 forge2D 能够我们监听夹板发生碰撞的时机。我们就可以在这里向夹板添加交互的回调,当两个物体发生碰撞的时候我们就能收到通知。例如,弹球(它是圆形的)与弹簧(它是椭圆形的)接触时,我们就会增加它的得分。在这些回调中,我们可以清楚地设置接触开始和结束的位置,以便当两个物体相互接触时,会发生碰撞。
@override
Body createBody() {
  final shape = CircleShape()..radius = size.x / 2;
  final bodyDef = BodyDef(
    position: initialPosition,
    type: BodyType.dynamic,
    userData: this,
  );
  return world.createBody(bodyDef)
    ..createFixtureFromShape(shape, 1);
}

Sprite sheet 动画

在弹球游戏场中有一些小东西,例如 Android、Dash(Dart 吉祥物)、Sparky(Firebase 吉祥物)以及 Chrome 小恐龙,这些都是动画。对于这些东西,我们使用了 sprite sheets,它已经包含在 Flame 引擎中了,叫做 SpriteAnimationComponent。对于每个元素,我们都有一个文件,其中包含不同方向的图像、文件中的帧数以及帧之间的时间。使用这些数据,Flame 中的 SpriteAnimationComponent 能够在一个循环中将所有图像编在一起,使元素看起来在运动。
△ Sprite sheet 示例
△ Sprite sheet 示例
final spriteSheet = gameRef.images.fromCache(
  Assets.images.android.spaceship.animatronic.keyName,
);
const amountPerRow = 18;
const amountPerColumn = 4;
final textureSize = Vector2(
  spriteSheet.width / amountPerRow,
  spriteSheet.height / amountPerColumn,
);
size = textureSize / 10;
animation = SpriteAnimation.fromFrameData(
  spriteSheet,
  SpriteAnimationData.sequenced(
    amount: amountPerRow * amountPerColumn,
    amountPerRow: amountPerRow,
    stepTime: 1 / 24,
    textureSize: textureSize,
  ),
);
接下来详细解析 I/O 弹球游戏代码。

来自 Firebase 的实时积分排行榜

I/O 弹球排行榜实时地显示世界各地玩家的最高分数,玩家还可以在 Twitter 和 Facebook 上分享他们的分数。我们使用 Firebase Cloud Firestore[5] 记录排名前十的分数,将其显示在排行榜上。当一个新的分数计入排行榜时,一个 Cloud Function[6] 会将分数按降序排列并删除目前不在前十的分数。
/// Acquires top 10 [LeaderboardEntryData]s.
Future<List<LeaderboardEntryData>> fetchTop10Leaderboard() async {
  try {
    final querySnapshot = await _firebaseFirestore
      .collection(_leaderboardCollectionName)
      .orderBy(_scoreFieldName, descending: true)
      .limit(_leaderboardLimit)
      .get();
    final documents = querySnapshot.docs;
    return documents.toLeaderboard();
  } on LeaderboardDeserializationException {
    rethrow;
  } on Exception catch (error, stackTrace) {
    throw FetchTop10LeaderboardException(error, stackTrace);
  }
}

构建 Web 应用

与传统应用相比,制作响应式的游戏更容易。弹球游戏只需要根据设备的大小进行缩放即可。对于 I/O 弹球游戏,我们基于固定比例的设备大小进行缩放。确保了无论在何种显示大小,坐标系统总是相同的。保证组件在不同设备之间的一致显示和交互非常重要。
I/O 弹球游戏也适配了移动和桌面浏览器。在移动浏览器上,用户可以点击启动按钮开始播放,也可以点击屏幕左右两侧来控制相应的挡板。在桌面浏览器上,用户可以使用键盘来发射弹球和控制挡板。

代码架构

弹球代码遵循分层架构,每个功能都在自己的文件夹中。在这个项目中,游戏逻辑也与视觉组件分离。让我们能独立于游戏逻辑轻松地更新视觉元素。弹球游戏的主题取决于玩家在游戏开始前选择的角色。主题是由 CharacterThemeCubit 类控制的。根据角色的选择,球的颜色、背景和其他元素都会更新。
/// {@template character_theme}
/// Base class for creating character themes.
///
/// Character specific game components should have a getter specified here to
/// load their corresponding assets for the game.
/// {@endtemplate}
abstract class CharacterTheme extends Equatable {
  /// {@macro character_theme}
  const CharacterTheme();
/// Name of character.
  String get name;
/// Asset for the ball.
  AssetGenImage get ball;
/// Asset for the background.
  AssetGenImage get background;
/// Icon asset.
  AssetGenImage get icon;
/// Icon asset for the leaderboard.
  AssetGenImage get leaderboardIcon;
/// Asset for the the idle character animation.
  AssetGenImage get animation;
@override
  List<Object> get props => [
        name,
        ball,
        background,
        icon,
        leaderboardIcon,
        animation,
      ];
}
I/O 弹球的游戏状态是用 flam_bloc[7] 这个 package 处理的,这是一个组合了 bloc 和 Flame 组件的 package。例如,我们使用 flame_bloc 来记录剩余的游戏回合数、游戏中获得的奖励以及当前的游戏分数。另外,在 wdget 树顶层有一个 widget,它包含加载页面的逻辑以及玩游戏的说明。我们还遵循 行为型模式[8] 来封装和隔离基于组件的游戏功能元素。例如,保险杠在被球击中时会发出声音,所以我们实现了 BumperNoiseBehavior 类来处理这个问题。
class BumperNoiseBehavior extends ContactBehavior {
  @override
  void beginContact(Object other, Contact contact) {
    super.beginContact(other, contact);
    readProvider<PinballPlayer>().play(PinballAudio.bumper);
  }
}
代码库还包含全面的单元测试、组件测试和黄金测试。测试游戏会带来一些挑战,因为一个组件可能具有多个职责,使得它们很难单独地进行测试。最终我们定义了更好的隔离和测试组件的模式,并将其改进整合到 flame_test[9] 这个 package 中。

组件沙盒

这个项目高度依赖于 Flame 组件带来的仿真弹球体验。代码附带了一个组件沙盒,它类似于 Flutter Gallery 中展示的 UI 组件库[10]。在开发游戏时,这是一个很有用的工具,因为它提供了独立的每个游戏组件以确保它们的外观和行为符合预期,然后再将它们整合到游戏中。

接下来

邀请你来 I/O Pinball[11] 试玩并获取高分!关注积分排行榜并且在社交媒体上分享你的分数,也可以在 GitHub repo[12] 访问并学习项目的开源代码。

原文链接:
https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d
本地化: CFUG 团队
中文链接: https://flutter.cn/posts/i-o-pinball

文内链接

[1]

I/O 弹球游戏主页: https://pinball.flutter.dev/

[2]

Flame 开发文档主页: https://docs.flame-engine.org/

[3]

Flame 团队维护的 package: forge2d: https://pub.flutter-io.cn/packages/forge2d

[4]

Box2D 物理引擎官网: https://box2d.org/

[5]

Firebase Cloud Firestore 文档: https://firebase.google.cn/docs/firestore

[6]

Firebase Cloud Function 文档: https://firebase.google.cn/docs/functions

[7]

Flutter package: flam_bloc 页面: https://pub.flutter-io.cn/packages/flame_bloc

[8]

百度百科: 设计模式行为型模式: http://baike.baidu.com/l/i4znnfCN

[9]

Flutter package: flame_test 页面: https://pub.flutter-io.cn/packages/flame_test

[10]

Flutter Gallery 网页版: https://gallery.flutter.cn/#/

[11]

I/O Pinball 弹球游戏: https://pinball.flutter.dev/

[12]

弹球游戏源代码仓库: https://github.com/flutter/pinball


Flutter 毁了客户端和 Web 开发!
为什么那么多公司钟爱 Flutter ?
使用 Flutter 开发 3 年后,我总结了这些经验

我是code小生,喜欢可以随手点个在看、转发给你的朋友,
谢谢~


觉得不错,请点个在看

相关阅读

  • 微软正式宣布 Visual Studio 2022

  • ↓推荐关注↓转自:Rwingcnblogs.com/Rwing/p/visual-studio-2022.html首先,我们要感谢正在阅读这篇文章的你,我们所有的产品开发都始于你也止于你,无论你是在开发者社区上发帖,还
  • VSCode 自动化插件

  • 点击下方“逆锋起笔”,公众号回复 视频教程领取大佬们推荐的学习资料转载自:葡萄城技术团队来源:https://www.cnblogs.com/powertoolsteam/p/14609876.html编程是复杂的。在保
  • 太 仓 温 度 hot

  • 悠悠万事,民生为重政之所兴,在顺民心一点一滴皆用心一枝一叶总关情30年来,太仓持续把社会经济发展“大愿景”和改善民生“小目标”有效衔接,增进民生福祉,擦亮幸福底色。养老服务
  • 《HelloGitHub》第 84 期

  • 兴趣是最好的老师,HelloGitHub 让你对编程感兴趣!简介HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。https://github.com/521xueweihan/HelloGitHub这里有实战项目、入
  • 10 款开源的在线游戏,点开就能玩的那种

  • 我早前写过一篇介绍 GitHub 上开源游戏的文章:《误入 GitHub 游戏区,结果意外地收获颇丰》,文中介绍了 5 款有趣、好玩的开源游戏,虽然当时那篇文章收获了广大读者的肯定,但也有
  • 十分钟搞懂限流及常见方案

  • 来源:blog.csdn.net/liuerchong/article/details/118882053文章目录限流基本概念QPS和连接数控制传输速率黑白名单分布式环境限流方案常用算法令牌桶算法漏桶算法滑动窗口常
  • 如何使用并查集解决朋友圈问题?

  • 这是 JsonChao 的第 347 期分享大家好,我是小彭。今天分享到的是一种相对冷门的数据结构 —— 并查集。虽然冷门,但是它背后体现的算法思想却非常精妙,在处理特定问题上能做到
  • 从源码看Flutter BuildContext的秘密

  • 点击上方蓝字关注我,知识会给你力量我们每次在写Flutter代码的时候,都会看到这个参数——BuildContext,在Android开发中,也经常看见一个类似的东西——Context,它们是不是一样的
  • 解读SourceMap

  • 大厂技术 坚持周更 精选好文本文为来自 字节教育-成人与创新前端团队 成员的文章,已授权 ELab 发布。SourceMap的用途前端工程打包后代码会与源码产生不一致,当代码运行出错

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • 全开源的 Android 手机或将到来

  • 以下内容来自公众号code小生,关注每日干货及时送达文 | Travis出品 | OSC开源社区(ID:oschina2013)近日,一位来自「Simple Mobile Tools」名为 "Tibbbi" 的开发者通过 Reddit
  • 张一鸣:给产品技术人才的建议

  • 点击下方“逆锋起笔”,公众号回复 视频教程领取大佬们推荐的学习资料作者:张一鸣排版:易总这是张一鸣多年前的一篇文章,彼时的头条还是个小公司,还在为招募人才发愁。这篇文章对
  • 薪酬最高的编程语言居然是?

  • 点击下方“逆锋起笔”,公众号回复 视频教程领取大佬们推荐的学习资料作者 | invozone译者 | 平川策划 | 凌敏要在软件开发领域有所发展,你需要用最新的编程技能武装自己。这就
  • 使用 Flutter 与 Firebase 制作 I/O 弹球游戏

  • 以下内容来自公众号code小生,关注每日干货及时送达文/ Very Good Ventures 团队,5 月 11 日发表于 Flutter 官方博客为了今年的 Google I/O 大会,Flutter 团队使用 Flutter 以
  • Android 最详细的图片压缩攻略(建议收藏)

  • 以下内容来自公众号code小生,关注每日干货及时送达作者:Mr.Louis https://blog.csdn.net/weixin_44005563前言最近在研究图片压缩原理,看了大量资料,从上层尺寸压缩、质量压缩原
  • 微软正式宣布 Visual Studio 2022

  • ↓推荐关注↓转自:Rwingcnblogs.com/Rwing/p/visual-studio-2022.html首先,我们要感谢正在阅读这篇文章的你,我们所有的产品开发都始于你也止于你,无论你是在开发者社区上发帖,还