【Flutter&Flame游戏 - 拾】探索构件 | Component 生命周期回调


theme: cyanosis

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


前言

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

第一季完结,谢谢支持 ~


1. Component 生命周期回调一览

所谓生命周期,就是一个对象从生到死的过程。在上一篇中介绍过 Component 的生命周期状态 (LifecycleState) 有如下六种。可能很多人分不清什么是生命周期,什么是生命周期回调。

生命周期,本质上是一种 状态 ,也就是说它是一种数据;而生命周期回调是一个函数,或说方法,一般来说该函数会在状态切换时触发,从而让外界可以感知到对象的状态变化,以此实现某些特定的逻辑。 Component 中的生命周期回调方法如下:

一般来说,常用的是如下六个回调,先简单认识一下:

  • onGameResize : 顶层画布尺寸变化时
  • onLoad:资源加载时
  • onMount:添加到父节点时
  • onRemove:从父节点移除时
  • update:跟随 Ticker 不断触发
  • render:新帧渲染时触发

2. onGameResize 和 onLoad

如下可以看出,在生命周期状态从 uninitialized 切换到 loading 时,会触发一次 onGameResize;紧接着触发 onLoad 异步方法。在 483 行所示,异步任完成后,生命周期状态将置为 loaded


如下通过断点查看一下自定义的 Ball 组件 onLoad 方法触发时,方法栈的情况。可见在 Flutter 程序的开始,BuildOwner#buildScope 构建组件时, _GameWidgetState 会触发 loaderFuture 的方法。在父构件执行 add 方法,会先触发该子构建的 onLoad 方法来加载资源。可就是说,通过这个回调,可以给构件准备资源的机会。


3. onMount 和 onRemove

这两个是一对反义词,onMount 方法在生命周期状态变为 mounted 之前触发。让使用者知道该构件节点添加到构件树的确切时机。


当某个组件被父节点踢出群聊时,会触发onRemove 方法,之后紧接着将生命周期状态置为 removed 。让使用者知道该构件节点添加到构件树的确切时机。


4. update 和 render

前面我们对这两个方法已经有所了解,这两者都是一个持续不断的回调,一般每隔 16.66ms 触发一次,也就是一秒钟触发 60 次 。update 方法本质上由 Ticker 触发,这点可以通过断点调试进行应证,如下所示:


render 方法本质上是在帧绘制期间被触发的,也就是 RendererBinding.drawFrame 方法。这个看过 《Flutter 渲染机制 - 聚沙成塔》 的朋友对这些应该比较熟悉,没看过也没有关系。 Ticker 触发新帧的申请,回调 update 方法,在新帧来临是触发 drawFrame 方法,回调 render 方法,所以这两者的先后关系是很明确的。


如下是着六个回调方法顺序的简单示意,其中 updaterender 方法是在 Ticker 循环中不断触发的,当 Ticker 停止时,这两个方法也会停止回调。另外当该组件被移除之后,也不会继续回调updaterender


5. 运动圆

下面通过一个小案例来梳理一下 Component 的生命周期回调。如下,小圆不停运动,在碰到桌面后反弹,代码详见 【10/01】


onLoad 方法中,可以对画笔、位置、速度、加速度等属性进行初始化:

```dart final Paint _paint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = 1;

Vector2 v = Vector2.zero(); // 速度 px/s Vector2 a = Vector2.zero(); // 加速度 px/s^2

@override Future onLoad() async { _paint.color = color; position = gameRef.size / 2; v = Vector2(80, 50); } ```


render 方法中进行绘制圆:

dart @override void render(Canvas canvas) { super.render(canvas); canvas.translate(size.x / 2, size.y / 2); canvas.drawCircle(Offset.zero, size.x / 2, _paint); }


update 中,根据运动学格式,在 dt 的时间内,更新速度和位移的值,小球即可运动。另外小球的碰壁反弹可以通过位置校验来处理 ,Flame 中有对于碰撞的简单封装,但这里还是自己手动校验,体会一下简单的配置检测。

速度的合成.png

碰撞分析png

dart @override void update(double dt) { super.update(dt); v += a * dt; position += v * dt; Vector2 winSize = gameRef.size; //限定下边界 if (position.y > winSize.y - size.y/2) { position.y = winSize.y - size.y/2; v.y = -v.y; } //限定上边界 if (position.y < size.y/2) { position.y = size.y/2; v.y = -v.y; } //限定左边界 if (position.x < size.x/2) { position.x = size.x/2; v.x = -v.x; } //限定右边界 if (position.x > winSize.x - size.x/2) { position.x = winSize.x - size.x/2; v.x = -v.x; } }


下面可以继续拓展,比如在点击屏幕时添加通过 Ball ,双击屏幕时移除 Ball 列表的第一个。效果如下,代码详见 【10/02】

```dart class TolyGame extends FlameGame with TapDetector,DoubleTapDetector{

int _counter = 0;

@override Future onLoad() async { addABall(); }

void addABall(){ Ball ball = Ball(tag: 'tag$_counter'); add(ball); _counter++; }

@override void onTap() { addABall(); }

@override void onDoubleTap() { List balls = children.whereType ().toList(); if(balls.isNotEmpty){ balls.first.removeFromParent(); } } } ```


这样在移除时 Ball 自身可以通过 onRemove 监听到事件:


到这里,我们就对 FlameComponent 的生命周期回调有了较深的理解。这个知识点是非常重要的,希望大家可以好好消化吸收。那本文就到这里,明天见 ~

\

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值