flame_rive 桥接库实战:在 Flame 游戏中集成 Rive 实时交互动画 📅 发布时间:2026/9/15 12:41:58 👁 浏览次数: flame_rive 桥接库实战在 Flame 游戏中集成 Rive 实时交互动画【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flameflame_rive是 Flame 官方提供的桥接库它把 Rive 为主体结合 RiveComponent 源码 与 官方示例完整讲解从加载.riv文件、创建 Artboard、绑定 StateMachine到通过 Data Binding 实现交互的全部流程读完后你可以在自己的 Flame 游戏中直接落地 Rive 动画。1. flame_rive 是什么flame_rive是用于在 Flame 游戏中使用 Rive 动画的桥接库。Rive 是一个实时交互式设计与动画工具用它创建的.riv文件包含矢量图形、动画与状态机运行时数据可以动态绑定Data Binding非常适合游戏内奖励界面、角色动画、UI 动效等场景。从 pubspec.yaml 可以看出它的定位与依赖关系包版本1.11.2依赖flame: ^1.38.0与rive: ^0.14.6描述明确写道Rive support for the Flame game engine. This uses the rive package and provides wrappers and components to be used inside Flame.也就是说flame_rive本身不实现 Rive 运行时而是把 Rive 官方的rive包封装成 Flame 组件模型PositionComponent的子类从而获得 Flame 的组件树、布局、事件与生命周期管理能力。在库的入口 lib/flame_rive.dart 中可以看到其导出策略export package:rive/rive.dart hide RiveSemanticsMixin, RiveSemanticsWidget; export src/rive_component.dart;它重新导出了rive包的全部 API仅隐藏两个语义相关的 Widget 混入并导出src/rive_component.dart。因此使用flame_rive时File.asset、Artboard、StateMachine、ViewModel等类型都直接可用无需再单独引入rive包。2. 安装与资源准备2.1 添加依赖在游戏项目的pubspec.yaml中添加依赖参考 示例项目 pubspec.yamldependencies: flame: ^1.38.0 flame_rive: ^1.11.2 flutter: sdk: flutter rive: ^0.14.0 flutter: uses-material-design: true assets: - assets/需要注意两点flame_rive依赖 SDK3.12.0 4.0.0、Flutter3.44.0请确认你的开发环境满足版本要求必须在flutter:段声明资源目录将.riv文件所在的assets/目录或具体文件注册进去否则运行时无法通过File.asset读取。2.2 放置动画文件将 Rive 导出的动画文件放入assets文件夹例如示例中的 assets/rewards.riv 与 assets/skills.riv。一个.riv文件内部可以包含多个 Artboard、多个 State Machine 以及关联的 ViewModel 定义。3. 核心使用流程使用flame_rive的完整流程在 rive.md 中概括为三步把animation.riv文件加入 assets 文件夹使用loadArtboard方法把动画的 Artboard 加载进游戏从 Artboard 创建StateMachine把它传给RiveComponent——组件会自动推进advance状态机。官方文档给出的最小可运行示例class RiveExampleGame extends FlameGame { override Futurevoid onLoad() async { final file await File.asset( assets/rewards.riv, riveFactory: Factory.rive, ); final artboard await loadArtboard(file!); final stateMachine artboard.defaultStateMachine(); if (stateMachine ! null) { final viewModel file.defaultArtboardViewModel(artboard); if (viewModel ! null) { final viewModelInstance viewModel.createDefaultInstance(); if (viewModelInstance ! null) { stateMachine.bindViewModelInstance(viewModelInstance); final coinAmount viewModelInstance.viewModel(Coin)?.number(Item_Value); coinAmount?.value 100; } } } add( RiveComponent( artboard: artboard, stateMachine: stateMachine, size: Vector2.all(550), ), ); } }下面对每一步进行源码级拆解。3.1 加载 Rive 文件File.assetFile.asset来自rive包的加载器配合riveFactory指定后端渲染工厂官方文档示例使用Factory.rive示例工程 main.dart 中则使用Factory.flutterFlutter 渲染后端并在main()中先执行await RiveNative.init();再运行GameWidget.managed。void main() async { WidgetsFlutterBinding.ensureInitialized(); await RiveNative.init(); runApp(const GameWidget.managed(gameFactory: RiveExampleGame.new)); }如果你的 Rive 资源以原生native方式加载或用到 Rive 原生运行时能力建议按示例在启动时先await RiveNative.init()纯 Dart/Flutter 工厂路径Factory.flutter通常不需要该步骤。具体以你的运行平台与资源类型为准。3.2 加载 ArtboardloadArtboardloadArtboard是flame_rive提供的顶层函数签名与实现如下FutureArtboard loadArtboard( FutureOrFile file, { String? artboardName, }) async { final loaded await file; if (artboardName null) { return loaded.defaultArtboard()!; } else { final artboard loaded.artboard(artboardName); assert( artboard ! null, No artboard with the specified name exists in the RiveFile, ); return artboard!; } }要点不传artboardName时返回文件默认 Artboard传入名称时按名查找若不存在会触发断言assert失败提示No artboard with the specified name exists in the RiveFile该方法接受FutureOrFile因此可以直接把File.asset(...)的异步结果传进去。3.3 获取并绑定 StateMachineartboard.defaultStateMachine()获取默认状态机也可以像示例工程那样按名称获取final stateMachine artboard.stateMachine(State Machine 1);把状态机传给RiveComponent后组件会在每次update中自动调用stateMachine.advanceAndApply(dt)详见下文第 4 节无需手动推进。4. RiveComponent 源码剖析RiveComponent定义在 packages/flame_rive/lib/src/rive_component.dart继承自PositionComponent因此天然拥有 Flame 的位置、缩放、旋转、锚点、子组件与优先级等全部能力。4.1 构造参数RiveComponent({ required this.artboard, this.stateMachine, bool antialiasing true, BoxFit fit BoxFit.contain, this._alignment Alignment.center, this._clipToBounds false, super.position, Vector2? size, // 逻辑尺寸默认值为 Artboard 尺寸 super.scale, super.angle 0.0, super.anchor Anchor.topLeft, super.children, super.priority, super.key, })参数说明参数默认值作用artboard必填Rive Artboard承载全部绘制内容stateMachinenull绑定到 Artboard 的状态机不传则退化为纯动画推进antialiasingtrue是否开启抗锯齿关闭时会以isAntiAlias false的 Paint 创建离屏图层fitBoxFit.contain内容适配方式见 4.2 的_toRiveFit映射_alignmentAlignment.center对齐方式_clipToBoundsfalse是否裁剪到组件边界sizeArtboard 尺寸组件逻辑尺寸默认取Vector2(artboard.width, artboard.height)构造时会把传入的BoxFit转换为 Rive 的Fit枚举并给size添加监听器以同步渲染尺寸}) : _riveFit _toRiveFit(fit), _layerPaint antialiasing ? null : (Paint()..isAntiAlias false), super(size: size ?? Vector2(artboard.width, artboard.height)) { void updateRenderSize() { _renderSize this.size.toSize(); } this.size.addListener(updateRenderSize); updateRenderSize(); }4.2 渲染renderoverride void render(Canvas canvas) { canvas.save(); if (_clipToBounds) { canvas.clipRect(Offset.zero _renderSize); } if (_layerPaint ! null) { canvas.saveLayer(Offset.zero _renderSize, _layerPaint); } final renderer Renderer.make(canvas); try { if (_frameSize ! _renderSize) { _frameSize _renderSize; _frame AABB.fromValues(0, 0, _renderSize.width, _renderSize.height); } renderer.align( _riveFit, _alignment, _frame, artboard.bounds, 1.0, ); artboard.draw(renderer); } finally { renderer.dispose(); if (_layerPaint ! null) { canvas.restore(); } canvas.restore(); } }绘制过程保存画布 → 按需裁剪 → 按需创建离屏图层 → 用Renderer.make(canvas)生成 Rive 渲染器 → 依据Fit/对齐方式与帧尺寸做align→artboard.draw(renderer)完成绘制 → 最终在finally中释放渲染器并恢复画布状态保证异常时画布状态也能正确还原。4.3 更新updateoverride void update(double dt) { if (stateMachine ! null) { stateMachine!.advanceAndApply(dt); } else { artboard.advance(dt); } }这就是组件会自动推进状态机的实现传入stateMachine时调用advanceAndApply(dt)推进并应用状态机未传入时退化为artboard.advance(dt)仅推进基础动画。4.4 Fit 映射_toRiveFit把 Flutter 的BoxFit一一映射到 Rive 的Fitstatic Fit _toRiveFit(BoxFit fit) switch (fit) { BoxFit.fill Fit.fill, BoxFit.contain Fit.contain, BoxFit.cover Fit.cover, BoxFit.fitHeight Fit.fitHeight, BoxFit.fitWidth Fit.fitWidth, BoxFit.none Fit.none, BoxFit.scaleDown Fit.scaleDown, };选择BoxFit.contain会在组件内完整显示动画可能留边BoxFit.cover会填满组件可能裁剪BoxFit.fill则会拉伸变形按需求取舍。5. 用 Data Binding 实现交互官方文档特别强调交互性应通过 Data Binding 处理而不是 state machine inputs因为后者在 Rive 0.14.x 中已被弃用。Data Binding 的思路在 Rive 编辑器里为状态机绑定 ViewModel运行时创建 ViewModel 实例绑定到状态机然后通过读写实例上的 Number 等属性来驱动动画变化。结合 3 节的示例关键调用链为file.defaultArtboardViewModel(artboard)取文件默认 ViewModelviewModel.createDefaultInstance()创建实例stateMachine.bindViewModelInstance(viewModelInstance)绑定到状态机通过viewModelInstance.viewModel(Coin)?.number(Item_Value)获取 Number 字段写入value即可驱动动画final coinAmount viewModelInstance.viewModel(Coin)?.number(Item_Value); coinAmount?.value 100;6. 完整示例可点击的奖励面板示例工程 演示了比文档示例更完整的玩法把 Rive 动画作为游戏背景/面板叠加三个可点击区域点击即通过 Data Binding 修改硬币、宝石、生命值等数值动画随之联动。加载部分与文档流程一致使用stateMachine(State Machine 1)与 ViewModel 绑定final file await File.asset( assets/rewards.riv, riveFactory: Factory.flutter, ).then((file) file!); final artboard await loadArtboard(file); final stateMachine artboard.stateMachine(State Machine 1); if (stateMachine ! null) { final viewModel file.defaultArtboardViewModel(artboard); if (viewModel ! null) { final viewModelInstance viewModel.createDefaultInstance(); if (viewModelInstance ! null) { stateMachine.bindViewModelInstance(viewModelInstance); } } } add(RewardsComponent(artboard, stateMachine));RewardsComponent继承RiveComponent在onLoad中从stateMachine!.boundRuntimeViewModelInstance取回绑定好的实例解析三个数值字段final viewModelInstance stateMachine!.boundRuntimeViewModelInstance; if (viewModelInstance ! null) { _coinInput viewModelInstance.viewModel(Coin)?.number(Item_Value); _gemInput viewModelInstance.viewModel(Gem)?.number(Item_Value); _livesInput viewModelInstance .viewModel(Energy_Bar) ?.number(Lives); }交互区则组合 Flame 的PositionComponent、TapCallbacks与GestureHitboxes点击时修改数值class RewardsArea extends PositionComponent with TapCallbacks, GestureHitboxes { RewardsArea({required this.onTap}); final VoidCallback onTap; override Futurevoid onLoad() async { await super.onLoad(); add(RectangleHitbox()); } override void onTapDown(TapDownEvent event) onTap(); }点击回调中直接读写已绑定的 Number 属性例如onTap: () { if (_coinInput ! null) { _coinInput!.value (_coinInput!.value 10) % 1001; } }onGameResize中根据屏幕尺寸重排三个点击区域的位置与大小并在isLoaded时调用_updateAreas()同步布局。整体模式是Rive 负责视觉与状态Flame 组件树负责布局与输入Data Binding 负责两者之间的数据通道。7. 常见问题与注意事项依赖版本flame_rive 1.11.2需要rive ^0.14.6交互统一走 Data Binding旧式的 state machine inputs 已弃用新代码不要再使用。命名 ArtboardloadArtboard指定artboardName时若文件中不存在同名 Artboard 会触发断言失败请先在 Rive 编辑器中确认名称。状态机推进只要把stateMachine传给RiveComponent其update会自动advanceAndApply(dt)不传状态机时只播放基础动画。尺寸适配组件默认尺寸等于 Artboard 尺寸传入自定义size并配合fitBoxFit控制显示方式size变化会自动同步渲染帧。原生初始化使用原生渲染路径时先在main()中await RiveNative.init()见 示例 main.dart并保证WidgetsFlutterBinding.ensureInitialized()已调用。8. 进一步阅读桥接文档入口doc/bridge_packages/flame_rive/flame_rive.md其主体内容即本文核心依据的 rive.md核心实现packages/flame_rive/lib/src/rive_component.dartRiveComponent、loadArtboard入口导出packages/flame_rive/lib/flame_rive.dart完整可运行示例packages/flame_rive/example/lib/main.dart资源在 packages/flame_rive/example/assets依赖与版本约束packages/flame_rive/pubspec.yaml、packages/flame_rive/example/pubspec.yaml。至此从加载.riv文件到 Artboard、StateMachine再到 Data Binding 交互与源码级原理你已经掌握了在 Flame 游戏中集成 Rive 动画的完整方案可以直接基于 rewards.riv / skills.riv 示例资源 动手验证。【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flame创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考