Search code examples
flutterdartflame

Can a Flame game object such as PositionComponent be Collidable and Draggable


Successfully implemented a Draggable component. When adding the Hitbox and Collidable mixins to the class extended by PositionComponent the drag functionality stops working.

Is it possible to have a draggable component that is also collidable?

Flutter version: 2.2.3
Flame version: 1.0.0-releasecandidate.13

main.dart

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

void main() {
  runApp(
    GameWidget(
      game: DraggablesGame(),
    ),
  );
}

DraggablesGame.dart

import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'DraggableSquare.dart';

class DraggablesGame extends BaseGame with HasDraggableComponents, HasCollidables {

  @override
  Future<void> onLoad() async {
    add(DraggableSquare());
    add(DraggableSquare()..y = 350);
  }
}

DraggableSquare.dart

import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame/gestures.dart';
import 'package:flutter/material.dart' show Colors;
import 'DraggablesGame.dart';

class DraggableSquare extends PositionComponent
    with Draggable, HasGameRef<DraggablesGame>, Hitbox, Collidable {
  @override
  bool debugMode = true;

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

  Vector2? dragDeltaPosition;
  bool get isDragging => dragDeltaPosition != null;

  @override
  void update(double dt) {
    super.update(dt);
    debugColor = isDragging ? Colors.greenAccent : Colors.purple;
  }

  @override
  bool onDragStart(int pointerId, DragStartInfo info) {
    dragDeltaPosition = info.eventPosition.game - position;
    return false;
  }

  @override
  bool onDragUpdate(int pointerId, DragUpdateInfo event) {
    final dragDeltaPosition = this.dragDeltaPosition;
    if (dragDeltaPosition == null) {
      return false;
    }

    position.setFrom(event.eventPosition.game - dragDeltaPosition);
    return false;
  }

  @override
  bool onDragEnd(int pointerId, _) {
    dragDeltaPosition = null;
    return false;
  }

  @override
  bool onDragCancel(int pointerId) {
    dragDeltaPosition = null;
    return false;
  }
}

Update based on answer

Spydon's answer suggested using addHitbox(HitboxRectangle());. This resulted in the following error:

The method 'addHitbox' isn't defined for the type 'DraggableSquare'.

Instead this modified constructor allows for both the dragging and colliding.

Updated DraggableSquare Constructor

DraggableSquare({Vector2? position})
      : super(
    position: position,
    size: Vector2.all(100),
  ) {
    final hitBox = HitboxRectangle();
    addShape(hitBox);
  }

Solution

  • When you add the Hitbox mixin you also have to add some hitboxes, otherwise it will not be able to know what it should count as a "hit". The simplest solution is to add an empty hitbox of either the HitboxRectangle or HitboxCircle type. These hitbox will will the full size of the component if you don't define anything more specific in them.

    So to add a HitboxRectangle, you modify your constructor to this:

      DraggableSquare({Vector2? position})
          : super(
              position: position ?? Vector2.all(100),
              size: Vector2.all(100),
            ) {
        addShape(HitboxRectangle());
      }
    

    If you set debugMode = true you will be able to visually see the hitboxes that you add.