ARTICLE DETAIL

资讯详情

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

Flame 其他输入方式与辅助组件全指南:虚拟摇杆、按钮体系与事件忽略机制

Flame 其他输入方式与辅助组件全指南:虚拟摇杆、按钮体系与事件忽略机制 Flame 其他输入方式与辅助组件全指南虚拟摇杆、按钮体系与事件忽略机制【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flame本文是 Flame 游戏引擎输入体系inputs.md中其他输入与辅助部分的深度指南聚焦键盘、鼠标之外的游戏输入手段。文章围绕 other_inputs.md 展开结合仓库源码深入讲解JoystickComponent虚拟摇杆、HudButtonComponent/SpriteButtonComponent/ButtonComponent/AdvancedButtonComponent/ToggleButtonComponent五级按钮体系以及用于性能优化的IgnoreEvents事件忽略机制。读完后你将能够在自己的 Flame 游戏中快速实现移动端虚拟摇杆、屏幕固定 HUD 按钮、多状态皮肤按钮并通过事件忽略机制优化组件树的输入分发。概述除键盘鼠标之外Flame 还提供哪些输入能力Flame 的输入 API 以 mixin 形式混入组件详见 inputs.md 的事件坐标系统说明但游戏往往还需要键盘、鼠标、触摸之外的交互手段移动端常见的虚拟摇杆、模拟手柄按键的屏幕按钮、外部游戏手柄等。本文涉及的组件与 mixin 全部位于packages/flame/lib/src/components/input/目录包括组件 / Mixin核心用途源码位置JoystickComponent虚拟摇杆拖动旋钮产生方向与力度输入joystick_component.dartHudButtonComponent以边距定位、固定于屏幕HUD的按钮hud_button_component.dartSpriteButtonComponent由两张 Sprite 定义的按钮sprite_button_component.dartButtonComponent由两个PositionComponent定义的通用按钮button_component.dartAdvancedButtonComponent为每个指针阶段提供独立皮肤的状态按钮advanced_button_component.dartToggleButtonComponent可选中/未选中切换的按钮toggle_button_component.dartIgnoreEventsmixin让组件子树整体忽略事件ignore_events.dart其中游戏手柄Gamepad输入由独立插件包flame_gamepads提供仓库中对应目录为 packages/flame_gamepads下文单独说明。虚拟摇杆 JoystickComponentJoystickComponent是 Flame 提供的虚拟摇杆组件创建一个摇杆组件按需配置然后加入游戏即可。它适合移动端触屏游戏的方向控制场景也常用于桌面/网页演示。基本用法与完整示例以下示例来自 other_inputs.md创建了MyGame与Player两个类MyGame负责创建摇杆并传给PlayerPlayer在update中根据摇杆当前状态驱动自身运动class MyGame extends FlameGame { override Futurevoid onLoad() async { super.onLoad(); final image await images.load(assets/images/joystick.png); final sheet SpriteSheet.fromColumnsAndRows( image: image, columns: 6, rows: 1, ); final joystick JoystickComponent( knob: SpriteComponent( sprite: sheet.getSpriteById(1), size: Vector2.all(100), ), background: SpriteComponent( sprite: sheet.getSpriteById(0), size: Vector2.all(150), ), margin: const EdgeInsets.only(left: 40, bottom: 40), ); final player Player(joystick); add(player); add(joystick); } } class Player extends SpriteComponent with HasGameRef { Player(this.joystick) : super( anchor: Anchor.center, size: Vector2.all(100.0), ); /// Pixels/s double maxSpeed 300.0; final JoystickComponent joystick; override Futurevoid onLoad() async { sprite await gameRef.loadSprite(assets/images/layers/player.png); position gameRef.size / 2; } override void update(double dt) { if (joystick.direction ! JoystickDirection.idle) { position.add(joystick.relativeDelta * maxSpeed * dt); angle joystick.delta.screenAngle(); } } }关键点拆解摇杆旋钮knob与底盘background两者都是PositionComponent。可以从精灵表SpriteSheet.fromColumnsAndRows取图也可以直接用CircleComponent等形状组件见 joystick_example.dart 中的纯圆形实现joystick JoystickComponent( knob: CircleComponent(radius: 30, paint: knobPaint), background: CircleComponent(radius: 100, paint: backgroundPaint), margin: const EdgeInsets.only(left: 40, bottom: 40), );margin 定位摇杆通过marginEdgeInsets相对视口边缘定位默认左下角由ComponentViewportMarginmixin 实现见 component_viewport_margin.dart。构造约束从源码joystick_component.dart可以看到构造函数含两条断言size与background必须至少提供其一knob与background的位置不允许手动设置必须保持零点组件挂载时会自动计算摆放。knobRadius默认取size.x / 2即旋钮可拖动的最大半径默认为摇杆底盘的半宽。摇杆状态字段intensity、delta、relativeDelta摇杆在不同状态下有一组字段用于读取当前输入状态other_inputs.mdintensitydouble取值范围[0.0, 1.0]表示旋钮从圆心被拖到摇杆边缘或knobRadius若设置了该值的百分比可理解为力度/行程比例。deltaVector2旋钮相对圆心的绝对拖拽量以像素为单位其长度会被限制在knobRadius之内。relativeDeltaVector2delta / knobRadius的归一化结果元素取值范围约[-1, 1]表示旋钮当前从基位被拉向边缘的百分比与方向适合直接乘以速度参与位移计算如示例中的position.add(joystick.relativeDelta * maxSpeed * dt)。三者与源码的对应关系joystick_component.dartdouble intensity 0.0; // [0.0, 1.0] 拖拽行程比例 final Vector2 delta Vector2.zero(); // 绝对拖拽量被限制在 knobRadius 内 Vector2 get relativeDelta delta / knobRadius; // 归一化拖拽向量在updatejoystick_component.dart中每次更新都会将原始拖拽量_unscaledDelta限制到knobRadius圆内delta.scaleTo(knobRadius)再更新旋钮位置并计算intensity delta.length2 / knobRadius2。也就是说即使手指拖出摇杆底盘delta与intensity也始终保持在合理范围内不会产生越界值。八方向判定JoystickDirectionJoystickComponent还提供了direction属性返回JoystickDirection枚举用于快速判断当前摇杆方向joystick_component.dartenum JoystickDirection { up, upLeft, upRight, right, down, downRight, downLeft, left, idle, }direction的判定逻辑joystick_component.dart当delta为零时返回idle否则取delta.screenAngle()将负角度转换为[0, 2π)区间再按每π/822.5°一个扇区映射为八方向之一。因此你可以像示例那样先用direction ! JoystickDirection.idle判断是否有输入再结合relativeDelta做连续位移、用delta.screenAngle()设置角色朝向。进阶用法摇杆 HUD 按钮的组合示例仓库的 joystick_advanced_example.dart 展示了更完整的实战组合摇杆驱动玩家移动HudButtonComponent作为翻转按钮分别水平/垂直翻转玩家用ButtonComponent实现缩放按钮、用SpriteButtonComponent实现透明度按钮这些按钮触发的是RotateEffect、ScaleEffect、OpacityEffect等效果见 effects.md 相关文档摇杆的实时状态intensity对应的速度、direction文本通过TextComponent叠加在屏幕上显示。同时注意该示例的组件挂载方式摇杆和按钮通过camera.viewport.addAll([...])挂到视口上从而始终固定在屏幕上玩家则加入world随游戏世界运动相关概念可参考 camera.md。按钮体系从 HUD 按钮到通用按钮Flame 的按钮组件统一遵循按下/释放两种外观 回调的设计模式。HudButtonComponent、ButtonComponent、SpriteButtonComponent三者对应这一模式的不同变体。HudButtonComponent屏幕固定按钮HudButtonComponent是用相对视口边缘的margin来定位而不是用position的按钮other_inputs.md。它接收两个PositionComponentbutton按钮空闲未按下时显示的外观buttonDown按钮被按下时显示的外观可选——若不希望在按下时改变外观或由button组件自行处理按下态可以省略。由于默认是 HUD 组件即使游戏相机移动按钮也始终静止在屏幕上。若希望它参与相机变换可设置hudButtonComponent.respectCamera true;。处理按下/释放有两种方式other_inputs.md在构造时传入回调onPressed、onReleased以及源码中支持的onCancelled继承组件并重写onTapDown、onTapUp、onTapCancel实现自定义逻辑。从源码hud_button_component.dart看HudButtonComponent extends ButtonComponent with HasGameRef, ComponentViewportMarginmargin定位与游戏引用gameRef均由 mixin 提供。进阶示例中的典型用法final flipButton HudButtonComponent( button: SpriteComponent(sprite: sheet.getSpriteById(2), size: buttonSize), buttonDown: SpriteComponent(sprite: sheet.getSpriteById(4), size: buttonSize), margin: const EdgeInsets.only(right: 80, bottom: 60), onPressed: player.flipHorizontally, );HudButtonComponent的button也可以是形状组件如CircleComponent、RectangleComponent进阶示例中的旋转按钮就用CircleComponent作为默认外观、RectangleComponent作为按下外观并配合margin定位。ButtonComponent两个 PositionComponent 的通用按钮ButtonComponent与HudButtonComponent同源但它用position定位而非边距适合放在游戏世界的任意坐标。它由两个PositionComponent定义一个表示按下状态一个表示释放状态other_inputs.md。如果需要纯 Sprite 按钮优先用SpriteButtonComponent但若想用SpriteAnimationComponent或其他非纯 Sprite 的内容做按钮ButtonComponent更合适。源码行为button_component.dart构造参数size缺省时取button?.size挂载时若size为零也会自动取button的尺寸挂载断言button必须提供构造传入或onLoad中设置onTapDown时若存在buttonDown会从父级移除button并挂上buttonDown同时调用onPressedonTapUp时反向切换并调用onReleasedonTapCancel时恢复默认外观并调用onCancelled。进阶示例中ButtonComponent用描边矩形与填充矩形分别作为两种状态配合position精确摆放final buttonComponent ButtonComponent( button: RectangleComponent( size: Vector2(185, 50), paint: Paint() ..color Colors.orange ..style PaintingStyle.stroke, ), buttonDown: RectangleComponent( size: Vector2(185, 50), paint: BasicPalette.magenta.paint(), ), position: Vector2(20, size.y - 280), onPressed: () player.add( ScaleEffect.by( Vector2.all(1.5), EffectController(duration: 1.0, reverseDuration: 1.0), ), ), );SpriteButtonComponent双 Sprite 按钮SpriteButtonComponent由两张Sprite定义按钮外观一张表示按下一张表示释放other_inputs.md。实现上它继承自SpriteGroupComponentButtonStateButtonState枚举含up与down两个状态见 sprite_button_component.dart因此可以手动修改current属性切换状态。与ButtonComponent的差异值得注意sprite_button_component.dartonTapDown仅切换外观current ButtonState.down不触发任何回调onPressed回调在onTapUp手指抬起时才触发同时外观恢复为uponTapCancel时仅恢复外观buttonDown是可选的不设置时按下态与释放态外观相同get buttonDown _buttonDown ?? button。进阶示例中从buttons.png精灵表1 列 2 行取上、下两张图作为两种状态final spriteButtonComponent SpriteButtonComponent( button: buttonSheet.getSpriteById(0), buttonDown: buttonSheet.getSpriteById(1), position: Vector2(20, size.y - 360), size: Vector2(185, 50), onPressed: () player.add( OpacityEffect.fadeOut( EffectController(duration: 0.5, reverseDuration: 0.5), ), ), );游戏手柄 Gamepad外部游戏手柄物理手柄输入由 Flame 的独立插件提供flame_gamepads包仓库源码位于 packages/flame_gamepads。原文档other_inputs.md指出如需支持手柄请使用该专用插件并在项目的pubspec.yaml中引入flame_gamepads依赖。手柄与本文的虚拟摇杆/屏幕按钮属于不同输入通道前者是外部控制器事件流后者是基于触摸/指针的组件级输入。AdvancedButtonComponent按指针阶段细分的多皮肤按钮AdvancedButtonComponent为每一种指针阶段pointer phase提供了独立状态每个状态皮肤都是一个PositionComponentother_inputs.md。其状态机由ButtonState枚举驱动advanced_button_component.dartup、down、hover、disabled。可用来自定义外观的字段字段说明defaultSkin按钮默认显示的外观必须提供downSkin按钮被点击/触摸时显示的外观hoverSkin鼠标悬停时显示的外观桌面与 Web 平台defaultLabel显示在皮肤之上的文字组件自动居中对齐disabledSkin按钮禁用时显示的外观disabledLabel按钮禁用时显示在皮肤之上的文字组件源码实现要点advanced_button_component.dart皮肤存储于skinsMap、标签存储于labelsMap均以ButtonState为键defaultSkin是必须的挂载时有断言其余皮肤可选标签通过AlignComponent(alignment: Anchor.center)自动居中于按钮之上labelAlignContainer所有皮肤尺寸会跟随按钮的size同步缩放_updateSizes状态切换优先级为禁用isDisabled 按下isPressed 悬停isHovered 默认见updateState第 185-199 行除onPressed/onReleased回调外还提供onChangeState(ButtonState state)在状态变化时触发isDisabled为可读写属性置为true后按钮将忽略点击/触摸事件。ToggleButtonComponent可选中切换的按钮ToggleButtonComponent是AdvancedButtonComponent的子类在选中与未选中两种状态间切换适合做开关switch或可选中标签tabother_inputs.md。在AdvancedButtonComponent已有皮肤之外它额外提供以下选中态皮肤与标签toggle_button_component.dart字段说明defaultSelectedSkin按钮处于选中状态时显示的外观downAndSelectedSkin按钮选中且被按下时显示的外观hoverAndSelectedSkin按钮选中且鼠标悬停时显示的外观桌面与 WebdisabledAndSelectedSkin按钮选中且禁用时显示的外观defaultSelectedLabel按钮选中时显示在皮肤之上的文字组件源码实现要点toggle_button_component.dartisSelected为读写属性点击onTapUp时自动取反选中状态挂载时断言defaultSelectedSkin必须提供提供onSelectedChanged(bool selected)回调在选中状态变化时触发setSkin对downAndSelected、hoverAndSelected、disabledAndSelected等复合状态做了回退处理若未提供对应的选中态皮肤会自动回退到非选中态的同级皮肤第 116-132 行updateState会结合isDisabled、isPressed、isHovered、isSelected四者综合计算当前ButtonState。使用示例结合仓库测试 toogle_button_component_test.dart 中的构造方式final toggle ToggleButtonComponent( defaultSkin: defaultSkin, defaultSelectedSkin: selectedSkin, onSelectedChanged: (selected) { // 处理选中/未选中变化 }, );IgnoreEvents让组件子树忽略所有事件如果某个组件子树不需要接收任何事件可以给它混入IgnoreEventsmixinother_inputs.md。添加该 mixin 后ignoreEvents默认即为true此时该组件及其所有后代组件都会停止接收事件将ignoreEvents置为false可恢复接收。源码实现ignore_events.dartmixin IgnoreEvents on Component { bool ignoreEvents true; }使用要点运行时动态开关可以随时读写ignoreEvents属性无需移除/重新添加组件连带效果忽略事件的同时该组件及其后代在Component.componentsAtLocation坐标点命中查询中也会被忽略——这意味着它既不会收到输入也不会参与基于位置的组件查找适用场景当一棵较大的组件子树不需要接收任何事件时使用例如纯装饰性的粒子层、静态背景层。文档明确指出这样做可以优化性能因为当前所有事件都会遍历整棵组件树忽略无用子树可以减少无谓的遍历开销。小结与进一步探索本文覆盖了 Flame 键盘鼠标之外的主要输入手段与辅助组件JoystickComponent虚拟摇杆通过intensity/delta/relativeDelta三个字段读取拖拽状态direction提供八方向判定margin实现视口边缘定位按钮四件套HudButtonComponent边距定位 HUD、ButtonComponent坐标定位 任意组件外观、SpriteButtonComponent双 Sprite 外观、AdvancedButtonComponent/ToggleButtonComponent按指针阶段细分的多皮肤状态机支持选中态与禁用态IgnoreEvents让组件子树整体忽略事件与坐标命中用于输入性能优化flame_gamepads外部手柄输入由独立插件包提供。如果希望继续深入仓库内可参考的资源包括完整可运行示例joystick_example.dart基础摇杆、joystick_advanced_example.dart摇杆 各类按钮组合、joystick_player.dart碰撞 摇杆驱动的玩家单元测试packages/flame/test/components/下的 joystick_component_test.dart、hud_button_component_test.dart、advanced_button_component_test.dart、toogle_button_component_test.dart以及 ignore_events_test.dart可用于理解各组件的行为边界输入体系总览inputs.md其中介绍了事件坐标系统devicePosition/canvasPosition/localPosition与GestureHitboxes命中判定机制是理解本页各组件事件来源的基础。【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flame创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表