ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Flame 游戏引擎 SequenceEffect 序列效果详解:依次编排多种效果、正反交替与无限循环

Flame 游戏引擎 SequenceEffect 序列效果详解:依次编排多种效果、正反交替与无限循环 Flame 游戏引擎 SequenceEffect 序列效果详解依次编排多种效果、正反交替与无限循环【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flame本指南围绕 Flame 游戏引擎内置的SequenceEffect展开讲解如何把多个不同类型的效果移动、旋转、缩放、透明度、移除等编排成一条依次执行的动画序列并支持正反交替alternate、限定次数重复repeatCount与无限循环infinite。读完本文你将掌握SequenceEffect的完整构造参数语义、与效果控制器EffectController的关系、底层执行机制以及如何用它编写可复用的组合动画。什么是 SequenceEffectSequenceEffect是 Flame 提供的一种组合效果composite effect它的作用是把多个子效果串成一条链让它们一个接一个地顺序执行。与CombinedEffect同时并行执行多个效果不同序列中的子效果可以拥有完全不同的类型例如先缩放、再移动、再淡出、最后移除组件。其核心特性包括任意类型子效果混排构成序列的效果可以是ScaleEffect、MoveEffect、OpacityEffect、RotateEffect、RemoveEffect等任何内置或自定义效果正反交替通过alternate: true让序列先正向执行再反向执行重复执行通过repeatCount限定重复次数或通过infinite: true无限循环自动管理子效果会作为SequenceEffect的子组件被挂载但同一时刻只会有一个子效果在运行执行完毕后效果会自动清理。在 Flame 的效果体系中SequenceEffect位于 doc/flame/effects/effects.md 所介绍的内置效果列表之中是构建组合动作连招、关卡演出、UI 动效的常用工具。基本用法把不同效果串成一条序列下面的示例来自 sequence_effect.md 的官方演示它把 4 个不同类型的效果串联在一起先放大 1.5 倍且正反交替弹动一次再位移(30, -50)接着透明度降到 0 完成淡出最后通过RemoveEffect把组件从游戏树中移除final effect SequenceEffect([ ScaleEffect.by( Vector2.all(1.5), EffectController( duration: 0.2, alternate: true, ), ), MoveEffect.by( Vector2(30, -50), EffectController( duration: 0.5, ), ), OpacityEffect.to( 0, EffectController( duration: 0.3, ), ), RemoveEffect(), ]);使用方法与其他效果一致——把它附加到目标组件上即可component.add(effect);执行流程非常直观第 00.2 秒组件从当前尺寸放大到 1.5 倍再在 0.2 秒内缩放回原尺寸alternate: true使这段缩放去而复返第 0.40.9 秒组件向右移动 30、向上移动 50第 0.91.2 秒组件透明度从当前值渐变为 0序列结束后组件从游戏树中被移除。注意RemoveEffect在这里没有使用delay参数因此它排在序列末尾、在其他子效果全部完成后立即触发。若希望延迟移除可以按 doc/flame/effects/remove_effect.md 中的说明传入RemoveEffect(delay: 3.0)之类的延时值。构造参数详解SequenceEffect的构造函数定义在 sequence_effect.dart 中SequenceEffect( ListEffect effects, { bool alternate false, bool infinite false, int repeatCount 1, super.onComplete, super.key, })各参数语义如下参数类型默认值说明effectsListEffect必填按顺序执行的子效果列表不能为空构造时断言。列表中的元素可以是任意类型的Effectalternateboolfalse为true时序列正向执行完后会再按相反顺序执行一遍infiniteboolfalse为true时序列无限循环永不结束等价于repeatCount为无穷大repeatCountint1序列重复执行的次数仅当alternate为true时一次往返正 反才算一个周期onCompleteVoidCallback?null序列整体执行完毕后触发的回调keyKey?null组件键用于 Widget 树复用等场景构造函数内部还有两条重要的合法性断言assertassert(effects.isNotEmpty, The list of effects cannot be empty), assert( !(infinite repeatCount ! 1), Parameters infinite and repeatCount cannot be specified simultaneously, ),也就是说空序列会直接断言失败infinite: true与repeatCount ! 1不能同时指定。若两者同时出现infinite优先源码注释明确说明infinite true等价于repeatCount为无穷大。这些行为在 sequence_effect_test.dart 中有对应的测试用例验证。交替、重复与无限循环的组合语义alternate、repeatCount、infinite三个参数组合起来可以覆盖绝大多数动画编排需求其精确语义在源码注释sequence_effect.dart中有详细定义仅alternate: true序列先正向执行到达末尾后按相反顺序执行回来即正 → 反各一遍然后结束。例如[A, B, C]的执行顺序为A → B → C → B → A仅repeatCount: n整条序列正向若还设置了alternate则为一次往返重复执行n次alternate: true与repeatCount: n同时使用序列先正后反这样的往返整体重复n次infinite: true上述过程无限循环序列永远不会标记为完成。从实现角度看_createController会按需用InfiniteEffectController或RepeatedEffectController定义于 infinite_effect_controller.dart 和 repeated_effect_controller.dart包装内部的序列控制器实现重复/无限语义EffectController ec _SequenceEffectEffectController(effects, alternate: alternate); if (infinite) { ec InfiniteEffectController(ec); } else if (repeatCount 1) { ec RepeatedEffectController(ec, repeatCount); }同时注意 sequence_effect.dart 中的一行关键代码effects.forEach((e) e.removeOnFinish false);即子效果的removeOnFinish与paused标志会被忽略防止子效果在自身完成时提前把自己从游戏树中移除从而破坏序列的连续性。子组件的清理完全由SequenceEffect统一管理。为什么 SequenceEffect 不接受 EffectControllerFlame 中绝大多数效果如MoveEffect、ScaleEffect都要求传入一个EffectController来描述进度随时间如何变化但SequenceEffect是例外——它的构造函数里没有EffectController参数。原因在源码注释sequence_effect.dart中解释得很清楚与其它效果不同SequenceEffect不接受EffectController作为参数。因为序列效果的时序取决于其中每个子效果各自的时序无法用一个普通的效果控制器来表示。也就是说序列的总时间轴是由各子效果自己的EffectController各自的时长、曲线、交替、随机等拼接出来的SequenceEffect本身只负责调度不参与具体的插值计算。这也意味着每个子效果仍可独立使用EffectController的丰富能力例如alternate、curve、repeatCount、startDelay等参数细节见 doc/flame/effects/effect_controllers.md序列的总时长等于所有子效果时长之和若alternate: true总时长再翻倍可在_SequenceEffectEffectController.duration的实现中看到。源码级原理单效果串行调度机制SequenceEffect在底层做了两件关键的事子组件挂载与串行调度。1. 子效果作为子组件挂载。构造函数中调用addAll(effects)把所有子效果作为自己的子组件添加同时SequenceEffect混入了CustomTraversal并重写updateSubtreesequence_effect.dartoverride void updateSubtree(double dt) { update(dt); // Do not update children: the controller will take care of it }注释明确指出不更新子组件——子效果的推进完全交由内部的序列控制器负责。这就保证了同一时刻只有一个子效果在运行。2. 内部控制器维护当前索引。实际的调度逻辑在私有类_SequenceEffectEffectControllersequence_effect.dart中。它用一个整数_index记录当前正在执行的子效果正向执行时_index取0, 1, ..., n-1若开启alternate序列到达末尾后_index变为-1, -2, ..., -n其中-1表示最后一个效果、-n表示第一个效果从而以负数索引表达反向执行currentEffect通过effects[_index 0 ? _index n : _index]解析出真正的子效果。advance(dt)方法把剩余时间逐级传递给当前子效果子效果消耗掉一部分dt后若还有剩余时间则推进到下一个子效果继续消耗sequence_effect.dart。同样地recede(dt)实现了反向回退保证在alternate、暂停/恢复等场景下时间轴可以正确地倒着走。3. 时长与进度计算。duration累加所有子效果时长sequence_effect.dartvar totalDuration 0.0; for (final effect in effects) { totalDuration effect.controller.duration ?? 0; } if (alternate) { totalDuration * 2; }需要留意的是源码注释也承认_SequenceEffectEffectController并不严格遵循标准EffectController接口它的progress返回(_index 1) / n定义并不严谨但由于SequenceEffect.apply()是空实现该值在实际渲染中不会被使用不影响功能。澄清SequenceEffect 与 SequenceEffectController 的区别Flame 中还有另一个名字相近的类SequenceEffectController定义于 sequence_effect_controller.dart二者容易混淆但层次完全不同SequenceEffect一个Effect用于把**多个效果Effect**串行编排是本文的主角SequenceEffectController一个EffectController用于把**多个效果控制器EffectController**串行编排供单个效果内部使用。SequenceEffectController的用法如下来自 doc/flame/effects/effect_controllers.mdfinal controller SequenceEffectController([ LinearEffectController(1), PauseEffectController(0.2), ReverseLinearEffectController(1), ]);即它编排的是时间曲线片段而不是动画动作。SequenceEffect内部使用的_SequenceEffectEffectController与公开的SequenceEffectController是两套独立实现前者针对Effect列表做了调度优化如removeOnFinish的忽略处理。完整实战示例交替 无限循环的移动路径仓库中的交互式示例 sequence_effect_example.dart 给出了一个完整的实战用法一个玩家组件沿折线路径移动途中穿插旋转与缩放并且整条序列正反交替、无限循环地执行class SequenceEffectExample extends FlameGame { override Futurevoid onLoad() async { EffectController duration(double x) EffectController(duration: x); add( Player() ..position Vector2(200, 300) ..add( SequenceEffect( [ MoveEffect.to(Vector2(400, 300), duration(0.7)), RotateEffect.by(tau / 4, duration(0.5)), MoveEffect.to(Vector2(400, 400), duration(0.7)), ScaleEffect.by(Vector2.all(1.5), duration(0.7)), MoveEffect.to(Vector2(400, 500), duration(0.7)), ], alternate: true, infinite: true, ), ), ); } }这段代码展示了几个要点序列中混排了MoveEffect.to、RotateEffect.by、ScaleEffect.by三种效果各自拥有独立的时长alternate: true让玩家在走完整条路径后沿原路倒序返回infinite: true让前进 → 返回循环往复形成持续的巡逻/漂浮动画所有子效果通过链式..add(...)附加到Player组件上无需手动管理生命周期。该示例运行效果即 sequence_effect.md 顶部内嵌的flutter-app演示页面指向examples下的sequence_effect故事。若想运行可进入仓库的 examples 目录按其中说明启动示例工程查看。测试验证行为由测试保证SequenceEffect的关键行为在 sequence_effect_test.dart 中有完整测试覆盖主要包括简单序列两个MoveEffect序列的总时长为两者之和如3 0.1 × 15 4.5秒isRandom为false交替序列alternate: true时单效果的序列时长翻倍3 → 6秒无限循环infinite: true时duration返回无穷大double.infinity随机子效果当子效果使用RandomEffectController时序列的isRandom返回true总时长随随机结果变化错误断言空列表抛出 The list of effects cannot be emptyinfinite与repeatCount同时指定抛出对应断言。这些测试同时印证了前文所述的总时长计算、交替翻倍与参数互斥规则可作为自定义组合效果时的参考范式。相关资源Effects 总览Flame 全部内置效果与自建效果的指南包含SequenceEffect在效果体系中的定位Effect ControllersEffectController工厂构造函数及全部内置控制器的参数说明Move Effects / Scale Effects / Rotate Effects / Color Effects序列中常用子效果的详细用法Combined Effect与SequenceEffect互补的并行组合效果适合多个效果同时作用于同一组件的场景Remove Effect序列收尾常用的组件移除效果sequence_effect.dartSequenceEffect核心实现源码sequence_effect_example.dart可运行的综合示例sequence_effect_test.dart行为测试用例。【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flame创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表