This is my code. It comes from the demo of Flame. At present, the code can run normally. However, when I change the type attribute of the BodyDef class to BodyType.dynamic, it cannot run normally. I want a demo of ChainShape dynamic
import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame/events.dart';
import 'package:flame/extensions.dart';
import 'package:flame/palette.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
class RevoluteJointWithMotorExample1 extends Forge2DGame {
static const String description = '''
This example showcases a revolute joint, which is the spinning balls in the
center.
If you tap the screen some colorful balls are added and will
interact with the bodies tied to the revolute joint once they have fallen
down the funnel.
''';
RevoluteJointWithMotorExample1()
: super(world: RevoluteJointWithMotorWorld());
}
class RevoluteJointWithMotorWorld extends Forge2DWorld
with TapCallbacks, HasGameReference<Forge2DGame> {
final random = Random();
@override
Future<void> onLoad() async {
super.onLoad();
final boundaries = createBoundaries(game);
addAll(boundaries);
final center = Vector2.zero();
add(CircleShuffler(center));
add(CornerRamp(center, isMirrored: true));
add(CornerRamp(center));
}
@override
void onTapDown(TapDownEvent info) {
super.onTapDown(info);
final tapPosition = info.localPosition;
List.generate(15, (i) {
final randomVector = (Vector2.random() - Vector2.all(-0.5)).normalized();
add(Ball(tapPosition + randomVector, radius: random.nextDouble()));
});
}
}
class CircleShuffler extends BodyComponent {
CircleShuffler(this._center);
final Vector2 _center;
@override
Body createBody() {
final bodyDef = BodyDef(
type: BodyType.dynamic,
position: _center + Vector2(0.0, 25.0),
);
const numPieces = 5;
const radius = 6.0;
final body = world.createBody(bodyDef);
for (var i = 0; i < numPieces; i++) {
final xPos = radius * cos(2 * pi * (i / numPieces));
final yPos = radius * sin(2 * pi * (i / numPieces));
final shape = CircleShape()
..radius = 1.2
..position.setValues(xPos, yPos);
final fixtureDef = FixtureDef(
shape,
density: 50.0,
friction: .1,
restitution: .9,
);
body.createFixture(fixtureDef);
}
// Create an empty ground body.
final groundBody = world.createBody(BodyDef());
final revoluteJointDef = RevoluteJointDef()
..initialize(body, groundBody, body.position)
..motorSpeed = pi
..maxMotorTorque = 1000000.0
..enableMotor = true;
world.createJoint(RevoluteJoint(revoluteJointDef));
return body;
}
}
class CornerRamp extends BodyComponent {
CornerRamp(this._center, {this.isMirrored = false});
final bool isMirrored;
final Vector2 _center;
@override
Body createBody() {
final shape = ChainShape();
final mirrorFactor = isMirrored ? -1 : 1;
final diff = 2.0 * mirrorFactor;
final vertices = [
Vector2(diff, 0),
Vector2(diff + 20.0 * mirrorFactor, -20.0),
Vector2(diff + 35.0 * mirrorFactor, -30.0),
];
shape.createLoop(vertices);
final fixtureDef = FixtureDef(shape, friction: 0.1);
final bodyDef = BodyDef()
..position = _center
..type = BodyType.static;
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
List<Wall> createBoundaries(Forge2DGame game, {double? strokeWidth}) {
final visibleRect = game.camera.visibleWorldRect;
final topLeft = visibleRect.topLeft.toVector2();
final topRight = visibleRect.topRight.toVector2();
final bottomRight = visibleRect.bottomRight.toVector2();
final bottomLeft = visibleRect.bottomLeft.toVector2();
return [
Wall(topLeft, topRight, strokeWidth: strokeWidth),
Wall(topRight, bottomRight, strokeWidth: strokeWidth),
Wall(bottomLeft, bottomRight, strokeWidth: strokeWidth),
Wall(topLeft, bottomLeft, strokeWidth: strokeWidth),
];
}
class Wall extends BodyComponent {
final Vector2 start;
final Vector2 end;
final double strokeWidth;
Wall(this.start, this.end, {double? strokeWidth})
: strokeWidth = strokeWidth ?? 1;
@override
Body createBody() {
final shape = EdgeShape()..set(start, end);
final fixtureDef = FixtureDef(shape, friction: 0.3);
final bodyDef = BodyDef(
userData: this, // To be able to determine object in collision
position: Vector2.zero(),
);
paint.strokeWidth = strokeWidth;
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
class Ball extends BodyComponent with ContactCallbacks {
late Paint originalPaint;
bool giveNudge = false;
final double radius;
final BodyType bodyType;
final Vector2 _position;
double _timeSinceNudge = 0.0;
static const double _minNudgeRest = 2.0;
final Paint _blue = BasicPalette.blue.paint();
Ball(
this._position, {
this.radius = 2,
this.bodyType = BodyType.dynamic,
Color? color,
}) {
if (color != null) {
originalPaint = PaletteEntry(color).paint();
} else {
originalPaint = randomPaint();
}
paint = originalPaint;
}
Paint randomPaint() => PaintExtension.random(withAlpha: 0.9, base: 100);
@override
Body createBody() {
final shape = CircleShape();
shape.radius = radius;
final fixtureDef = FixtureDef(
shape,
restitution: 0.8,
friction: 0.4,
);
final bodyDef = BodyDef(
userData: this,
angularDamping: 0.8,
position: _position,
type: bodyType,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
@override
void renderCircle(Canvas canvas, Offset center, double radius) {
super.renderCircle(canvas, center, radius);
final lineRotation = Offset(0, radius);
canvas.drawLine(center, center + lineRotation, _blue);
}
final _impulseForce = Vector2(0, 1000);
@override
@mustCallSuper
void update(double dt) {
_timeSinceNudge += dt;
if (giveNudge) {
giveNudge = false;
if (_timeSinceNudge > _minNudgeRest) {
body.applyLinearImpulse(_impulseForce);
_timeSinceNudge = 0.0;
}
}
}
@override
void beginContact(Object other, Contact contact) {
if (other is Wall) {
other.paint = paint;
}
if (other is WhiteBall) {
return;
}
if (other is Ball) {
if (paint != originalPaint) {
paint = other.paint;
} else {
other.paint = paint;
}
}
}
}
class WhiteBall extends Ball with ContactCallbacks {
WhiteBall(super.position) {
originalPaint = BasicPalette.white.paint();
paint = originalPaint;
}
@override
void beginContact(Object other, Contact contact) {
if (other is Ball) {
other.giveNudge = true;
}
}
}
I checked the relevant files of Flame and found no solution. The error points to the source code. I have difficulty understanding the source code at present
I found a detailed description from the Box2D document. The ChainShape is an advanced EdgeShape, and the EdgeShape only supports collisions with circles and polygons.