|
|
|
@ -1,7 +1,9 @@
|
|
|
|
|
import 'dart:math' as math;
|
|
|
|
|
|
|
|
|
|
import 'package:flame/extensions.dart';
|
|
|
|
|
import 'package:flame_forge2d/flame_forge2d.dart';
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:geometry/geometry.dart' show centroid;
|
|
|
|
|
import 'package:geometry/geometry.dart' as geometry show centroid;
|
|
|
|
|
import 'package:pinball/game/game.dart';
|
|
|
|
|
|
|
|
|
|
/// {@template sling_shot}
|
|
|
|
@ -31,54 +33,94 @@ class SlingShot extends BodyComponent with InitialPosition {
|
|
|
|
|
/// The size of the [SlingShot] body.
|
|
|
|
|
// TODO(alestiago): Use size from PositionedBodyComponent instead,
|
|
|
|
|
// once a sprite is given.
|
|
|
|
|
static final Vector2 size = Vector2(6, 8);
|
|
|
|
|
static final Vector2 size = Vector2(4, 10);
|
|
|
|
|
|
|
|
|
|
List<FixtureDef> _createFixtureDefs() {
|
|
|
|
|
final fixturesDef = <FixtureDef>[];
|
|
|
|
|
final fixturesDefs = <FixtureDef>[];
|
|
|
|
|
final direction = _side.direction;
|
|
|
|
|
const quarterPi = math.pi / 4;
|
|
|
|
|
|
|
|
|
|
// TODO(alestiago): This magic number can be deduced by specifying the
|
|
|
|
|
// angle and using polar coordinate system to place the bottom right
|
|
|
|
|
// vertex.
|
|
|
|
|
// Something as: y = -size.y * math.cos(angle)
|
|
|
|
|
const additionalIncrement = 3;
|
|
|
|
|
final triangleVertices = _side.isLeft
|
|
|
|
|
? [
|
|
|
|
|
Vector2(0, 0),
|
|
|
|
|
Vector2(0, -size.y),
|
|
|
|
|
Vector2(
|
|
|
|
|
size.x,
|
|
|
|
|
-size.y - additionalIncrement,
|
|
|
|
|
),
|
|
|
|
|
]
|
|
|
|
|
: [
|
|
|
|
|
Vector2(size.x, 0),
|
|
|
|
|
Vector2(size.x, -size.y),
|
|
|
|
|
final upperCircle = CircleShape()..radius = 1.45;
|
|
|
|
|
upperCircle.position.setValues(0, -upperCircle.radius / 2);
|
|
|
|
|
final upperCircleFixtureDef = FixtureDef(upperCircle);
|
|
|
|
|
fixturesDefs.add(upperCircleFixtureDef);
|
|
|
|
|
|
|
|
|
|
final lowerCircle = CircleShape()..radius = 1.45;
|
|
|
|
|
lowerCircle.position.setValues(
|
|
|
|
|
size.x * -direction,
|
|
|
|
|
-size.y,
|
|
|
|
|
);
|
|
|
|
|
final lowerCircleFixtureDef = FixtureDef(lowerCircle);
|
|
|
|
|
fixturesDefs.add(lowerCircleFixtureDef);
|
|
|
|
|
|
|
|
|
|
final wallFacingEdge = EdgeShape()
|
|
|
|
|
..set(
|
|
|
|
|
upperCircle.position +
|
|
|
|
|
Vector2(
|
|
|
|
|
upperCircle.radius * direction,
|
|
|
|
|
0,
|
|
|
|
|
-size.y - additionalIncrement,
|
|
|
|
|
),
|
|
|
|
|
];
|
|
|
|
|
final triangleCentroid = centroid(triangleVertices);
|
|
|
|
|
for (final vertex in triangleVertices) {
|
|
|
|
|
vertex.setFrom(vertex - triangleCentroid);
|
|
|
|
|
}
|
|
|
|
|
// TODO(alestiago): Use values from design.
|
|
|
|
|
Vector2(2.0 * direction, -size.y + 2),
|
|
|
|
|
);
|
|
|
|
|
final wallFacingLineFixtureDef = FixtureDef(wallFacingEdge);
|
|
|
|
|
fixturesDefs.add(wallFacingLineFixtureDef);
|
|
|
|
|
|
|
|
|
|
final triangle = PolygonShape()..set(triangleVertices);
|
|
|
|
|
final triangleFixtureDef = FixtureDef(triangle)..friction = 0;
|
|
|
|
|
fixturesDef.add(triangleFixtureDef);
|
|
|
|
|
final bottomEdge = EdgeShape()
|
|
|
|
|
..set(
|
|
|
|
|
wallFacingEdge.vertex2,
|
|
|
|
|
lowerCircle.position +
|
|
|
|
|
Vector2(
|
|
|
|
|
lowerCircle.radius * math.cos(quarterPi) * direction,
|
|
|
|
|
-lowerCircle.radius * math.sin(quarterPi),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
final bottomLineFixtureDef = FixtureDef(bottomEdge);
|
|
|
|
|
fixturesDefs.add(bottomLineFixtureDef);
|
|
|
|
|
|
|
|
|
|
final kicker = EdgeShape()
|
|
|
|
|
final kickerEdge = EdgeShape()
|
|
|
|
|
..set(
|
|
|
|
|
triangleVertices.first,
|
|
|
|
|
triangleVertices.last,
|
|
|
|
|
upperCircle.position +
|
|
|
|
|
Vector2(
|
|
|
|
|
upperCircle.radius * math.cos(quarterPi) * -direction,
|
|
|
|
|
upperCircle.radius * math.sin(quarterPi),
|
|
|
|
|
),
|
|
|
|
|
lowerCircle.position +
|
|
|
|
|
Vector2(
|
|
|
|
|
lowerCircle.radius * math.cos(quarterPi) * -direction,
|
|
|
|
|
lowerCircle.radius * math.sin(quarterPi),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
// TODO(alestiago): Play with restitution value once game is bundled.
|
|
|
|
|
final kickerFixtureDef = FixtureDef(kicker)
|
|
|
|
|
|
|
|
|
|
final kickerFixtureDef = FixtureDef(kickerEdge)
|
|
|
|
|
// TODO(alestiago): Play with restitution value once game is bundled.
|
|
|
|
|
..restitution = 10.0
|
|
|
|
|
..friction = 0;
|
|
|
|
|
fixturesDef.add(kickerFixtureDef);
|
|
|
|
|
fixturesDefs.add(kickerFixtureDef);
|
|
|
|
|
|
|
|
|
|
// TODO(alestiago): Evaluate if there is value on centering the fixtures.
|
|
|
|
|
final centroid = geometry.centroid(
|
|
|
|
|
[
|
|
|
|
|
upperCircle.position + Vector2(0, -upperCircle.radius),
|
|
|
|
|
lowerCircle.position +
|
|
|
|
|
Vector2(
|
|
|
|
|
lowerCircle.radius * math.cos(quarterPi) * -direction,
|
|
|
|
|
-lowerCircle.radius * math.sin(quarterPi),
|
|
|
|
|
),
|
|
|
|
|
wallFacingEdge.vertex2,
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
for (final fixtureDef in fixturesDefs) {
|
|
|
|
|
if (fixtureDef.shape is EdgeShape) {
|
|
|
|
|
final edge = fixtureDef.shape as EdgeShape;
|
|
|
|
|
edge.set(edge.vertex1 - centroid, edge.vertex2 - centroid);
|
|
|
|
|
} else if (fixtureDef.shape is CircleShape) {
|
|
|
|
|
final circle = fixtureDef.shape as CircleShape;
|
|
|
|
|
circle.position.setFrom(circle.position - centroid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fixturesDef;
|
|
|
|
|
return fixturesDefs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|