Search code examples
fluttergame-physicsbox2dflameforge2d

PolygonShape created at different position


I'm trying to create a polygon at the center of the screen with a mouse joint, very simple. A CircleShape works great. Also the mouse joint behaves strangely and I couldn't find a pattern. All code is in the main file here. I kept the code to a minimum.

Vector2 vec2Median(List<Vector2> vecs) {
  var sum = Vector2(0, 0);
  for (final v in vecs) {
    sum += v;
  }
  return sum / vecs.length.toDouble();
}

void main() {
  final game = MyGame();
  runApp(GameWidget(game: game));
}

class MyGame extends Forge2DGame with MultiTouchDragDetector, HasTappables {
  MouseJoint? mouseJoint;
  static late BodyComponent grabbedBody;
  late Body groundBody;

  MyGame() : super(gravity: Vector2(0, -10.0));

  @override
  Future<void> onLoad() async {
    final boundaries = createBoundaries(this); //Adding boundries
    boundaries.forEach(add);

    groundBody = world.createBody(BodyDef());
    final center = screenToWorld(camera.viewport.effectiveSize / 2);
    final poly = Polygon([
      center + Vector2(0, 0),
      center + Vector2(0, 5),
      center + Vector2(5, 0),
      center + Vector2(5, 5)
    ], bodyType: BodyType.dynamic);
    add(poly);
    grabbedBody = poly;
  }

  @override
  bool onDragUpdate(int pointerId, DragUpdateInfo details) {
    final mouseJointDef = MouseJointDef()
      ..maxForce = 3000 * grabbedBody.body.mass * 10 //Not neccerly needed
      ..dampingRatio = 1
      ..frequencyHz = 5
      ..target.setFrom(grabbedBody.body.position)
      ..collideConnected = false //Maybe set to true
      ..bodyA = groundBody
      ..bodyB = grabbedBody.body;

    mouseJoint ??= world.createJoint(mouseJointDef) as MouseJoint;

    mouseJoint?.setTarget(details.eventPosition.game);
    return false;
  }

  @override
  bool onDragEnd(int pointerId, DragEndInfo details) {
    if (mouseJoint == null) {
      return true;
    }
    world.destroyJoint(mouseJoint!);
    mouseJoint = null;
    return false;
  }
}

abstract class TappableBodyComponent extends BodyComponent with Tappable {
  final Vector2 position;
  final BodyType bodyType;
  TappableBodyComponent(this.position, {this.bodyType = BodyType.dynamic});

  @override
  bool onTapDown(_) {
    MyGame.grabbedBody = this;
    return false;
  }

  Body tappableBCreateBody(Shape shape) {
    final fixtureDef = FixtureDef(shape)
      ..restitution = 0.8
      ..density = 1.0
      ..friction = 0.4;

    final bodyDef = BodyDef()
      // To be able to determine object in collision
      ..userData = this
      ..angularDamping = 0.8
      ..position = position
      ..type = bodyType;

    return world.createBody(bodyDef)..createFixture(fixtureDef);
  }
}

class Polygon extends TappableBodyComponent {
  final List<Vector2> vertecies;

  Polygon(this.vertecies, {BodyType bodyType = BodyType.dynamic})
      : super(vec2Median(vertecies), bodyType: bodyType);

  @override
  Body createBody() {
    final shape = PolygonShape()..set(vertecies);
    return tappableBCreateBody(shape);
  }
}

tappableBCreateBody encapsulate Tappable and body creation methods, Polygon is the object I'm trying to create, vec2Median returns the center of the polygon (by vertices).

Thank you very much!


Solution

  • I think that you have to remove center from the vertices and only add that as the position of the BodyComponent instead, like you already do in the super call of your Polygon class.