【Flutter&Flame 游戏 - 贰贰】菜单、字体和浮层

2022-06-19 16:55:43 浏览数 (1)

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 23 天,点击查看活动详情


前言

这是一套 张风捷特烈 出品的 Flutter&Flame 系列教程,发布于掘金社区。如果你在其他平台看到本文,可以根据对于链接移步到掘金中查看。因为文章可能会更新、修正,一切以掘金文章版本为准。本系列源码于 【toly_game】 ,如果本系列对你有所帮助,希望点赞支持,本系列文章一览:

  • 【Flutter&Flame 游戏 - 壹】开启新世界的大门
  • 【Flutter&Flame 游戏 - 贰】操纵杆与角色移动
  • 【Flutter&Flame 游戏 - 叁】键盘事件与手势操作
  • 【Flutter&Flame 游戏 - 肆】精灵图片加载方式
  • 【Flutter&Flame 游戏 - 伍】Canvas 参上 | 角色的血条
  • 【Flutter&Flame 游戏 - 陆】暴击 Dash | 文字构件的使用
  • 【Flutter&Flame 游戏 - 柒】人随指动 | 动画点触与移动
  • 【Flutter&Flame 游戏 - 捌】装弹完毕 | 角色武器发射
  • 【Flutter&Flame 游戏 - 玖】探索构件 | Component 是什么
  • 【Flutter&Flame 游戏 - 拾】探索构件 | Component 生命周期回调
  • 【Flutter&Flame 游戏 - 拾壹】探索构件 | Component 使用细节
  • 【Flutter&Flame 游戏 - 拾贰】探索构件 | 角色管理
  • 【Flutter&Flame 游戏 - 拾叁】碰撞检测 | CollisionCallbacks
  • 【Flutter&Flame 游戏 - 拾肆】碰撞检测 | 之前代码优化
  • 【Flutter&Flame 游戏 - 拾伍】粒子系统 | ParticleSystemComponent
  • 【Flutter&Flame 游戏 - 拾陆】粒子系统 | 粒子的种类
  • 【Flutter&Flame 游戏 - 拾柒】构件特效 | 了解 Effect 体系
  • 【Flutter&Flame 游戏 - 拾捌】构件特效 | ComponentEffect 一族
  • 【Flutter&Flame 游戏 - 拾玖】构件特效 | 了解 EffectController 体系
  • 【Flutter&Flame 游戏 - 贰拾】构件特效 | 其他 EffectControler
  • 【Flutter&Flame 游戏 - 贰壹】视差组件 | ParallaxComponent
  • 【Flutter&Flame 游戏 - 贰贰】菜单、字体和浮层
  • 未完待续 ~

1.在游戏中添加菜单组件

一般来说,休闲游戏并不会打开时立即进入游戏。会有一个菜单界面,让用户选择开始游戏,或通过设置按钮来打开配置界面,对游戏进行设置。而我们知道,Flame“世界” 是通过 Ticker 不断触发更新的,但往往菜单是 静态 的,不需要一直更新。所以可以使用 Flutter 原生的组件来做菜单,再加上界面跳转也需要原生的路由。

其实本质上来说,Flame 所呈现的游戏界面也只是一个 Widget 而已,我们可以一视同仁。比如下面定义两个 GameWorld 组件,来表示游戏世界: 【22/01】

代码语言:javascript复制
class GameWorld extends StatelessWidget {
  const GameWorld({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GameWidget(game: TolyGame());
  }
}
复制代码

由于需要界面路由跳转,所以这里使用 MaterialApp ,其内部集成了路由体系。并且这里使用 navigatorKey ,便于在无上下文的情况下,获取导航状态。

代码语言:javascript复制
class GameApp extends StatelessWidget {
  const GameApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return  MaterialApp(
      debugShowCheckedModeBanner: false,
      navigatorKey: Keys.navKey,
      themeMode: ThemeMode.dark,
      darkTheme: ThemeData(brightness: Brightness.dark),
      home: const MainMenu(),
    );
  }
}

class Keys {
  Keys._();
  static GlobalKey<NavigatorState> navKey = GlobalKey(debugLabel: 'navKey');

  static NavigatorState? get navigator => navKey.currentState;
}
复制代码

比如现在先给个简单的菜单界面,如下所示,一个名字文本,两个按钮:

如下所示,定义一个 Flutter 常规的 MainMenu 组件,对内容进行展示即可,代码如下。其中 开始 按钮通过 Keys 中的 navKey 获取导航栏状态,通过 pushReplacement 方法,跳转到 GameWorld 游戏界面,并将当前的 MainMenu 界面弹栈。

代码语言:javascript复制
class MainMenu extends StatelessWidget {
  const MainMenu({Key? key}) : super(key: key);

  final TextStyle shadowStyle = const TextStyle(
      fontSize: 30, 
      shadows: [Shadow(color: Colors.white,blurRadius: 10)]
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Wrap(
          spacing: 20,
          direction: Axis.vertical,
          crossAxisAlignment: WrapCrossAlignment.center,
          children: [
            Text('Adventurer', style: shadowStyle,),
            ElevatedButton(onPressed: _doPlay, child: const Text('开 始')),
            ElevatedButton(onPressed: _toOptions, child: const Text('设 置'))
          ],
        ),
      ),
    );
  }

  void _doPlay() {
    Keys.navigator?.pushReplacement(
      MaterialPageRoute(builder: (ctx) => const GameWorld()),
    );
  }

  void _toOptions() {}
}
复制代码

2. 字体下载

有人觉得默认字体可能并不是很好看,想要引入别的字体,但很多字体不可以商用。其实google_fonts 中提供了大量可以商用的字体,我们可以在 fonts.google.com/ 中进行挑选。


在某个字体的 License 中,可以瞄一眼,比如 Ma Shan Zheng 是允许在- 项目-印刷或数字,商业或其他场景使用的。


点击下载,在 OFL 中也可以看到,字体证书是 STL ,允许商用:


你可以通过 线上本地 两种方式来加载字体。线上加载,可以使用 google_fonts 的字体库,所有的字体样式都可以通过 GoogleFonts 类通过静态方法获取,使用时会自动下载字体。

线上的缺点是必须依赖网络,而且需要下载时间,对于很大的字体,首次下载时间比较长,突然的字体改变,体验并不是很好。可以把字体下载到本地,这样就没有延迟的风险,而且在没有网络的情况下也能使用,缺点是会增加应用体积,大家可以酌情选择。本地字体使用也非常方便,只需要引入,在 pubspec.yamlfonts 节点下引入即可:

如果想要指定全局字体,可以在主题数据 ThemeData ,指定对应的 fontFamily

代码语言:javascript复制
class GameApp extends StatelessWidget {
  const GameApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return  MaterialApp(
      debugShowCheckedModeBanner: false,
      themeMode: ThemeMode.dark,
      darkTheme: ThemeData(
        brightness: Brightness.dark,
        fontFamily: 'ZCOOLKuaiLe' //<--- 指定字体
      ),
      home: const MainMenu(),
    );
  }
}
复制代码

这样就可以对应用的 Text 组件的字体进行统一设置,效果如下:


3. 游戏的暂停和恢复

我们知道 Falme 中通过 GameLoop 维护一个持续触发的 Ticker 用于游戏的渲染更新。当然,游戏中也需要要有暂停和恢复的方法,如下案例中,通过按下空格键来切换游戏状态:

https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bf40900125e6480ca6334a7e09089adf~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp?

Game 类中提供了 resumeEnginepauseEngine 两个方法,用于恢复和暂停游戏。此外 paused 属性可以得知游戏是否已经停止。由于 FlameGame 混入了 Game ,所以它有这些方法,如果在其他的构件中希望暂停或恢复游戏,可以通过混入 HasGameRef ,来得到 gameRef 对象触发这些方法。

代码语言:javascript复制
void toggleGameState(){
  if(paused){
    resumeEngine();
  }else{
    pauseEngine();
  }
}
复制代码

4. 在 Flame 中展示浮层

有时我们有显示浮层的需求,比如暂停游戏时,显示暂停面板。不然用户不小心碰到了暂停键,有可能不知所措,显示一个浮层界面可以更好的引导交互。如下所示,在点击空格键时,显示浮层:代码详见 【22/02】


使用浮层需要三步:

  • 1.创建浮层中的内容组件

这里和开始菜单类似,就不贴代码了,详见源码。在其中定义了 Game 成员,在构造方法中初始化,这是为了方便在 PauseMenu 的继续按钮触发时,调用引擎的相关方法,继续游戏。当然,你也可以把事件回调出去,让使用者处理,其实都差不多,酌情考量即可。

另外,定义了一个 menuId 的静态常量,为了方便标识这个菜单,而不是在每处使用时,都写一个死的字符串。


  • 2.通过 GameWidgetoverlayBuilderMap 参数指定 浮层id组件内容 的映射关系:


  • 3.通过 浮层id 开启或隐藏浮层,其中 overlaysGame 中的公开成员:


本文介绍了,如何在 Flame 游戏中,让 Flutter 原生的组件发挥价值。其实 Flame 是在 Flutter 中的,你可以随时随地,使用 Flutter 中的任何知识。并没有必要把 FlameFlutter 进行割裂,Flutter 的基础设施仍然可以使用,比如国际化、主题切换、状态管理等等。

  • @张风捷特烈 2022.06.17 未允禁转
  • 我的 掘金主页 : 张风捷特烈
  • 我的 B站主页 : 张风捷特烈
  • 我的 github 主页 : toly1994328

0 人点赞