diff --git a/lib/game/components/sling_shot.dart b/lib/game/components/sling_shot.dart index a39d130e..b00ce4ca 100644 --- a/lib/game/components/sling_shot.dart +++ b/lib/game/components/sling_shot.dart @@ -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 _createFixtureDefs() { - final fixturesDef = []; + final fixturesDefs = []; + 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