持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 10 天,点击查看活动详情
前言
这是一套 张风捷特烈 出品的 Flutter&Flame
系列教程,发布于掘金社区。如果你在其他平台看到本文,可以根据对于链接移步到掘金中查看。因为文章可能会更新、修正,一切以掘金文章版本为准。本系列文章一览:
- 【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. Component 的树形结构
通过前面八篇的尝鲜,或说预热,我们可以感知到无论是主角、怪兽、文字、子弹、触点都是 Component
。它是游戏的基本构建模块,可以表示任何需要被渲染、更新的内容。
下面是 Component
类的部分结构,可以看出 Component
是一个普通类。其本身会持有父级构件,以及子级构件集合。也就是说 Component
本身是一个树形结构的节点类,认识到这一点至关重要。
正是由于树形结构的特点,Component
类有添加
和 移除
构件的能力。如下所示,可以通过 add
方法添加子级构件,也可以通过 addToParent
方法,将自身添加到父级构件中。
前面介绍过子弹、怪物消失,使用的是 removeFromParent
方法。如下源码中可以看出,是调用父级构件 _parent
的移除方法,把当前构件对象从父级节点上移除:
---->[Component#removeFromParent]----
/// Remove the component from its parent in the next tick.
void removeFromParent() {
_parent?.remove(this);
}
复制代码
2. Component 生命周期状态
Component
中有一个 _state
属性,其类型为 LifecycleState
枚举,用于表示构件的状态:
其中有如下 6
种状态,初始状态是 uninitialized
,表示未初始化
,也就是构件实例化时的默认状态。前面知道构件中有个 onLoad
的异步方法用于加载资源,在执行异步方法的前一刻就是 loading
状态。该状态会持续到异步方法执行完毕,变成 loaded
状态。
enum LifecycleState {
uninitialized,
loading,
loaded,
mounted,
removing,
removed,
}
复制代码
Component
是树形结构的节点,当某个 Component
添加到父节点上后,就会变成 mounted
状态。相关代码如下所示:
另外当父级执行 remove
方法时,入参的子构件非 removing
状态时,会被加入到 lifecycle._removals
列表中,等待下帧触发时移除。此时该子构件的状态为 removing
。当构件被从父节点上移除后,其状态为 removed
,就变成了孤魂野鬼,等待被 GC
回收。
如下图是六种状态的转换示意图,其实还是比较清晰的。了解这六种状态,在下篇介绍 Component
生命周期方法时,就会更好理解。
另外 Component
中关于生命周期状态有三个 get
方法,这里介绍一下:
- isLoaded:非
uninitialized
且非loading
状态,表示异步加载任务是否已经完成。 - isMounted:
mounted
或removing
状态,表示构件依然在树上。 - shouldRemove:
removing
状态,表示构件已被收集到移除列表中,将在下一帧中被移除。
---->[Component]----
bool get isLoaded {
return (_state != LifecycleState.uninitialized) &&
(_state != LifecycleState.loading);
}
bool get isMounted {
return (_state == LifecycleState.mounted) ||
(_state == LifecycleState.removing);
}
bool get shouldRemove => _state == LifecycleState.removing;
复制代码
3. Component 的衍生类
在 Flame
的 components
包中的文件,是对 Component
的衍生。其中一些 mixin
,比如 Draggable
、Hoverable
、Tappable
等都是依赖于 Component
,情理上来说也算是 Component
的衍生产物。
Component
大致可分为三大类,支持定位和变换的 PositionComponent
、附加效果的 Effect
、以及直接继承自 Component
的少数构建。
其中群体最庞大的是 PositionComponent
,这一族引入了 尺寸
、锚点
、位置
、旋转
、缩放
等属性,决定了该族构件将非常实用:
我们之前用的 SpriteComponent
、TextComponent
、SpriteAnimationComponent
等都是 PositionComponent
一族的。另外,自定义的子弹、主角、怪兽,也都是 PositionComponent
。
另外,Effect
一族定义在 effects
包中,我们在前面用到的 MoveEffect
就是这类的构件。在之后的学习中我们再深入认识其他的效果,或者自定义 Effects
。
4. 自定义 Component
前面的案例中我们也尝试过自定义 Component
,比如 Adventurer
、Monster
、TouchIndicator
等。其实自定义 Component
和 Flutter
中自定义 Widget
的功效类似,都是为了把一些通用的构成逻辑进行封装,以便复用和管理。
比如通过下面的 Monster
类,可以生成多个怪兽对象:可以定义不同的帧序列和生命值,它们对于玩家来说就是两个不同的怪兽。对于编程者而言它们都是通过 Monster
构建类实例化的对象,本质没有什么区别。代码详见 【09/01】
https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/543581e81c244a9e9166f019da6665ab~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp?
代码语言:javascript复制class Monster extends SpriteAnimationComponent with Liveable {
final double life;
Monster({
required SpriteAnimation animation,
required Vector2 size,
required Vector2 position,
this.life = 4000,
}) : super(
animation: animation,
size: size,
position: position,
anchor: Anchor.center,
);
@override
void onDied() {
removeFromParent();
}
@override
Future<void> onLoad() async {
initPaint(lifePoint: life);
}
void move(Vector2 ds) {
position.add(ds);
}
}
复制代码
另外,通过自定义构件类,可以覆写 Component
的相关回调方法,监听相关状态,处理逻辑。这里先对 Component
认识到这里,下一章我们将信息探讨一下 Component
的生命周期回调。那时你就会对 Component
有一个更深的认知,那么本文就到这里,明天见 ~
@张风捷特烈 2022.06.03 未允禁转
我的 掘金主页
: 张风捷特烈我的 B站主页
: 张风捷特烈我的 github 主页
: toly1994328