Search code examples
flutterflame

Multi touch detection In Flutter flame


I want to implement game control for a boxing game.

Four sections in black represent uppercuts and jabs. In order to block, the player should use multi-touch, where one figure should be on the left half of the screen and another figure on the right half. This is clearly overlapping other controls.

What is the best way to handle multi-touch while little conflicts with other controls in Flame engine (flutter).

enter image description here


Solution

  • Since this was replied to in the GitHub issue, I'll write the answer here too if anyone else stumbles upon this.

    You can use Tappable or TapCallbacks mixin on a component that covers those areas, like this:

    class MultiTapGame extends FlameGame with HasTappables {
      @override
      Future<void> onLoad() async {
        debugMode = true;
        await add(Controls());
      }
    }
    
    class Controls extends Component with HasGameRef {
      bool isLeftDown = false;
      bool isRightDown = false;
    
      @override
      Future<void> onLoad() async {
        await addAll(
          [
            TappableRegion(
              position: Vector2.all(0),
              size: Vector2(gameRef.size.x * 0.5, gameRef.size.y * 0.75),
              onTapDownCallback: () {
                isLeftDown = true;
                if (isRightDown) {
                  print("Block");
                } else {
                  print("Left Uppercut");
                }
              },
              onTapUpCallback: () {
                isLeftDown = false;
              },
            ),
            TappableRegion(
              position: Vector2(gameRef.size.x * 0.5, 0),
              size: Vector2(gameRef.size.x * 0.5, gameRef.size.y * 0.75),
              onTapDownCallback: () {
                isRightDown = true;
                if (isLeftDown) {
                  print("Block");
                } else {
                  print("Right Uppercut");
                }
              },
              onTapUpCallback: () {
                isRightDown = false;
              },
            ),
            TappableRegion(
              position: Vector2(0, gameRef.size.y * 0.75),
              size: Vector2(gameRef.size.x * 0.5, gameRef.size.y * 0.25),
              onTapDownCallback: () {
                isLeftDown = true;
                if (isRightDown) {
                  print("Block");
                } else {
                  print("Left Jab");
                }
              },
              onTapUpCallback: () {
                isLeftDown = false;
              },
            ),
            TappableRegion(
              position: Vector2(gameRef.size.x * 0.5, gameRef.size.y * 0.75),
              size: Vector2(gameRef.size.x * 0.5, gameRef.size.y * 0.25),
              onTapDownCallback: () {
                isRightDown = true;
                if (isLeftDown) {
                  print("Block");
                } else {
                  print("Right Jab");
                }
              },
              onTapUpCallback: () {
                isRightDown = false;
              },
            ),
          ],
        );
      }
    }
    
    class TappableRegion extends PositionComponent with Tappable {
      TappableRegion({
        super.position,
        super.size,
        super.scale,
        super.angle,
        super.nativeAngle,
        super.anchor,
        super.children,
        super.priority,
        this.onTapDownCallback,
        this.onTapUpCallback,
      });
    
      late RectangleComponent _rectangleComponent;
    
      @override
      FutureOr<void> onLoad() {
        _rectangleComponent = RectangleComponent(
          size: size,
          paint: Paint()..color = Colors.green,
        );
        return super.onLoad();
      }
    
      void Function()? onTapDownCallback;
      void Function()? onTapUpCallback;
    
      @override
      bool onTapDown(TapDownInfo info) {
        onTapDownCallback?.call();
        add(_rectangleComponent);
        return super.onTapDown(info);
      }
    
      @override
      bool onTapUp(TapUpInfo info) {
        onTapUpCallback?.call();
        _rectangleComponent.removeFromParent();
        return super.onTapUp(info);
      }
    
      @override
      bool onTapCancel() {
        onTapUpCallback?.call();
        _rectangleComponent.removeFromParent();
        return super.onTapCancel();
      }
    }