Flutter快速触控插件flame_fast_touch的使用

Flutter快速触控插件flame_fast_touch的使用

在处理大量组件时,Flame框架的触摸事件处理可能会变得非常缓慢。flame_fast_touch插件提供了一种替代方法来解决这个问题。从Flame 1.10.0版本开始,引入了IgnoreEvents混合类,它允许你跳过某些组件子树的事件处理。然而,这种方法的一个缺点是你需要在每个可能的组件分支上添加这样的混合类,这可能会导致难以记住所有分支,从而导致性能损失。

flame_fast_touch插件不需要任何混合类(但它支持Flame的混合类),并且允许你直接指定一个子树的根节点,该子树的后代将处理所有的触摸事件。

特性

此库是一个可能的快速解决方案,使你的触摸输入再次快速响应。

你可以通过比较原生性能与本库性能来体验这一点。访问以下示例页面:

注意,由于大约有2000000*3个组件需要加载到游戏中,这在浏览器中运行可能需要一些时间。

入门指南

为了确保一切正常工作,你需要遵循一些严格的规则:

  1. 所有的组件都应该被添加到游戏的world组件及其后代中。不能直接添加到游戏中。
  2. 非交互式组件应该与交互式组件分开,并放置在不同的父组件中。

完成上述步骤后,你就可以使用游戏的特殊变量componentsAtPointRoot。该变量限制框架只在一个组件中搜索可点击或可拖动的组件。如果需要从另一个组件树分支与组件集进行交互,可以随时更改componentsAtPointRoot。甚至可以将其设置为null,以回退到默认的Flame行为。

使用示例

最小的工作代码示例如下,查看TapCallbacksMultipleExample类:

import 'package:flame/components.dart';
import 'package:flame/events.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame_fast_touch/flame_fast_touch.dart';
import 'package:flutter/material.dart';

const componentsCount = 2000000;

class TapCallbacksMultipleExample extends FlameGame with FastTouch {
  static const String description = '''
    这个示例与tap_callbacks_example做同样的事情,但背景中有大量的非交互式组件。在这种情况下,当你在屏幕上任何地方点击时,会经历冻结,因为需要扫描整个组件树。
    该示例展示了避免这种冻结的一种方法:
    1. 将所有组件添加到游戏的世界或相机的视口或视图查找器中。这是重要的要求!
    2. 将所有交互式组件添加到特殊的分组组件中。
    3. 将这个组件分配给`componentsAtPointRoot`变量。
    4. 将所有其他组件放置在你想放置的任何位置。
    这些步骤使得`componentsAtPoint`函数只搜索`componentsAtPointRoot`的子组件,并跳过其他树分支的循环。
  ''';

  [@override](/user/override)
  Future<void> onLoad() async {
    final interactiveComponents = Component();
    interactiveComponents.add(TappableSquare()..anchor = Anchor.center);

    final bottomSquare = TappableSquare()..y = 350;
    interactiveComponents.add(bottomSquare);
    world.add(interactiveComponents);

    final updateTreeDisabled = ComponentNoTreeUpdate();
    for (var i = 1; i < componentsCount; i++) {
      updateTreeDisabled.add(Component());
    }
    world.add(updateTreeDisabled);

    camera.moveTo(bottomSquare.position + Vector2(0, -50));

    componentsAtPointRoot = interactiveComponents;
  }
}

class TapCallbacksVanillaExample extends FlameGame {
  static const String description = '''
    这是原生版本的触摸处理。你应该会在触摸屏幕的任何点时经历冻结。
  ''';

  [@override](/user/override)
  Future<void> onLoad() async {
    add(TappableSquare()..anchor = Anchor.center);
    add(TappableSquare()..y = 350);

    final updateTreeDisabled = ComponentNoTreeUpdate();
    for (var i = 1; i < componentsCount; i++) {
      updateTreeDisabled.add(Component());
    }
    add(updateTreeDisabled);
  }
}

class TapCallbacksIgnoreMixinExample extends FlameGame {
  static const String description = '''
    这是原生版本的触摸处理,由新的IgnoreEvents混合类增强。结果应该与FastTouch相同,但需要扩展每个应该忽略事件的组件。
  ''';

  [@override](/user/override)
  Future<void> onLoad() async {
    add(TappableSquare()..anchor = Anchor.center);
    add(TappableSquare()..y = 350);

    final updateTreeDisabled = _IgnoreEventsComponent();
    for (var i = 1; i < componentsCount; i++) {
      updateTreeDisabled.add(Component());
    }
    add(updateTreeDisabled);
  }
}

class _IgnoreEventsComponent extends ComponentNoTreeUpdate with IgnoreEvents {}

class TappableSquare extends PositionComponent
    with TapCallbacks, HasGameReference<TapCallbacksMultipleExample> {
  static final Paint _white = Paint()..color = const Color(0xFFFFFFFF);
  static final Paint _grey = Paint()..color = const Color(0xFFA5A5A5);

  bool _beenPressed = false;

  TappableSquare({
    Vector2? position,
  }) : super(
          position: position ?? Vector2.all(100),
          size: Vector2.all(100),
        );

  [@override](/user/override)
  void render(Canvas canvas) {
    canvas.drawRect(size.toRect(), _beenPressed ? _white : _grey);
  }

  [@override](/user/override)
  void onTapUp(_) {
    _beenPressed = false;
  }

  [@override](/user/override)
  void onTapDown(_) {
    _beenPressed = true;
    angle += 1.0;
  }

  [@override](/user/override)
  void onTapCancel(_) {
    _beenPressed = false;
  }
}

class ComponentNoTreeUpdate extends Component {
  [@override](/user/override)
  void updateTree(double dt) {}

  [@override](/user/override)
  void renderTree(Canvas canvas) {}
}

class TwoPane extends StatelessWidget {
  const TwoPane({
    super.key,
    required this.gameLeft,
    required this.gameMiddle,
    required this.gameRight,
  });

  final FlameGame gameLeft;
  final FlameGame gameMiddle;
  final FlameGame gameRight;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Material(
        child: Column(
          children: [
            const Center(
                child: Text(
                    '当游戏中包含大量组件时,触摸事件处理算法的性能对比。\n'
                    '当前组件数量: $componentsCount',
                    style: TextStyle(
                      fontSize: 16,
                    ))),
            Row(children: [
              Column(children: [
                const Text('Vanilla Flame',
                    style: TextStyle(
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    )),
                SizedBox(
                  height: 600,
                  width: 400,
                  child: GameWidget(game: gameLeft),
                ),
              ]),
              const VerticalDivider(width: 10),
              Column(children: [
                const Text('带有IgnoreEvents混合类的Flame',
                    style: TextStyle(
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    )),
                SizedBox(
                  height: 600,
                  width: 400,
                  child: GameWidget(game: gameMiddle),
                ),
              ]),
              const VerticalDivider(width: 10),
              Column(
                children: [
                  const Text('Fast Touch插件',
                      style: TextStyle(
                        fontSize: 20,
                        fontWeight: FontWeight.bold,
                      )),
                  SizedBox(
                    height: 600,
                    width: 400,
                    child: GameWidget(game: gameRight),
                  ),
                ],
              )
            ]),
          ],
        ),
      ),
    );
  }
}

void main(List<String> args) async {
  runApp(TwoPane(
    gameLeft: TapCallbacksVanillaExample(),
    gameMiddle: TapCallbacksIgnoreMixinExample(),
    gameRight: TapCallbacksMultipleExample(),
  ));
}

更多关于Flutter快速触控插件flame_fast_touch的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter快速触控插件flame_fast_touch的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用flame_fast_touch插件的示例代码。flame_fast_touchflame游戏引擎的一部分,用于处理快速且准确的触摸事件。

首先,你需要在你的pubspec.yaml文件中添加flameflame_fast_touch依赖项:

dependencies:
  flutter:
    sdk: flutter
  flame: ^1.0.0  # 确保使用最新版本
  flame_fast_touch: ^1.0.0  # 确保使用最新版本

然后运行flutter pub get来安装这些依赖项。

接下来,你可以在你的Flutter项目中使用flame_fast_touch。以下是一个简单的示例,展示了如何创建一个Flutter应用并处理触摸事件:

import 'package:flutter/material.dart';
import 'package:flame/game.dart';
import 'package:flame_fast_touch/flame_fast_touch.dart';

class MyGame extends Game with FastTouchDetector {
  @override
  void onLoad() {
    // 初始化游戏
    add(FastTouchComponent());
  }
}

class FastTouchComponent extends PositionComponent with Tappable {
  final double size = 100.0;
  bool isPressed = false;

  @override
  void render(Canvas canvas) {
    final paint = Paint()
      ..color = isPressed ? Colors.red : Colors.blue
      ..style = PaintingStyle.fill;

    canvas.drawRect(Rect.fromLTWH(x, y, size, size), paint);
  }

  @override
  Rect getBounds() => Rect.fromLTWH(x, y, size, size);

  @override
  void onTapDown(TapDownDetails details) {
    isPressed = true;
  }

  @override
  void onTapUp(TapUpDetails details) {
    isPressed = false;
  }
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flame Fast Touch Example'),
        ),
        body: GameWidget(game: MyGame()),
      ),
    );
  }
}

在这个示例中:

  1. MyGame类继承自Game并实现了FastTouchDetector,这使得它能够处理快速触摸事件。
  2. FastTouchComponent是一个PositionComponent,它实现了Tappable接口,用于处理触摸事件。它重写了onTapDownonTapUp方法来改变其颜色,表示按钮被按下或释放。
  3. MyApp是一个标准的Flutter应用,它使用GameWidget来显示MyGame游戏。

运行这个代码后,你应该会看到一个蓝色的方块,当你按下它时,它会变成红色,当你释放它时,它会再次变成蓝色。

这个示例展示了如何使用flame_fast_touch来处理基本的触摸事件。根据你的需求,你可以进一步扩展这个示例来处理更多的触摸事件或实现更复杂的逻辑。

回到顶部