前言
这是一套 张风捷特烈 出品的 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 游戏 - 贰贰】菜单、字体和浮层
- 【Flutter&Flame 游戏 - 贰叁】 资源管理与国际化
- 【Flutter&Flame 游戏 - 贰肆】pinball 源码分析 - 项目结构介绍
- 【Flutter&Flame 游戏 - 贰伍】pinball 源码分析 - 资源加载与 Loading(
本文
) 未完待续
~
1. 加载界面在哪里
pinball
在游戏开始时,会显示资源加载的界面,是一个加载的进度条,如下所示。那问题来了,如何定位这个界面在源码中的位置。这样才能有机会分析资源加载的代码:
从一些外在表征去定位源码,是一个非常有用手段,比如在资源文件中可以看出 loading_game
的文件夹,其中包含着 io_pinball.png
的图片。这就说明只要找到什么地方使用了 io_pinball.png
,就可以发现相关视图处理的代码逻辑
全局搜索一下,就不难发现,该图片名称在 lib/gen/assets.gen.dart
中被使用:
点进去可以看到该文件是通过工具自动生成的资源管理代码,ioPinball
代表这个图片资源:
然后 顺藤摸瓜
,就可以找到图片资源使用的场景,这就像根据线索来探查真相。现在知道一开始加载的界面的代码在 lib/assets_manager/views/assets_loading_page.dart
中。这样我们就能通过源码来分析一下界面实现的逻辑,包括界面如何布局,进度如何变化等。
2. 加载界面 - 背景与图片
加载中的布局主要右四个部分组成,分别是 背景
、图片
、Loading
文字以及 进度条
:
在上面可以看出,图片本身背景是透明的,所以背景中的横线条纹在源码中一定有其出处:从界面组件 AssetsLoadingPage
的实现中可以看出,背景是通过 CrtBackground
装饰进行绘制的。其实这里的 Container
没有使用其他属性完全可以换成 DecoratedBox
组件,更加轻便:
这里的选择是自定义子类集成自 BoxDecoration
,感觉并没有太大的必要性。直接使用 BoxDecoration
指定 gradient
参数就行了,不过也无伤大雅。
class CrtBackground extends BoxDecoration {
/// {@macro crt_background}
const CrtBackground()
: super(
gradient: const LinearGradient(
begin: Alignment(1, 0.015),
stops: [0.0, 0.5, 0.5, 1],
colors: [
PinballColors.darkBlue,
PinballColors.darkBlue,
PinballColors.crtBackground,
PinballColors.crtBackground,
],
tileMode: TileMode.repeated,
),
);
}
复制代码
这里的图片组件是通过 ioPinball
对象调用 image()
方法获取的,其实这就是自动生成的代码给的一个形式语法糖。
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Assets.images.loadingGame.ioPinball.image(),
),
复制代码
ioPinball
类型为 AssetGenImage
,是一个 ImageProvider
,也就是 Image
组件在需要传入的 image
参数类型。该类中的 image
方法,提供了构造 Image
所需的可选属性,返回 Image
组件。并将自身作为构造 Image
组件的 image
入参:
也就是说,下面的两种写法是等价的,只不过上面在的写法不需要嵌入在 Image
组件下而已。本质上没有什么区别,只是简化书写形式而已:
Assets.images.loadingGame.ioPinball.image()
等价于
Image(image:Assets.images.loadingGame.ioPinball)
复制代码
3. 加载界面 - 加载中文字与指示器
如下所示: Loading
文字三个点会依次出现,是个循环动画。另外加载进度通过下面的指示器来显示,整个加载中界面的 业务逻辑
只有一个: 加载进度值的计算。
Loading...
的循环动画是通过 AnimatedEllipsisText
组件实现的,这个组件感觉挺实用。如果以后有需要,可以直接拷贝过去用,这就是 Flutter
组件化的好处。
简单瞄一眼源码,这里 ...
不断运动的动画,是通过 Timer.periodic
周期触发定时器实现的,每 500 ms
触发一次更新。由于这里是单独抽离的 AnimatedEllipsisText
,所以 setState
也只是局部的组件更新,不会影响触发外界组件的重新构建。
最后,是加载页最核心的业务逻辑,该项目是通过 flutter_bloc
来进行状态管理的。这里使用 AssetsManagerCubit
来维护加载资源的逻辑,其中状态数据是 AssetsManagerState
,该状态量可以获取加载的进度。这里通过 BlocBuilder
来监听状态的变化来构建组件。
从代码中可以看出,这个像素风格的进度条,通过 PinballLoadingIndicator
组件进行显示。构造中传入进度值,红色的区域就会占据相应的百分比。
从 PinballLoadingIndicator
组件的源码实现中可以看出,这个像素风格的进度条是通过六个 _InnerIndicator
组件进行显示的。仔细数一下上图,就会发现整体是由六个细条,从上到下排列的而构成的。
4. 资源加载的业务逻辑
上面我们知道,资源加载的核心逻辑以及过程中的进度状态数据,是由 AssetsManagerCubit
进行维护的。如下,在 lib/assets_manager
文件夹中管理着资源加载的 bloc
业务逻辑和 views
视图:
下面我们就进入 AssetsManagerCubit
,来看一下资源是如何加载的,以及进度状态的产出。AssetsManagerCubit
构造时需要传入如下两个对象,其中只有一个 load
异步方法,本身还是比较简单的。
这个 load
方法,会在 AssetsManagerCubit
构造时被立刻触发:
load
方法在有一个小细节,一开始延迟了一秒钟才开始真正加载,这是因为加载是个昂贵的操作,先给出 1s
的时间,让 UI
先展示出来,然后再真正进行加载资源。这里加载资源的异步任务
通过 loadables
列表进行维护:
异步操加载资源的任务,被定义在个个模块中。比如 _game.preLoadAssets()
方法,会返回所有构件图片资源加载的异步方法,其他几个也是类似。当你看到源码的这么多资源加载的异步方法,就会明白为什么这个 load
会是昂贵的。
然后通过 _triggerLoad
局部函数对象,分三波依次触发这些异步任务。每次异步任务完成时,都会产出新的状态,让已加载的资源数加一。
这样状态数据中的进度值 progress
就会变化,整个加载的小体系就得以运转,从业务逻辑到视图更新展示,可以体会一下,bloc
在其中的角色,品味一下状态管理的价值。
到这里,pinball
首次进入时资源加载,以及进度的显示流程就介绍完毕了。那本文就到这里,明天见 ~