From 4e2f56a70c372e6606d84e863303a8499248dafa Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 12:48:24 +0100 Subject: [PATCH 01/32] feat: created maths package for calculate curves --- packages/maths/.gitignore | 7 +++ packages/maths/README.md | 11 ++++ packages/maths/analysis_options.yaml | 1 + packages/maths/lib/maths.dart | 3 + packages/maths/lib/src/maths.dart | 81 +++++++++++++++++++++++++ packages/maths/pubspec.yaml | 15 +++++ packages/maths/test/src/maths_test.dart | 11 ++++ 7 files changed, 129 insertions(+) create mode 100644 packages/maths/.gitignore create mode 100644 packages/maths/README.md create mode 100644 packages/maths/analysis_options.yaml create mode 100644 packages/maths/lib/maths.dart create mode 100644 packages/maths/lib/src/maths.dart create mode 100644 packages/maths/pubspec.yaml create mode 100644 packages/maths/test/src/maths_test.dart diff --git a/packages/maths/.gitignore b/packages/maths/.gitignore new file mode 100644 index 00000000..526da158 --- /dev/null +++ b/packages/maths/.gitignore @@ -0,0 +1,7 @@ +# See https://www.dartlang.org/guides/libraries/private-files + +# Files and directories created by pub +.dart_tool/ +.packages +build/ +pubspec.lock \ No newline at end of file diff --git a/packages/maths/README.md b/packages/maths/README.md new file mode 100644 index 00000000..cbc4cca1 --- /dev/null +++ b/packages/maths/README.md @@ -0,0 +1,11 @@ +# maths + +[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link] +[![License: MIT][license_badge]][license_link] + +A Very Good Project created by Very Good CLI. + +[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg +[license_link]: https://opensource.org/licenses/MIT +[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg +[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis \ No newline at end of file diff --git a/packages/maths/analysis_options.yaml b/packages/maths/analysis_options.yaml new file mode 100644 index 00000000..3742fc3d --- /dev/null +++ b/packages/maths/analysis_options.yaml @@ -0,0 +1 @@ +include: package:very_good_analysis/analysis_options.2.4.0.yaml \ No newline at end of file diff --git a/packages/maths/lib/maths.dart b/packages/maths/lib/maths.dart new file mode 100644 index 00000000..b340388b --- /dev/null +++ b/packages/maths/lib/maths.dart @@ -0,0 +1,3 @@ +library maths; + +export 'src/maths.dart'; diff --git a/packages/maths/lib/src/maths.dart b/packages/maths/lib/src/maths.dart new file mode 100644 index 00000000..ea447db5 --- /dev/null +++ b/packages/maths/lib/src/maths.dart @@ -0,0 +1,81 @@ +import 'dart:math' as math; +import 'package:flame/extensions.dart'; + +/// Method to calculate all points (with a required precision amount of them) +/// of a circumference based on angle, offsetAngle and radius +List calculateArc({ + required Vector2 center, + required double radius, + required double angle, + double offsetAngle = 0, + int precision = 100, +}) { + final stepAngle = radians(angle / precision); + final stepOffset = radians(offsetAngle); + + final points = []; + for (var i = 0; i <= precision; i++) { + final xCoord = center.x + radius * math.cos((stepAngle * i) + stepOffset); + final yCoord = center.y - radius * math.sin((stepAngle * i) + stepOffset); + + final point = Vector2(xCoord, yCoord); + points.add(point); + } + + return points; +} + +/// Method that calculates all points of a bezier curve of degree 'g' and +/// n=g-1 control points and range 0<=t<=1 +/// https://en.wikipedia.org/wiki/B%C3%A9zier_curve +List calculateBezierCurve({ + required List controlPoints, + double step = 0.001, +}) { + assert( + controlPoints.length >= 2, + 'At least 2 control points to create a bezier curve', + ); + var t = 0.0; + final n = controlPoints.length - 1; + final points = []; + + do { + var xCoord = 0.0; + var yCoord = 0.0; + for (var i = 0; i <= n; i++) { + final point = controlPoints[i]; + + xCoord += + _binomial(n, i) * math.pow(1 - t, n - i) * math.pow(t, i) * point.x; + yCoord += + _binomial(n, i) * math.pow(1 - t, n - i) * math.pow(t, i) * point.y; + } + points.add(Vector2(xCoord, yCoord)); + + t = t + step; + } while (t <= 1); + + return points; +} + +/// Method to calculate the binomial coefficient of 'n' and 'k' +/// https://en.wikipedia.org/wiki/Binomial_coefficient +num _binomial(num n, num k) { + assert(0 <= k && k <= n, 'Range 0<=k<=n'); + if (k == 0 || n == k) { + return 1; + } else { + return _factorial(n) / (_factorial(k) * _factorial(n - k)); + } +} + +/// Method to calculate the factorial of some number 'n' +/// https://en.wikipedia.org/wiki/Factorial +num _factorial(num n) { + if (n == 1) { + return 1; + } else { + return n * _factorial(n - 1); + } +} diff --git a/packages/maths/pubspec.yaml b/packages/maths/pubspec.yaml new file mode 100644 index 00000000..3b4b863c --- /dev/null +++ b/packages/maths/pubspec.yaml @@ -0,0 +1,15 @@ +name: maths +description: A Very Good Project created by Very Good CLI. +version: 1.0.0+1 +publish_to: none + +environment: + sdk: ">=2.16.0 <3.0.0" + +dev_dependencies: + coverage: ^1.1.0 + mocktail: ^0.2.0 + test: ^1.19.2 + very_good_analysis: ^2.4.0 +dependencies: + flame: ^1.0.0 diff --git a/packages/maths/test/src/maths_test.dart b/packages/maths/test/src/maths_test.dart new file mode 100644 index 00000000..dceedab8 --- /dev/null +++ b/packages/maths/test/src/maths_test.dart @@ -0,0 +1,11 @@ +// ignore_for_file: prefer_const_constructors +import 'package:maths/maths.dart'; +import 'package:test/test.dart'; + +void main() { + group('Maths', () { + test('can be instantiated', () { + expect(Maths(), isNotNull); + }); + }); +} From 09464347ef2af67d9676e95a67f04096e83227d1 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 12:49:16 +0100 Subject: [PATCH 02/32] feat: added maths local dependency --- pubspec.lock | 7 +++++++ pubspec.yaml | 2 ++ 2 files changed, 9 insertions(+) diff --git a/pubspec.lock b/pubspec.lock index e218776d..db3b239a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -261,6 +261,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.3" + maths: + dependency: "direct main" + description: + path: "/Users/ruialonso/dev/flutter/googleIO22/pinball/packages/maths" + relative: false + source: path + version: "1.0.0+1" meta: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 5d708073..7b9c9c19 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,6 +18,8 @@ dependencies: flutter_localizations: sdk: flutter intl: ^0.17.0 + maths: + path: packages/maths dev_dependencies: bloc_test: ^9.0.2 From b0e8a3be143c27f1d022a65e14858e8c99206331 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 12:49:48 +0100 Subject: [PATCH 03/32] feat: created path component to paint pathways for the ball --- lib/game/components/components.dart | 1 + lib/game/components/path.dart | 141 ++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 lib/game/components/path.dart diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index 95134ec2..81e8cad2 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -1,5 +1,6 @@ export 'anchor.dart'; export 'ball.dart'; +export 'path.dart'; export 'plunger.dart'; export 'score_points.dart'; export 'wall.dart'; diff --git a/lib/game/components/path.dart b/lib/game/components/path.dart new file mode 100644 index 00000000..0d7105d7 --- /dev/null +++ b/lib/game/components/path.dart @@ -0,0 +1,141 @@ +import 'package:flame/extensions.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flutter/material.dart'; +import 'package:maths/maths.dart'; + +class Path extends BodyComponent { + Path._({ + Color? color, + required Vector2 position, + required List> paths, + }) : _position = position, + _paths = paths { + if (color != null) { + paint = Paint() + ..color = color + ..style = PaintingStyle.stroke; + } + } + + factory Path.straight({ + Color? color, + required Vector2 position, + required Vector2 start, + required Vector2 end, + required double pathWidth, + double rotation = 0, + bool onlyOneWall = false, + }) { + final paths = >[]; + final wall1 = [ + start.clone(), + end.clone(), + ]; + paths.add(wall1.map((e) => e..rotate(radians(rotation))).toList()); + + if (!onlyOneWall) { + final wall2 = [ + start + Vector2(pathWidth, 0), + end + Vector2(pathWidth, 0), + ]; + paths.add(wall2.map((e) => e..rotate(radians(rotation))).toList()); + } + + return Path._( + color: color, + position: position, + paths: paths, + ); + } + + factory Path.arc({ + Color? color, + required Vector2 position, + required double pathWidth, + required double radius, + required double angle, + double rotation = 0, + bool onlyOneWall = false, + }) { + final paths = >[]; + + final wall1 = calculateArc( + center: position, + radius: radius, + angle: angle, + offsetAngle: rotation, + ); + paths.add(wall1); + + if (!onlyOneWall) { + final minRadius = radius - pathWidth; + + final wall2 = calculateArc( + center: position, + radius: minRadius, + angle: angle, + offsetAngle: rotation, + ); + paths.add(wall2); + } + + return Path._( + color: color, + position: position, + paths: paths, + ); + } + + factory Path.bezierCurve({ + Color? color, + required Vector2 position, + required List controlPoints, + required double pathWidth, + double rotation = 0, + bool onlyOneWall = false, + }) { + final paths = >[]; + + final wall1 = calculateBezierCurve(controlPoints: controlPoints); + paths.add(wall1.map((e) => e..rotate(radians(rotation))).toList()); + + var wall2 = []; + if (!onlyOneWall) { + wall2 = calculateBezierCurve( + controlPoints: controlPoints + .map((e) => e + Vector2(pathWidth, -pathWidth)) + .toList(), + ); + paths.add(wall2.map((e) => e..rotate(radians(rotation))).toList()); + } + + return Path._( + color: color, + position: position, + paths: paths, + ); + } + + final Vector2 _position; + final List> _paths; + + @override + Body createBody() { + final bodyDef = BodyDef() + ..type = BodyType.static + ..position = _position; + + final body = world.createBody(bodyDef); + + for (final path in _paths) { + final chain = ChainShape() + ..createChain( + path.map((e) => gameRef.screenToWorld(e)).toList(), + ); + final fixtureDef = FixtureDef(chain); + body.createFixture(fixtureDef); + } + + return body; + } +} From 53f24643b7a8c94dc82b725ff2d17bb511f11e0d Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 12:50:12 +0100 Subject: [PATCH 04/32] test: tests for Path component --- test/game/components/path_test.dart | 249 ++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 test/game/components/path_test.dart diff --git a/test/game/components/path_test.dart b/test/game/components/path_test.dart new file mode 100644 index 00000000..440070b6 --- /dev/null +++ b/test/game/components/path_test.dart @@ -0,0 +1,249 @@ +// ignore_for_file: cascade_invocations + +import 'dart:math'; +import 'dart:ui'; + +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/game/game.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(PinballGame.new); + + group('Path', () { + const pathWidth = 50.0; + + group('straight', () { + group('color', () { + flameTester.test( + 'has white color by default if not specified', + (game) async { + final path = Path.straight( + position: Vector2.zero(), + start: Vector2(10, 10), + end: Vector2(20, 20), + pathWidth: pathWidth, + ); + await game.ensureAdd(path); + expect(game.contains(path), isTrue); + expect(path.paint, isNotNull); + expect(path.paint.color, equals(Colors.white)); + }, + ); + flameTester.test( + 'has a color if set', + (game) async { + const defaultColor = Colors.blue; + + final path = Path.straight( + color: defaultColor, + position: Vector2.zero(), + start: Vector2(10, 10), + end: Vector2(20, 20), + pathWidth: pathWidth, + ); + await game.ensureAdd(path); + expect(game.contains(path), isTrue); + expect(path.paint, isNotNull); + expect(path.paint.color.value, equals(defaultColor.value)); + }, + ); + }); + + flameTester.test( + 'loads correctly', + (game) async { + final path = Path.straight( + position: Vector2.zero(), + start: Vector2(10, 10), + end: Vector2(20, 20), + pathWidth: pathWidth, + ); + await game.ensureAdd(path); + expect(game.contains(path), isTrue); + }, + ); + + group('body', () { + flameTester.test( + 'positions correctly', + (game) async { + final position = Vector2.all(10); + final path = Path.straight( + position: position, + start: Vector2(10, 10), + end: Vector2(20, 20), + pathWidth: pathWidth, + ); + await game.ensureAdd(path); + game.contains(path); + + expect(path.body.position, position); + }, + ); + + flameTester.test( + 'is static', + (game) async { + final path = Path.straight( + position: Vector2.zero(), + start: Vector2(10, 10), + end: Vector2(20, 20), + pathWidth: pathWidth, + ); + await game.ensureAdd(path); + + expect(path.body.bodyType, equals(BodyType.static)); + }, + ); + }); + + group('fixtures', () { + flameTester.test( + 'exists only one ChainShape if just one wall', + (game) async { + final path = Path.straight( + position: Vector2.zero(), + start: Vector2(10, 10), + end: Vector2(20, 20), + pathWidth: pathWidth, + onlyOneWall: true, + ); + await game.ensureAdd(path); + + expect(path.body.fixtures.length, 1); + final fixture = path.body.fixtures[0]; + expect(fixture, isA()); + expect(fixture.shape.shapeType, equals(ShapeType.chain)); + }, + ); + + flameTester.test( + 'exists two ChainShape if there is by default two walls', + (game) async { + final path = Path.straight( + position: Vector2.zero(), + start: Vector2(10, 10), + end: Vector2(20, 20), + pathWidth: pathWidth, + ); + await game.ensureAdd(path); + + expect(path.body.fixtures.length, 2); + for (var fixture in path.body.fixtures) { + expect(fixture, isA()); + expect(fixture.shape.shapeType, equals(ShapeType.chain)); + } + }, + ); + }); + }); + + group('arc', () { + flameTester.test( + 'loads correctly', + (game) async { + final path = Path.arc( + position: Vector2.zero(), + pathWidth: pathWidth, + radius: 100, + angle: 90, + ); + await game.ensureAdd(path); + expect(game.contains(path), isTrue); + }, + ); + + group('body', () { + flameTester.test( + 'positions correctly', + (game) async { + final position = Vector2.all(10); + final path = Path.arc( + position: position, + pathWidth: pathWidth, + radius: 100, + angle: 90, + ); + await game.ensureAdd(path); + game.contains(path); + + expect(path.body.position, position); + }, + ); + + flameTester.test( + 'is static', + (game) async { + final path = Path.arc( + position: Vector2.zero(), + pathWidth: pathWidth, + radius: 100, + angle: 90, + ); + await game.ensureAdd(path); + + expect(path.body.bodyType, equals(BodyType.static)); + }, + ); + }); + }); + + group('bezier curve', () { + final controlPoints = [ + Vector2(0, 0), + Vector2(50, 0), + Vector2(0, 50), + Vector2(50, 50), + ]; + + flameTester.test( + 'loads correctly', + (game) async { + final path = Path.bezierCurve( + position: Vector2.zero(), + controlPoints: controlPoints, + pathWidth: pathWidth, + ); + await game.ensureAdd(path); + expect(game.contains(path), isTrue); + }, + ); + + group('body', () { + flameTester.test( + 'positions correctly', + (game) async { + final position = Vector2.all(10); + final path = Path.bezierCurve( + position: position, + controlPoints: controlPoints, + pathWidth: pathWidth, + ); + await game.ensureAdd(path); + game.contains(path); + + expect(path.body.position, position); + }, + ); + + flameTester.test( + 'is static', + (game) async { + final path = Path.bezierCurve( + position: Vector2.zero(), + controlPoints: controlPoints, + pathWidth: pathWidth, + ); + await game.ensureAdd(path); + + expect(path.body.bodyType, equals(BodyType.static)); + }, + ); + }); + }); + }); +} From c693e9a10910c0dc8151077406da3c1d2ce9ca07 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 12:52:58 +0100 Subject: [PATCH 05/32] chore: analysis errors fixed on tests --- test/game/components/path_test.dart | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/game/components/path_test.dart b/test/game/components/path_test.dart index 440070b6..a97ba866 100644 --- a/test/game/components/path_test.dart +++ b/test/game/components/path_test.dart @@ -1,8 +1,4 @@ // ignore_for_file: cascade_invocations - -import 'dart:math'; -import 'dart:ui'; - import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/material.dart'; @@ -133,7 +129,7 @@ void main() { await game.ensureAdd(path); expect(path.body.fixtures.length, 2); - for (var fixture in path.body.fixtures) { + for (final fixture in path.body.fixtures) { expect(fixture, isA()); expect(fixture.shape.shapeType, equals(ShapeType.chain)); } From aeb289fce66e5d68dcb33033d3600e9f674b0575 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 13:04:28 +0100 Subject: [PATCH 06/32] fix: changed paths area default color to transparent --- lib/game/components/path.dart | 8 +++----- test/game/components/path_test.dart | 9 ++++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/game/components/path.dart b/lib/game/components/path.dart index 0d7105d7..66dad004 100644 --- a/lib/game/components/path.dart +++ b/lib/game/components/path.dart @@ -10,11 +10,9 @@ class Path extends BodyComponent { required List> paths, }) : _position = position, _paths = paths { - if (color != null) { - paint = Paint() - ..color = color - ..style = PaintingStyle.stroke; - } + paint = Paint() + ..color = color ?? const Color.fromARGB(0, 0, 0, 0) + ..style = PaintingStyle.stroke; } factory Path.straight({ diff --git a/test/game/components/path_test.dart b/test/game/components/path_test.dart index a97ba866..9564c21c 100644 --- a/test/game/components/path_test.dart +++ b/test/game/components/path_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: cascade_invocations +// ignore_for_file: cascade_invocations, prefer_const_constructors import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/material.dart'; @@ -15,7 +15,7 @@ void main() { group('straight', () { group('color', () { flameTester.test( - 'has white color by default if not specified', + 'has transparent color by default if not specified', (game) async { final path = Path.straight( position: Vector2.zero(), @@ -26,7 +26,10 @@ void main() { await game.ensureAdd(path); expect(game.contains(path), isTrue); expect(path.paint, isNotNull); - expect(path.paint.color, equals(Colors.white)); + expect( + path.paint.color, + equals(Color.fromARGB(0, 0, 0, 0)), + ); }, ); flameTester.test( From 9ccf85c2ef3bc060f4ac998c361a70807dabaace Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 13:27:13 +0100 Subject: [PATCH 07/32] doc: documented Path component --- lib/game/components/path.dart | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/game/components/path.dart b/lib/game/components/path.dart index 66dad004..69b16b20 100644 --- a/lib/game/components/path.dart +++ b/lib/game/components/path.dart @@ -3,6 +3,10 @@ import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/material.dart'; import 'package:maths/maths.dart'; +/// {@template path} +/// [Path] creates different shapes that sets the pathways that ball can follow +/// or collide to like walls. +/// {@endtemplate} class Path extends BodyComponent { Path._({ Color? color, @@ -15,6 +19,12 @@ class Path extends BodyComponent { ..style = PaintingStyle.stroke; } + /// {@macro path} + /// [Path.straight] creates a straight path for the ball given a [position] + /// for the body, between a [start] and [end] points. + /// It creates two [ChainShape] separated by a [pathWidth]. If [onlyOneWall] + /// is true, just one [ChainShape] is created (like a wall instead of a path) + /// The path could be rotated by [rotation] in degrees. factory Path.straight({ Color? color, required Vector2 position, @@ -46,6 +56,15 @@ class Path extends BodyComponent { ); } + /// {@macro path} + /// [Path.straight] creates an arc path for the ball given a [position] + /// for the body, a [radius] for the circumference and an [angle] to specify + /// the size of the semi circumference. + /// It creates two [ChainShape] separated by a [pathWidth], like a circular + /// crown. The specified [radius] is for the outer arc, the inner one will + /// have a radius of radius-pathWidth. + /// If [onlyOneWall] is true, just one [ChainShape] is created. + /// The path could be rotated by [rotation] in degrees. factory Path.arc({ Color? color, required Vector2 position, @@ -84,6 +103,14 @@ class Path extends BodyComponent { ); } + /// {@macro path} + /// [Path.straight] creates a bezier curve path for the ball given a + /// [position] for the body, with control point specified by [controlPoints]. + /// First and last points set the beginning and end of the curve, all the + /// inner points between them set the bezier curve final shape. + /// It creates two [ChainShape] separated by a [pathWidth]. If [onlyOneWall] + /// is true, just one [ChainShape] is created (like a wall instead of a path) + /// The path could be rotated by [rotation] in degrees. factory Path.bezierCurve({ Color? color, required Vector2 position, From 8f6bad7a6f7cfc73ba1cbc5bba7984cff4292903 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 14:12:33 +0100 Subject: [PATCH 08/32] test: tests for maths methods --- packages/maths/lib/src/maths.dart | 18 +++-- packages/maths/pubspec.yaml | 10 ++- packages/maths/test/src/maths_test.dart | 87 +++++++++++++++++++++++-- 3 files changed, 101 insertions(+), 14 deletions(-) diff --git a/packages/maths/lib/src/maths.dart b/packages/maths/lib/src/maths.dart index ea447db5..bdb31d43 100644 --- a/packages/maths/lib/src/maths.dart +++ b/packages/maths/lib/src/maths.dart @@ -3,6 +3,7 @@ import 'package:flame/extensions.dart'; /// Method to calculate all points (with a required precision amount of them) /// of a circumference based on angle, offsetAngle and radius +/// https://en.wikipedia.org/wiki/Trigonometric_functions List calculateArc({ required Vector2 center, required double radius, @@ -47,9 +48,9 @@ List calculateBezierCurve({ final point = controlPoints[i]; xCoord += - _binomial(n, i) * math.pow(1 - t, n - i) * math.pow(t, i) * point.x; + binomial(n, i) * math.pow(1 - t, n - i) * math.pow(t, i) * point.x; yCoord += - _binomial(n, i) * math.pow(1 - t, n - i) * math.pow(t, i) * point.y; + binomial(n, i) * math.pow(1 - t, n - i) * math.pow(t, i) * point.y; } points.add(Vector2(xCoord, yCoord)); @@ -61,21 +62,24 @@ List calculateBezierCurve({ /// Method to calculate the binomial coefficient of 'n' and 'k' /// https://en.wikipedia.org/wiki/Binomial_coefficient -num _binomial(num n, num k) { +num binomial(num n, num k) { assert(0 <= k && k <= n, 'Range 0<=k<=n'); if (k == 0 || n == k) { return 1; } else { - return _factorial(n) / (_factorial(k) * _factorial(n - k)); + return factorial(n) / (factorial(k) * factorial(n - k)); } } /// Method to calculate the factorial of some number 'n' /// https://en.wikipedia.org/wiki/Factorial -num _factorial(num n) { - if (n == 1) { +num factorial(num n) { + assert(0 <= n, 'Non negative n'); + if (n == 0) { + return 1; + } else if (n == 1) { return 1; } else { - return n * _factorial(n - 1); + return n * factorial(n - 1); } } diff --git a/packages/maths/pubspec.yaml b/packages/maths/pubspec.yaml index 3b4b863c..b037f4a4 100644 --- a/packages/maths/pubspec.yaml +++ b/packages/maths/pubspec.yaml @@ -6,10 +6,14 @@ publish_to: none environment: sdk: ">=2.16.0 <3.0.0" +dependencies: + flame: ^1.0.0 + flutter: + sdk: flutter + dev_dependencies: - coverage: ^1.1.0 + flutter_test: + sdk: flutter mocktail: ^0.2.0 test: ^1.19.2 very_good_analysis: ^2.4.0 -dependencies: - flame: ^1.0.0 diff --git a/packages/maths/test/src/maths_test.dart b/packages/maths/test/src/maths_test.dart index dceedab8..8faa57cf 100644 --- a/packages/maths/test/src/maths_test.dart +++ b/packages/maths/test/src/maths_test.dart @@ -1,11 +1,90 @@ -// ignore_for_file: prefer_const_constructors +// ignore_for_file: prefer_const_constructors, cascade_invocations +import 'package:flutter_test/flutter_test.dart'; import 'package:maths/maths.dart'; -import 'package:test/test.dart'; + +class Binomial { + Binomial({required this.n, required this.k}); + + final num n; + final num k; +} void main() { group('Maths', () { - test('can be instantiated', () { - expect(Maths(), isNotNull); + group('calculateArc', () {}); + group('calculateBezierCurve', () {}); + + group('binomial', () { + test('fails if k is negative', () { + expect(() => binomial(1, -1), throwsAssertionError); + }); + test('fails if n is negative', () { + expect(() => binomial(-1, 1), throwsAssertionError); + }); + test('fails if n < k', () { + expect(() => binomial(1, 2), throwsAssertionError); + }); + test('for a specific input gives a correct value', () { + final binomialInputsToExpected = { + Binomial(n: 0, k: 0): 1, + Binomial(n: 1, k: 0): 1, + Binomial(n: 1, k: 1): 1, + Binomial(n: 2, k: 0): 1, + Binomial(n: 2, k: 1): 2, + Binomial(n: 2, k: 2): 1, + Binomial(n: 3, k: 0): 1, + Binomial(n: 3, k: 1): 3, + Binomial(n: 3, k: 2): 3, + Binomial(n: 3, k: 3): 1, + Binomial(n: 4, k: 0): 1, + Binomial(n: 4, k: 1): 4, + Binomial(n: 4, k: 2): 6, + Binomial(n: 4, k: 3): 4, + Binomial(n: 4, k: 4): 1, + Binomial(n: 5, k: 0): 1, + Binomial(n: 5, k: 1): 5, + Binomial(n: 5, k: 2): 10, + Binomial(n: 5, k: 3): 10, + Binomial(n: 5, k: 4): 5, + Binomial(n: 5, k: 5): 1, + Binomial(n: 6, k: 0): 1, + Binomial(n: 6, k: 1): 6, + Binomial(n: 6, k: 2): 15, + Binomial(n: 6, k: 3): 20, + Binomial(n: 6, k: 4): 15, + Binomial(n: 6, k: 5): 6, + Binomial(n: 6, k: 6): 1, + }; + binomialInputsToExpected.forEach((input, value) { + expect(binomial(input.n, input.k), value); + }); + }); + }); + group('factorial', () { + test('fails if negative number', () { + expect(() => factorial(-1), throwsAssertionError); + }); + test('for a specific input gives a correct value', () { + final factorialInputsToExpected = { + 0: 1, + 1: 1, + 2: 2, + 3: 6, + 4: 24, + 5: 120, + 6: 720, + 7: 5040, + 8: 40320, + 9: 362880, + 10: 3628800, + 11: 39916800, + 12: 479001600, + 13: 6227020800, + }; + factorialInputsToExpected.forEach((input, expected) { + expect(factorial(input), expected); + }); + }); }); }); } From 71033266caa9817a1adc6df50279e494966d9093 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 14:25:21 +0100 Subject: [PATCH 09/32] fix: fixed precision value for calculateArc method --- packages/maths/lib/src/maths.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/maths/lib/src/maths.dart b/packages/maths/lib/src/maths.dart index bdb31d43..e37f8132 100644 --- a/packages/maths/lib/src/maths.dart +++ b/packages/maths/lib/src/maths.dart @@ -11,11 +11,11 @@ List calculateArc({ double offsetAngle = 0, int precision = 100, }) { - final stepAngle = radians(angle / precision); + final stepAngle = radians(angle / (precision - 1)); final stepOffset = radians(offsetAngle); final points = []; - for (var i = 0; i <= precision; i++) { + for (var i = 0; i < precision; i++) { final xCoord = center.x + radius * math.cos((stepAngle * i) + stepOffset); final yCoord = center.y - radius * math.sin((stepAngle * i) + stepOffset); From 53a22f1f1de320eb12ee014303f40a2cd4fc65b6 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 14:32:36 +0100 Subject: [PATCH 10/32] fix: added assert to maths --- packages/maths/lib/src/maths.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/maths/lib/src/maths.dart b/packages/maths/lib/src/maths.dart index e37f8132..d0912418 100644 --- a/packages/maths/lib/src/maths.dart +++ b/packages/maths/lib/src/maths.dart @@ -33,6 +33,7 @@ List calculateBezierCurve({ required List controlPoints, double step = 0.001, }) { + assert(0 <= step && step <= 1, 'Range 0<=step<=1'); assert( controlPoints.length >= 2, 'At least 2 control points to create a bezier curve', From ad8499d173783b8c7883b201ac7801cf7c336de2 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 14:33:01 +0100 Subject: [PATCH 11/32] test: coverage for maths methods --- packages/maths/test/src/maths_test.dart | 67 ++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/packages/maths/test/src/maths_test.dart b/packages/maths/test/src/maths_test.dart index 8faa57cf..2e164989 100644 --- a/packages/maths/test/src/maths_test.dart +++ b/packages/maths/test/src/maths_test.dart @@ -1,4 +1,5 @@ // ignore_for_file: prefer_const_constructors, cascade_invocations +import 'package:flame/extensions.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:maths/maths.dart'; @@ -11,8 +12,70 @@ class Binomial { void main() { group('Maths', () { - group('calculateArc', () {}); - group('calculateBezierCurve', () {}); + group('calculateArc', () { + test('it returns by default 100 points as indicated by precision', () { + final points = calculateArc( + center: Vector2.zero(), + radius: 100, + angle: 90, + ); + expect(points.length, 100); + }); + test('it returns as many points as indicated by precision', () { + final points = calculateArc( + center: Vector2.zero(), + radius: 100, + angle: 90, + precision: 50, + ); + expect(points.length, 50); + }); + }); + group('calculateBezierCurve', () { + test('fails if step not in range', () { + expect( + () => calculateBezierCurve( + controlPoints: [ + Vector2(0, 0), + Vector2(10, 10), + ], + step: 2, + ), + throwsAssertionError, + ); + }); + test('fails if not enough control points', () { + expect( + () => calculateBezierCurve(controlPoints: [Vector2.zero()]), + throwsAssertionError, + ); + expect( + () => calculateBezierCurve(controlPoints: []), + throwsAssertionError, + ); + }); + + test('it returns by default 1000 points as indicated by step', () { + final points = calculateBezierCurve( + controlPoints: [ + Vector2(0, 0), + Vector2(10, 10), + ], + ); + expect(points.length, 1000); + }); + + test('it returns as many points as indicated by step', () { + final points = calculateBezierCurve( + controlPoints: [ + Vector2(0, 0), + Vector2(10, 10), + ], + step: 0.01, + ); + expect(points.length, 100); + }); + }); group('binomial', () { test('fails if k is negative', () { From d6f7bb853d3b7a0c31d4f4fd509554805c199614 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 16:19:18 +0100 Subject: [PATCH 12/32] test: names for tests --- test/game/components/path_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/game/components/path_test.dart b/test/game/components/path_test.dart index 9564c21c..73a7bcfb 100644 --- a/test/game/components/path_test.dart +++ b/test/game/components/path_test.dart @@ -109,7 +109,7 @@ void main() { start: Vector2(10, 10), end: Vector2(20, 20), pathWidth: pathWidth, - onlyOneWall: true, + singleWall: true, ); await game.ensureAdd(path); From 8901bcec70cebd819fc11e0d6165108c402b218b Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 16:20:01 +0100 Subject: [PATCH 13/32] chore: var names and lambda method for identity element --- lib/game/components/path.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/game/components/path.dart b/lib/game/components/path.dart index 69b16b20..d5263024 100644 --- a/lib/game/components/path.dart +++ b/lib/game/components/path.dart @@ -32,7 +32,7 @@ class Path extends BodyComponent { required Vector2 end, required double pathWidth, double rotation = 0, - bool onlyOneWall = false, + bool singleWall = false, }) { final paths = >[]; final wall1 = [ @@ -41,7 +41,7 @@ class Path extends BodyComponent { ]; paths.add(wall1.map((e) => e..rotate(radians(rotation))).toList()); - if (!onlyOneWall) { + if (!singleWall) { final wall2 = [ start + Vector2(pathWidth, 0), end + Vector2(pathWidth, 0), @@ -72,7 +72,7 @@ class Path extends BodyComponent { required double radius, required double angle, double rotation = 0, - bool onlyOneWall = false, + bool singleWall = false, }) { final paths = >[]; @@ -84,7 +84,7 @@ class Path extends BodyComponent { ); paths.add(wall1); - if (!onlyOneWall) { + if (!singleWall) { final minRadius = radius - pathWidth; final wall2 = calculateArc( @@ -117,7 +117,7 @@ class Path extends BodyComponent { required List controlPoints, required double pathWidth, double rotation = 0, - bool onlyOneWall = false, + bool singleWall = false, }) { final paths = >[]; @@ -125,7 +125,7 @@ class Path extends BodyComponent { paths.add(wall1.map((e) => e..rotate(radians(rotation))).toList()); var wall2 = []; - if (!onlyOneWall) { + if (!singleWall) { wall2 = calculateBezierCurve( controlPoints: controlPoints .map((e) => e + Vector2(pathWidth, -pathWidth)) @@ -155,7 +155,7 @@ class Path extends BodyComponent { for (final path in _paths) { final chain = ChainShape() ..createChain( - path.map((e) => gameRef.screenToWorld(e)).toList(), + path.map(gameRef.screenToWorld).toList(), ); final fixtureDef = FixtureDef(chain); body.createFixture(fixtureDef); From 20c81533638e1e8b2a833ca079fd6ca52fdd4bfa Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 16:23:21 +0100 Subject: [PATCH 14/32] chore: var and test names not saved --- lib/game/components/path.dart | 6 +++--- packages/maths/test/src/maths_test.dart | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/game/components/path.dart b/lib/game/components/path.dart index d5263024..6f5c786e 100644 --- a/lib/game/components/path.dart +++ b/lib/game/components/path.dart @@ -22,7 +22,7 @@ class Path extends BodyComponent { /// {@macro path} /// [Path.straight] creates a straight path for the ball given a [position] /// for the body, between a [start] and [end] points. - /// It creates two [ChainShape] separated by a [pathWidth]. If [onlyOneWall] + /// It creates two [ChainShape] separated by a [pathWidth]. If [singleWall] /// is true, just one [ChainShape] is created (like a wall instead of a path) /// The path could be rotated by [rotation] in degrees. factory Path.straight({ @@ -63,7 +63,7 @@ class Path extends BodyComponent { /// It creates two [ChainShape] separated by a [pathWidth], like a circular /// crown. The specified [radius] is for the outer arc, the inner one will /// have a radius of radius-pathWidth. - /// If [onlyOneWall] is true, just one [ChainShape] is created. + /// If [singleWall] is true, just one [ChainShape] is created. /// The path could be rotated by [rotation] in degrees. factory Path.arc({ Color? color, @@ -108,7 +108,7 @@ class Path extends BodyComponent { /// [position] for the body, with control point specified by [controlPoints]. /// First and last points set the beginning and end of the curve, all the /// inner points between them set the bezier curve final shape. - /// It creates two [ChainShape] separated by a [pathWidth]. If [onlyOneWall] + /// It creates two [ChainShape] separated by a [pathWidth]. If [singleWall] /// is true, just one [ChainShape] is created (like a wall instead of a path) /// The path could be rotated by [rotation] in degrees. factory Path.bezierCurve({ diff --git a/packages/maths/test/src/maths_test.dart b/packages/maths/test/src/maths_test.dart index 2e164989..eaaf9367 100644 --- a/packages/maths/test/src/maths_test.dart +++ b/packages/maths/test/src/maths_test.dart @@ -13,7 +13,7 @@ class Binomial { void main() { group('Maths', () { group('calculateArc', () { - test('it returns by default 100 points as indicated by precision', () { + test('returns by default 100 points as indicated by precision', () { final points = calculateArc( center: Vector2.zero(), radius: 100, @@ -21,7 +21,7 @@ void main() { ); expect(points.length, 100); }); - test('it returns as many points as indicated by precision', () { + test('returns as many points as indicated by precision', () { final points = calculateArc( center: Vector2.zero(), radius: 100, @@ -55,7 +55,7 @@ void main() { ); }); - test('it returns by default 1000 points as indicated by step', () { + test('returns by default 1000 points as indicated by step', () { final points = calculateBezierCurve( controlPoints: [ Vector2(0, 0), @@ -65,7 +65,7 @@ void main() { expect(points.length, 1000); }); - test('it returns as many points as indicated by step', () { + test('returns as many points as indicated by step', () { final points = calculateBezierCurve( controlPoints: [ Vector2(0, 0), From 7e43e0042e748da82f85208084eb238693ebe40a Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Mon, 7 Mar 2022 16:40:01 +0100 Subject: [PATCH 15/32] refactor: changed name of Path to Pathway --- lib/game/components/components.dart | 2 +- .../components/{path.dart => pathway.dart} | 70 +++++----- test/game/components/path_test.dart | 126 +++++++++--------- 3 files changed, 100 insertions(+), 98 deletions(-) rename lib/game/components/{path.dart => pathway.dart} (66%) diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index 81e8cad2..a5989fd5 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -1,6 +1,6 @@ export 'anchor.dart'; export 'ball.dart'; -export 'path.dart'; +export 'pathway.dart'; export 'plunger.dart'; export 'score_points.dart'; export 'wall.dart'; diff --git a/lib/game/components/path.dart b/lib/game/components/pathway.dart similarity index 66% rename from lib/game/components/path.dart rename to lib/game/components/pathway.dart index 6f5c786e..75656e9e 100644 --- a/lib/game/components/path.dart +++ b/lib/game/components/pathway.dart @@ -3,12 +3,12 @@ import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/material.dart'; import 'package:maths/maths.dart'; -/// {@template path} -/// [Path] creates different shapes that sets the pathways that ball can follow -/// or collide to like walls. +/// {@template pathway} +/// [Pathway] creates different shapes that sets the pathwayways that ball +/// can follow or collide to like walls. /// {@endtemplate} -class Path extends BodyComponent { - Path._({ +class Pathway extends BodyComponent { + Pathway._({ Color? color, required Vector2 position, required List> paths, @@ -19,18 +19,19 @@ class Path extends BodyComponent { ..style = PaintingStyle.stroke; } - /// {@macro path} - /// [Path.straight] creates a straight path for the ball given a [position] - /// for the body, between a [start] and [end] points. - /// It creates two [ChainShape] separated by a [pathWidth]. If [singleWall] - /// is true, just one [ChainShape] is created (like a wall instead of a path) - /// The path could be rotated by [rotation] in degrees. - factory Path.straight({ + /// {@macro pathway} + /// [Pathway.straight] creates a straight pathway for the ball given + /// a [position] for the body, between a [start] and [end] points. + /// It creates two [ChainShape] separated by a [pathwayWidth]. If [singleWall] + /// is true, just one [ChainShape] is created + /// (like a wall instead of a pathway) + /// The pathway could be rotated by [rotation] in degrees. + factory Pathway.straight({ Color? color, required Vector2 position, required Vector2 start, required Vector2 end, - required double pathWidth, + required double pathwayWidth, double rotation = 0, bool singleWall = false, }) { @@ -43,32 +44,32 @@ class Path extends BodyComponent { if (!singleWall) { final wall2 = [ - start + Vector2(pathWidth, 0), - end + Vector2(pathWidth, 0), + start + Vector2(pathwayWidth, 0), + end + Vector2(pathwayWidth, 0), ]; paths.add(wall2.map((e) => e..rotate(radians(rotation))).toList()); } - return Path._( + return Pathway._( color: color, position: position, paths: paths, ); } - /// {@macro path} - /// [Path.straight] creates an arc path for the ball given a [position] + /// {@macro pathway} + /// [Pathway.straight] creates an arc pathway for the ball given a [position] /// for the body, a [radius] for the circumference and an [angle] to specify /// the size of the semi circumference. - /// It creates two [ChainShape] separated by a [pathWidth], like a circular + /// It creates two [ChainShape] separated by a [pathwayWidth], like a circular /// crown. The specified [radius] is for the outer arc, the inner one will - /// have a radius of radius-pathWidth. + /// have a radius of radius-pathwayWidth. /// If [singleWall] is true, just one [ChainShape] is created. - /// The path could be rotated by [rotation] in degrees. - factory Path.arc({ + /// The pathway could be rotated by [rotation] in degrees. + factory Pathway.arc({ Color? color, required Vector2 position, - required double pathWidth, + required double pathwayWidth, required double radius, required double angle, double rotation = 0, @@ -85,7 +86,7 @@ class Path extends BodyComponent { paths.add(wall1); if (!singleWall) { - final minRadius = radius - pathWidth; + final minRadius = radius - pathwayWidth; final wall2 = calculateArc( center: position, @@ -96,26 +97,27 @@ class Path extends BodyComponent { paths.add(wall2); } - return Path._( + return Pathway._( color: color, position: position, paths: paths, ); } - /// {@macro path} - /// [Path.straight] creates a bezier curve path for the ball given a + /// {@macro pathway} + /// [Pathway.straight] creates a bezier curve pathway for the ball given a /// [position] for the body, with control point specified by [controlPoints]. /// First and last points set the beginning and end of the curve, all the /// inner points between them set the bezier curve final shape. - /// It creates two [ChainShape] separated by a [pathWidth]. If [singleWall] - /// is true, just one [ChainShape] is created (like a wall instead of a path) - /// The path could be rotated by [rotation] in degrees. - factory Path.bezierCurve({ + /// It creates two [ChainShape] separated by a [pathwayWidth]. If [singleWall] + /// is true, just one [ChainShape] is created + /// (like a wall instead of a pathway) + /// The pathway could be rotated by [rotation] in degrees. + factory Pathway.bezierCurve({ Color? color, required Vector2 position, required List controlPoints, - required double pathWidth, + required double pathwayWidth, double rotation = 0, bool singleWall = false, }) { @@ -128,13 +130,13 @@ class Path extends BodyComponent { if (!singleWall) { wall2 = calculateBezierCurve( controlPoints: controlPoints - .map((e) => e + Vector2(pathWidth, -pathWidth)) + .map((e) => e + Vector2(pathwayWidth, -pathwayWidth)) .toList(), ); paths.add(wall2.map((e) => e..rotate(radians(rotation))).toList()); } - return Path._( + return Pathway._( color: color, position: position, paths: paths, diff --git a/test/game/components/path_test.dart b/test/game/components/path_test.dart index 73a7bcfb..e1c10455 100644 --- a/test/game/components/path_test.dart +++ b/test/game/components/path_test.dart @@ -9,25 +9,25 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(PinballGame.new); - group('Path', () { - const pathWidth = 50.0; + group('Pathway', () { + const pathwayWidth = 50.0; group('straight', () { group('color', () { flameTester.test( 'has transparent color by default if not specified', (game) async { - final path = Path.straight( + final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), - pathWidth: pathWidth, + pathwayWidth: pathwayWidth, ); - await game.ensureAdd(path); - expect(game.contains(path), isTrue); - expect(path.paint, isNotNull); + await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); + expect(pathway.paint, isNotNull); expect( - path.paint.color, + pathway.paint.color, equals(Color.fromARGB(0, 0, 0, 0)), ); }, @@ -37,17 +37,17 @@ void main() { (game) async { const defaultColor = Colors.blue; - final path = Path.straight( + final pathway = Pathway.straight( color: defaultColor, position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), - pathWidth: pathWidth, + pathwayWidth: pathwayWidth, ); - await game.ensureAdd(path); - expect(game.contains(path), isTrue); - expect(path.paint, isNotNull); - expect(path.paint.color.value, equals(defaultColor.value)); + await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); + expect(pathway.paint, isNotNull); + expect(pathway.paint.color.value, equals(defaultColor.value)); }, ); }); @@ -55,14 +55,14 @@ void main() { flameTester.test( 'loads correctly', (game) async { - final path = Path.straight( + final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), - pathWidth: pathWidth, + pathwayWidth: pathwayWidth, ); - await game.ensureAdd(path); - expect(game.contains(path), isTrue); + await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); }, ); @@ -71,31 +71,31 @@ void main() { 'positions correctly', (game) async { final position = Vector2.all(10); - final path = Path.straight( + final pathway = Pathway.straight( position: position, start: Vector2(10, 10), end: Vector2(20, 20), - pathWidth: pathWidth, + pathwayWidth: pathwayWidth, ); - await game.ensureAdd(path); - game.contains(path); + await game.ensureAdd(pathway); + game.contains(pathway); - expect(path.body.position, position); + expect(pathway.body.position, position); }, ); flameTester.test( 'is static', (game) async { - final path = Path.straight( + final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), - pathWidth: pathWidth, + pathwayWidth: pathwayWidth, ); - await game.ensureAdd(path); + await game.ensureAdd(pathway); - expect(path.body.bodyType, equals(BodyType.static)); + expect(pathway.body.bodyType, equals(BodyType.static)); }, ); }); @@ -104,17 +104,17 @@ void main() { flameTester.test( 'exists only one ChainShape if just one wall', (game) async { - final path = Path.straight( + final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), - pathWidth: pathWidth, + pathwayWidth: pathwayWidth, singleWall: true, ); - await game.ensureAdd(path); + await game.ensureAdd(pathway); - expect(path.body.fixtures.length, 1); - final fixture = path.body.fixtures[0]; + expect(pathway.body.fixtures.length, 1); + final fixture = pathway.body.fixtures[0]; expect(fixture, isA()); expect(fixture.shape.shapeType, equals(ShapeType.chain)); }, @@ -123,16 +123,16 @@ void main() { flameTester.test( 'exists two ChainShape if there is by default two walls', (game) async { - final path = Path.straight( + final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), - pathWidth: pathWidth, + pathwayWidth: pathwayWidth, ); - await game.ensureAdd(path); + await game.ensureAdd(pathway); - expect(path.body.fixtures.length, 2); - for (final fixture in path.body.fixtures) { + expect(pathway.body.fixtures.length, 2); + for (final fixture in pathway.body.fixtures) { expect(fixture, isA()); expect(fixture.shape.shapeType, equals(ShapeType.chain)); } @@ -145,14 +145,14 @@ void main() { flameTester.test( 'loads correctly', (game) async { - final path = Path.arc( + final pathway = Pathway.arc( position: Vector2.zero(), - pathWidth: pathWidth, + pathwayWidth: pathwayWidth, radius: 100, angle: 90, ); - await game.ensureAdd(path); - expect(game.contains(path), isTrue); + await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); }, ); @@ -161,31 +161,31 @@ void main() { 'positions correctly', (game) async { final position = Vector2.all(10); - final path = Path.arc( + final pathway = Pathway.arc( position: position, - pathWidth: pathWidth, + pathwayWidth: pathwayWidth, radius: 100, angle: 90, ); - await game.ensureAdd(path); - game.contains(path); + await game.ensureAdd(pathway); + game.contains(pathway); - expect(path.body.position, position); + expect(pathway.body.position, position); }, ); flameTester.test( 'is static', (game) async { - final path = Path.arc( + final pathway = Pathway.arc( position: Vector2.zero(), - pathWidth: pathWidth, + pathwayWidth: pathwayWidth, radius: 100, angle: 90, ); - await game.ensureAdd(path); + await game.ensureAdd(pathway); - expect(path.body.bodyType, equals(BodyType.static)); + expect(pathway.body.bodyType, equals(BodyType.static)); }, ); }); @@ -202,13 +202,13 @@ void main() { flameTester.test( 'loads correctly', (game) async { - final path = Path.bezierCurve( + final pathway = Pathway.bezierCurve( position: Vector2.zero(), controlPoints: controlPoints, - pathWidth: pathWidth, + pathwayWidth: pathwayWidth, ); - await game.ensureAdd(path); - expect(game.contains(path), isTrue); + await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); }, ); @@ -217,29 +217,29 @@ void main() { 'positions correctly', (game) async { final position = Vector2.all(10); - final path = Path.bezierCurve( + final pathway = Pathway.bezierCurve( position: position, controlPoints: controlPoints, - pathWidth: pathWidth, + pathwayWidth: pathwayWidth, ); - await game.ensureAdd(path); - game.contains(path); + await game.ensureAdd(pathway); + game.contains(pathway); - expect(path.body.position, position); + expect(pathway.body.position, position); }, ); flameTester.test( 'is static', (game) async { - final path = Path.bezierCurve( + final pathway = Pathway.bezierCurve( position: Vector2.zero(), controlPoints: controlPoints, - pathWidth: pathWidth, + pathwayWidth: pathwayWidth, ); - await game.ensureAdd(path); + await game.ensureAdd(pathway); - expect(path.body.bodyType, equals(BodyType.static)); + expect(pathway.body.bodyType, equals(BodyType.static)); }, ); }); From 631688f821998de9aa63d1f794dc06a8264e71dd Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Tue, 8 Mar 2022 11:26:57 +0100 Subject: [PATCH 16/32] refactor: path changed to pathway, as well as tests --- test/game/components/path_test.dart | 248 ---------------------------- 1 file changed, 248 deletions(-) delete mode 100644 test/game/components/path_test.dart diff --git a/test/game/components/path_test.dart b/test/game/components/path_test.dart deleted file mode 100644 index e1c10455..00000000 --- a/test/game/components/path_test.dart +++ /dev/null @@ -1,248 +0,0 @@ -// ignore_for_file: cascade_invocations, prefer_const_constructors -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flame_test/flame_test.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball/game/game.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(PinballGame.new); - - group('Pathway', () { - const pathwayWidth = 50.0; - - group('straight', () { - group('color', () { - flameTester.test( - 'has transparent color by default if not specified', - (game) async { - final pathway = Pathway.straight( - position: Vector2.zero(), - start: Vector2(10, 10), - end: Vector2(20, 20), - pathwayWidth: pathwayWidth, - ); - await game.ensureAdd(pathway); - expect(game.contains(pathway), isTrue); - expect(pathway.paint, isNotNull); - expect( - pathway.paint.color, - equals(Color.fromARGB(0, 0, 0, 0)), - ); - }, - ); - flameTester.test( - 'has a color if set', - (game) async { - const defaultColor = Colors.blue; - - final pathway = Pathway.straight( - color: defaultColor, - position: Vector2.zero(), - start: Vector2(10, 10), - end: Vector2(20, 20), - pathwayWidth: pathwayWidth, - ); - await game.ensureAdd(pathway); - expect(game.contains(pathway), isTrue); - expect(pathway.paint, isNotNull); - expect(pathway.paint.color.value, equals(defaultColor.value)); - }, - ); - }); - - flameTester.test( - 'loads correctly', - (game) async { - final pathway = Pathway.straight( - position: Vector2.zero(), - start: Vector2(10, 10), - end: Vector2(20, 20), - pathwayWidth: pathwayWidth, - ); - await game.ensureAdd(pathway); - expect(game.contains(pathway), isTrue); - }, - ); - - group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final pathway = Pathway.straight( - position: position, - start: Vector2(10, 10), - end: Vector2(20, 20), - pathwayWidth: pathwayWidth, - ); - await game.ensureAdd(pathway); - game.contains(pathway); - - expect(pathway.body.position, position); - }, - ); - - flameTester.test( - 'is static', - (game) async { - final pathway = Pathway.straight( - position: Vector2.zero(), - start: Vector2(10, 10), - end: Vector2(20, 20), - pathwayWidth: pathwayWidth, - ); - await game.ensureAdd(pathway); - - expect(pathway.body.bodyType, equals(BodyType.static)); - }, - ); - }); - - group('fixtures', () { - flameTester.test( - 'exists only one ChainShape if just one wall', - (game) async { - final pathway = Pathway.straight( - position: Vector2.zero(), - start: Vector2(10, 10), - end: Vector2(20, 20), - pathwayWidth: pathwayWidth, - singleWall: true, - ); - await game.ensureAdd(pathway); - - expect(pathway.body.fixtures.length, 1); - final fixture = pathway.body.fixtures[0]; - expect(fixture, isA()); - expect(fixture.shape.shapeType, equals(ShapeType.chain)); - }, - ); - - flameTester.test( - 'exists two ChainShape if there is by default two walls', - (game) async { - final pathway = Pathway.straight( - position: Vector2.zero(), - start: Vector2(10, 10), - end: Vector2(20, 20), - pathwayWidth: pathwayWidth, - ); - await game.ensureAdd(pathway); - - expect(pathway.body.fixtures.length, 2); - for (final fixture in pathway.body.fixtures) { - expect(fixture, isA()); - expect(fixture.shape.shapeType, equals(ShapeType.chain)); - } - }, - ); - }); - }); - - group('arc', () { - flameTester.test( - 'loads correctly', - (game) async { - final pathway = Pathway.arc( - position: Vector2.zero(), - pathwayWidth: pathwayWidth, - radius: 100, - angle: 90, - ); - await game.ensureAdd(pathway); - expect(game.contains(pathway), isTrue); - }, - ); - - group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final pathway = Pathway.arc( - position: position, - pathwayWidth: pathwayWidth, - radius: 100, - angle: 90, - ); - await game.ensureAdd(pathway); - game.contains(pathway); - - expect(pathway.body.position, position); - }, - ); - - flameTester.test( - 'is static', - (game) async { - final pathway = Pathway.arc( - position: Vector2.zero(), - pathwayWidth: pathwayWidth, - radius: 100, - angle: 90, - ); - await game.ensureAdd(pathway); - - expect(pathway.body.bodyType, equals(BodyType.static)); - }, - ); - }); - }); - - group('bezier curve', () { - final controlPoints = [ - Vector2(0, 0), - Vector2(50, 0), - Vector2(0, 50), - Vector2(50, 50), - ]; - - flameTester.test( - 'loads correctly', - (game) async { - final pathway = Pathway.bezierCurve( - position: Vector2.zero(), - controlPoints: controlPoints, - pathwayWidth: pathwayWidth, - ); - await game.ensureAdd(pathway); - expect(game.contains(pathway), isTrue); - }, - ); - - group('body', () { - flameTester.test( - 'positions correctly', - (game) async { - final position = Vector2.all(10); - final pathway = Pathway.bezierCurve( - position: position, - controlPoints: controlPoints, - pathwayWidth: pathwayWidth, - ); - await game.ensureAdd(pathway); - game.contains(pathway); - - expect(pathway.body.position, position); - }, - ); - - flameTester.test( - 'is static', - (game) async { - final pathway = Pathway.bezierCurve( - position: Vector2.zero(), - controlPoints: controlPoints, - pathwayWidth: pathwayWidth, - ); - await game.ensureAdd(pathway); - - expect(pathway.body.bodyType, equals(BodyType.static)); - }, - ); - }); - }); - }); -} From 4faf703648317810914a2238d0f9404565cc4a41 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Tue, 8 Mar 2022 11:28:18 +0100 Subject: [PATCH 17/32] chore: applied changes from GH comments, doc descriptions, var names, asserts, etc --- lib/game/components/pathway.dart | 59 +++--- test/game/components/pathway_test.dart | 248 +++++++++++++++++++++++++ 2 files changed, 280 insertions(+), 27 deletions(-) create mode 100644 test/game/components/pathway_test.dart diff --git a/lib/game/components/pathway.dart b/lib/game/components/pathway.dart index 75656e9e..13595cc9 100644 --- a/lib/game/components/pathway.dart +++ b/lib/game/components/pathway.dart @@ -4,11 +4,12 @@ import 'package:flutter/material.dart'; import 'package:maths/maths.dart'; /// {@template pathway} -/// [Pathway] creates different shapes that sets the pathwayways that ball -/// can follow or collide to like walls. +/// [Pathway] creates lines of various shapes that the [Ball] can collide +/// with and move along. /// {@endtemplate} class Pathway extends BodyComponent { Pathway._({ + // TODO(ruialonso): remove color when assets added. Color? color, required Vector2 position, required List> paths, @@ -20,10 +21,11 @@ class Pathway extends BodyComponent { } /// {@macro pathway} - /// [Pathway.straight] creates a straight pathway for the ball given - /// a [position] for the body, between a [start] and [end] points. - /// It creates two [ChainShape] separated by a [pathwayWidth]. If [singleWall] - /// is true, just one [ChainShape] is created + /// [Pathway.straight] creates a straight pathway for the ball. + /// + /// given a [position] for the body, between a [start] and [end] points. + /// It creates two [ChainShape] separated by a [pathwayWidth]. + /// If [singleWall] is true, just one [ChainShape] is created /// (like a wall instead of a pathway) /// The pathway could be rotated by [rotation] in degrees. factory Pathway.straight({ @@ -39,15 +41,15 @@ class Pathway extends BodyComponent { final wall1 = [ start.clone(), end.clone(), - ]; - paths.add(wall1.map((e) => e..rotate(radians(rotation))).toList()); + ].map((vector) => vector..rotate(radians(rotation))).toList(); + paths.add(wall1); if (!singleWall) { final wall2 = [ start + Vector2(pathwayWidth, 0), end + Vector2(pathwayWidth, 0), - ]; - paths.add(wall2.map((e) => e..rotate(radians(rotation))).toList()); + ].map((vector) => vector..rotate(radians(rotation))).toList(); + paths.add(wall2); } return Pathway._( @@ -58,9 +60,11 @@ class Pathway extends BodyComponent { } /// {@macro pathway} - /// [Pathway.straight] creates an arc pathway for the ball given a [position] - /// for the body, a [radius] for the circumference and an [angle] to specify - /// the size of the semi circumference. + /// [Pathway.arc] creates an arc pathway for the ball. + /// + /// The arc is created given a [position] for the body, a [radius] for the + /// circumference and an [angle] to specify the size of it (360 will return + /// a completed circumference and minor angles a semi circumference ). /// It creates two [ChainShape] separated by a [pathwayWidth], like a circular /// crown. The specified [radius] is for the outer arc, the inner one will /// have a radius of radius-pathwayWidth. @@ -86,11 +90,9 @@ class Pathway extends BodyComponent { paths.add(wall1); if (!singleWall) { - final minRadius = radius - pathwayWidth; - final wall2 = calculateArc( center: position, - radius: minRadius, + radius: radius - pathwayWidth, angle: angle, offsetAngle: rotation, ); @@ -105,12 +107,14 @@ class Pathway extends BodyComponent { } /// {@macro pathway} - /// [Pathway.straight] creates a bezier curve pathway for the ball given a - /// [position] for the body, with control point specified by [controlPoints]. + /// [Pathway.bezierCurve] creates a bezier curve pathway for the ball. + /// + /// The curve is created given a [position] for the body, and + /// with a list of control points specified by [controlPoints]. /// First and last points set the beginning and end of the curve, all the /// inner points between them set the bezier curve final shape. - /// It creates two [ChainShape] separated by a [pathwayWidth]. If [singleWall] - /// is true, just one [ChainShape] is created + /// It creates two [ChainShape] separated by a [pathwayWidth]. + /// If [singleWall] is true, just one [ChainShape] is created /// (like a wall instead of a pathway) /// The pathway could be rotated by [rotation] in degrees. factory Pathway.bezierCurve({ @@ -123,17 +127,18 @@ class Pathway extends BodyComponent { }) { final paths = >[]; - final wall1 = calculateBezierCurve(controlPoints: controlPoints); - paths.add(wall1.map((e) => e..rotate(radians(rotation))).toList()); + final wall1 = calculateBezierCurve(controlPoints: controlPoints) + .map((vector) => vector..rotate(radians(rotation))) + .toList(); + paths.add(wall1); - var wall2 = []; if (!singleWall) { - wall2 = calculateBezierCurve( + final wall2 = calculateBezierCurve( controlPoints: controlPoints - .map((e) => e + Vector2(pathwayWidth, -pathwayWidth)) + .map((vector) => vector + Vector2(pathwayWidth, -pathwayWidth)) .toList(), - ); - paths.add(wall2.map((e) => e..rotate(radians(rotation))).toList()); + ).map((vector) => vector..rotate(radians(rotation))).toList(); + paths.add(wall2); } return Pathway._( diff --git a/test/game/components/pathway_test.dart b/test/game/components/pathway_test.dart new file mode 100644 index 00000000..e1c10455 --- /dev/null +++ b/test/game/components/pathway_test.dart @@ -0,0 +1,248 @@ +// ignore_for_file: cascade_invocations, prefer_const_constructors +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/game/game.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(PinballGame.new); + + group('Pathway', () { + const pathwayWidth = 50.0; + + group('straight', () { + group('color', () { + flameTester.test( + 'has transparent color by default if not specified', + (game) async { + final pathway = Pathway.straight( + position: Vector2.zero(), + start: Vector2(10, 10), + end: Vector2(20, 20), + pathwayWidth: pathwayWidth, + ); + await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); + expect(pathway.paint, isNotNull); + expect( + pathway.paint.color, + equals(Color.fromARGB(0, 0, 0, 0)), + ); + }, + ); + flameTester.test( + 'has a color if set', + (game) async { + const defaultColor = Colors.blue; + + final pathway = Pathway.straight( + color: defaultColor, + position: Vector2.zero(), + start: Vector2(10, 10), + end: Vector2(20, 20), + pathwayWidth: pathwayWidth, + ); + await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); + expect(pathway.paint, isNotNull); + expect(pathway.paint.color.value, equals(defaultColor.value)); + }, + ); + }); + + flameTester.test( + 'loads correctly', + (game) async { + final pathway = Pathway.straight( + position: Vector2.zero(), + start: Vector2(10, 10), + end: Vector2(20, 20), + pathwayWidth: pathwayWidth, + ); + await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); + }, + ); + + group('body', () { + flameTester.test( + 'positions correctly', + (game) async { + final position = Vector2.all(10); + final pathway = Pathway.straight( + position: position, + start: Vector2(10, 10), + end: Vector2(20, 20), + pathwayWidth: pathwayWidth, + ); + await game.ensureAdd(pathway); + game.contains(pathway); + + expect(pathway.body.position, position); + }, + ); + + flameTester.test( + 'is static', + (game) async { + final pathway = Pathway.straight( + position: Vector2.zero(), + start: Vector2(10, 10), + end: Vector2(20, 20), + pathwayWidth: pathwayWidth, + ); + await game.ensureAdd(pathway); + + expect(pathway.body.bodyType, equals(BodyType.static)); + }, + ); + }); + + group('fixtures', () { + flameTester.test( + 'exists only one ChainShape if just one wall', + (game) async { + final pathway = Pathway.straight( + position: Vector2.zero(), + start: Vector2(10, 10), + end: Vector2(20, 20), + pathwayWidth: pathwayWidth, + singleWall: true, + ); + await game.ensureAdd(pathway); + + expect(pathway.body.fixtures.length, 1); + final fixture = pathway.body.fixtures[0]; + expect(fixture, isA()); + expect(fixture.shape.shapeType, equals(ShapeType.chain)); + }, + ); + + flameTester.test( + 'exists two ChainShape if there is by default two walls', + (game) async { + final pathway = Pathway.straight( + position: Vector2.zero(), + start: Vector2(10, 10), + end: Vector2(20, 20), + pathwayWidth: pathwayWidth, + ); + await game.ensureAdd(pathway); + + expect(pathway.body.fixtures.length, 2); + for (final fixture in pathway.body.fixtures) { + expect(fixture, isA()); + expect(fixture.shape.shapeType, equals(ShapeType.chain)); + } + }, + ); + }); + }); + + group('arc', () { + flameTester.test( + 'loads correctly', + (game) async { + final pathway = Pathway.arc( + position: Vector2.zero(), + pathwayWidth: pathwayWidth, + radius: 100, + angle: 90, + ); + await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); + }, + ); + + group('body', () { + flameTester.test( + 'positions correctly', + (game) async { + final position = Vector2.all(10); + final pathway = Pathway.arc( + position: position, + pathwayWidth: pathwayWidth, + radius: 100, + angle: 90, + ); + await game.ensureAdd(pathway); + game.contains(pathway); + + expect(pathway.body.position, position); + }, + ); + + flameTester.test( + 'is static', + (game) async { + final pathway = Pathway.arc( + position: Vector2.zero(), + pathwayWidth: pathwayWidth, + radius: 100, + angle: 90, + ); + await game.ensureAdd(pathway); + + expect(pathway.body.bodyType, equals(BodyType.static)); + }, + ); + }); + }); + + group('bezier curve', () { + final controlPoints = [ + Vector2(0, 0), + Vector2(50, 0), + Vector2(0, 50), + Vector2(50, 50), + ]; + + flameTester.test( + 'loads correctly', + (game) async { + final pathway = Pathway.bezierCurve( + position: Vector2.zero(), + controlPoints: controlPoints, + pathwayWidth: pathwayWidth, + ); + await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); + }, + ); + + group('body', () { + flameTester.test( + 'positions correctly', + (game) async { + final position = Vector2.all(10); + final pathway = Pathway.bezierCurve( + position: position, + controlPoints: controlPoints, + pathwayWidth: pathwayWidth, + ); + await game.ensureAdd(pathway); + game.contains(pathway); + + expect(pathway.body.position, position); + }, + ); + + flameTester.test( + 'is static', + (game) async { + final pathway = Pathway.bezierCurve( + position: Vector2.zero(), + controlPoints: controlPoints, + pathwayWidth: pathwayWidth, + ); + await game.ensureAdd(pathway); + + expect(pathway.body.bodyType, equals(BodyType.static)); + }, + ); + }); + }); + }); +} From 4290cb5a8d517c9ecafb9f8d8c5fbf34f46c8b6c Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Tue, 8 Mar 2022 11:37:08 +0100 Subject: [PATCH 18/32] refactor: changed math package to geometry --- lib/game/components/pathway.dart | 8 ++-- packages/{maths => geometry}/.gitignore | 0 packages/{maths => geometry}/README.md | 0 .../{maths => geometry}/analysis_options.yaml | 0 packages/geometry/lib/geometry.dart | 3 ++ .../lib/src/geometry.dart} | 46 +++++++++++++------ packages/{maths => geometry}/pubspec.yaml | 2 +- .../test/src/geometry_test.dart} | 9 +++- packages/maths/lib/maths.dart | 3 -- pubspec.lock | 14 +++--- pubspec.yaml | 4 +- 11 files changed, 56 insertions(+), 33 deletions(-) rename packages/{maths => geometry}/.gitignore (100%) rename packages/{maths => geometry}/README.md (100%) rename packages/{maths => geometry}/analysis_options.yaml (100%) create mode 100644 packages/geometry/lib/geometry.dart rename packages/{maths/lib/src/maths.dart => geometry/lib/src/geometry.dart} (53%) rename packages/{maths => geometry}/pubspec.yaml (95%) rename packages/{maths/test/src/maths_test.dart => geometry/test/src/geometry_test.dart} (98%) delete mode 100644 packages/maths/lib/maths.dart diff --git a/lib/game/components/pathway.dart b/lib/game/components/pathway.dart index 13595cc9..1fc48af1 100644 --- a/lib/game/components/pathway.dart +++ b/lib/game/components/pathway.dart @@ -1,7 +1,7 @@ import 'package:flame/extensions.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/material.dart'; -import 'package:maths/maths.dart'; +import 'package:geometry/geometry.dart'; /// {@template pathway} /// [Pathway] creates lines of various shapes that the [Ball] can collide @@ -21,7 +21,7 @@ class Pathway extends BodyComponent { } /// {@macro pathway} - /// [Pathway.straight] creates a straight pathway for the ball. + /// [Pathway.straight] creates a straight pathway for the [Ball]. /// /// given a [position] for the body, between a [start] and [end] points. /// It creates two [ChainShape] separated by a [pathwayWidth]. @@ -60,7 +60,7 @@ class Pathway extends BodyComponent { } /// {@macro pathway} - /// [Pathway.arc] creates an arc pathway for the ball. + /// [Pathway.arc] creates an arc pathway for the [Ball]. /// /// The arc is created given a [position] for the body, a [radius] for the /// circumference and an [angle] to specify the size of it (360 will return @@ -107,7 +107,7 @@ class Pathway extends BodyComponent { } /// {@macro pathway} - /// [Pathway.bezierCurve] creates a bezier curve pathway for the ball. + /// [Pathway.bezierCurve] creates a bezier curve pathway for the [Ball]. /// /// The curve is created given a [position] for the body, and /// with a list of control points specified by [controlPoints]. diff --git a/packages/maths/.gitignore b/packages/geometry/.gitignore similarity index 100% rename from packages/maths/.gitignore rename to packages/geometry/.gitignore diff --git a/packages/maths/README.md b/packages/geometry/README.md similarity index 100% rename from packages/maths/README.md rename to packages/geometry/README.md diff --git a/packages/maths/analysis_options.yaml b/packages/geometry/analysis_options.yaml similarity index 100% rename from packages/maths/analysis_options.yaml rename to packages/geometry/analysis_options.yaml diff --git a/packages/geometry/lib/geometry.dart b/packages/geometry/lib/geometry.dart new file mode 100644 index 00000000..2453ed05 --- /dev/null +++ b/packages/geometry/lib/geometry.dart @@ -0,0 +1,3 @@ +library geometry; + +export 'src/geometry.dart'; diff --git a/packages/maths/lib/src/maths.dart b/packages/geometry/lib/src/geometry.dart similarity index 53% rename from packages/maths/lib/src/maths.dart rename to packages/geometry/lib/src/geometry.dart index d0912418..c992e5a2 100644 --- a/packages/maths/lib/src/maths.dart +++ b/packages/geometry/lib/src/geometry.dart @@ -1,9 +1,15 @@ import 'dart:math' as math; import 'package:flame/extensions.dart'; -/// Method to calculate all points (with a required precision amount of them) -/// of a circumference based on angle, offsetAngle and radius -/// https://en.wikipedia.org/wiki/Trigonometric_functions +/// Calculates all [Vector2]s of a circumference. +/// +/// Circumference is created from a [center] and a [radius] +/// Also semi circumference could be created, specifying its [angle] in degrees +/// and the offset start angle [offsetAngle] for this semi circumference. +/// The higher the [precision], the more [Vector2]s will be calculated, +/// achieving a more rounded arc. +/// +/// For more information read: https://en.wikipedia.org/wiki/Trigonometric_functions. List calculateArc({ required Vector2 center, required double radius, @@ -26,18 +32,28 @@ List calculateArc({ return points; } -/// Method that calculates all points of a bezier curve of degree 'g' and -/// n=g-1 control points and range 0<=t<=1 -/// https://en.wikipedia.org/wiki/B%C3%A9zier_curve +/// Calculates all [Vector2]s of a bezier curve. +/// +/// A bezier curve of [controlPoints] that say how to create this curve. +/// First and last points specify the beginning and the end respectively +/// of the curve. The inner points specify the shape of the curve and +/// its turning points. +/// The [step] must be in range 0<=step<=1 and indicates the precision to +/// calculate the curve. +/// For more information read: https://en.wikipedia.org/wiki/B%C3%A9zier_curve List calculateBezierCurve({ required List controlPoints, double step = 0.001, }) { - assert(0 <= step && step <= 1, 'Range 0<=step<=1'); + assert( + 0 <= step && step <= 1, + 'Step ($step) must be in range 0 <= step <= 1', + ); assert( controlPoints.length >= 2, - 'At least 2 control points to create a bezier curve', + 'At least 2 control points needed to create a bezier curve', ); + var t = 0.0; final n = controlPoints.length - 1; final points = []; @@ -62,9 +78,10 @@ List calculateBezierCurve({ } /// Method to calculate the binomial coefficient of 'n' and 'k' -/// https://en.wikipedia.org/wiki/Binomial_coefficient +/// For more information read: https://en.wikipedia.org/wiki/Binomial_coefficient num binomial(num n, num k) { - assert(0 <= k && k <= n, 'Range 0<=k<=n'); + assert(0 <= k && k <= n, 'k ($k) and n ($n) must be in range 0 <= k <= n'); + if (k == 0 || n == k) { return 1; } else { @@ -73,12 +90,11 @@ num binomial(num n, num k) { } /// Method to calculate the factorial of some number 'n' -/// https://en.wikipedia.org/wiki/Factorial +/// For more information read: https://en.wikipedia.org/wiki/Factorial num factorial(num n) { - assert(0 <= n, 'Non negative n'); - if (n == 0) { - return 1; - } else if (n == 1) { + assert(n >= 0, 'Factorial is not defined for negative number n ($n)'); + + if (n == 0 || n == 1) { return 1; } else { return n * factorial(n - 1); diff --git a/packages/maths/pubspec.yaml b/packages/geometry/pubspec.yaml similarity index 95% rename from packages/maths/pubspec.yaml rename to packages/geometry/pubspec.yaml index b037f4a4..99c09cd0 100644 --- a/packages/maths/pubspec.yaml +++ b/packages/geometry/pubspec.yaml @@ -1,4 +1,4 @@ -name: maths +name: geometry description: A Very Good Project created by Very Good CLI. version: 1.0.0+1 publish_to: none diff --git a/packages/maths/test/src/maths_test.dart b/packages/geometry/test/src/geometry_test.dart similarity index 98% rename from packages/maths/test/src/maths_test.dart rename to packages/geometry/test/src/geometry_test.dart index eaaf9367..6ecf66c8 100644 --- a/packages/maths/test/src/maths_test.dart +++ b/packages/geometry/test/src/geometry_test.dart @@ -1,7 +1,7 @@ // ignore_for_file: prefer_const_constructors, cascade_invocations import 'package:flame/extensions.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:maths/maths.dart'; +import 'package:geometry/geometry.dart'; class Binomial { Binomial({required this.n, required this.k}); @@ -31,6 +31,7 @@ void main() { expect(points.length, 50); }); }); + group('calculateBezierCurve', () { test('fails if step not in range', () { expect( @@ -44,6 +45,7 @@ void main() { throwsAssertionError, ); }); + test('fails if not enough control points', () { expect( () => calculateBezierCurve(controlPoints: [Vector2.zero()]), @@ -81,12 +83,15 @@ void main() { test('fails if k is negative', () { expect(() => binomial(1, -1), throwsAssertionError); }); + test('fails if n is negative', () { expect(() => binomial(-1, 1), throwsAssertionError); }); + test('fails if n < k', () { expect(() => binomial(1, 2), throwsAssertionError); }); + test('for a specific input gives a correct value', () { final binomialInputsToExpected = { Binomial(n: 0, k: 0): 1, @@ -123,10 +128,12 @@ void main() { }); }); }); + group('factorial', () { test('fails if negative number', () { expect(() => factorial(-1), throwsAssertionError); }); + test('for a specific input gives a correct value', () { final factorialInputsToExpected = { 0: 1, diff --git a/packages/maths/lib/maths.dart b/packages/maths/lib/maths.dart deleted file mode 100644 index b340388b..00000000 --- a/packages/maths/lib/maths.dart +++ /dev/null @@ -1,3 +0,0 @@ -library maths; - -export 'src/maths.dart'; diff --git a/pubspec.lock b/pubspec.lock index db3b239a..5dee0c71 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -198,6 +198,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + geometry: + dependency: "direct main" + description: + path: "packages/geometry" + relative: true + source: path + version: "1.0.0+1" glob: dependency: transitive description: @@ -261,13 +268,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.3" - maths: - dependency: "direct main" - description: - path: "/Users/ruialonso/dev/flutter/googleIO22/pinball/packages/maths" - relative: false - source: path - version: "1.0.0+1" meta: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7b9c9c19..1d0c8ada 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,9 +17,9 @@ dependencies: flutter_bloc: ^8.0.1 flutter_localizations: sdk: flutter + geometry: + path: packages/geometry intl: ^0.17.0 - maths: - path: packages/maths dev_dependencies: bloc_test: ^9.0.2 From 178876b4fc8f5124f5370d8ca7a2844f311bbb44 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Tue, 8 Mar 2022 11:40:07 +0100 Subject: [PATCH 19/32] refactor: changed geometry package description --- packages/geometry/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/geometry/pubspec.yaml b/packages/geometry/pubspec.yaml index 99c09cd0..2678cdef 100644 --- a/packages/geometry/pubspec.yaml +++ b/packages/geometry/pubspec.yaml @@ -1,5 +1,5 @@ name: geometry -description: A Very Good Project created by Very Good CLI. +description: Helper package to calculate points of lines, arcs and curves for the pathways of the ball version: 1.0.0+1 publish_to: none From 6b3158b019cff4e9426c8a0325638843d0b1a58b Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Tue, 8 Mar 2022 11:41:45 +0100 Subject: [PATCH 20/32] chore: removed unscope Ball refs in doc --- lib/game/components/pathway.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/game/components/pathway.dart b/lib/game/components/pathway.dart index 1fc48af1..d6582a91 100644 --- a/lib/game/components/pathway.dart +++ b/lib/game/components/pathway.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:geometry/geometry.dart'; /// {@template pathway} -/// [Pathway] creates lines of various shapes that the [Ball] can collide +/// [Pathway] creates lines of various shapes that the ball can collide /// with and move along. /// {@endtemplate} class Pathway extends BodyComponent { @@ -21,7 +21,7 @@ class Pathway extends BodyComponent { } /// {@macro pathway} - /// [Pathway.straight] creates a straight pathway for the [Ball]. + /// [Pathway.straight] creates a straight pathway for the ball. /// /// given a [position] for the body, between a [start] and [end] points. /// It creates two [ChainShape] separated by a [pathwayWidth]. @@ -60,7 +60,7 @@ class Pathway extends BodyComponent { } /// {@macro pathway} - /// [Pathway.arc] creates an arc pathway for the [Ball]. + /// [Pathway.arc] creates an arc pathway for the ball. /// /// The arc is created given a [position] for the body, a [radius] for the /// circumference and an [angle] to specify the size of it (360 will return @@ -107,7 +107,7 @@ class Pathway extends BodyComponent { } /// {@macro pathway} - /// [Pathway.bezierCurve] creates a bezier curve pathway for the [Ball]. + /// [Pathway.bezierCurve] creates a bezier curve pathway for the ball. /// /// The curve is created given a [position] for the body, and /// with a list of control points specified by [controlPoints]. From bbab659b85613f8a11519317dd11549bbcb50137 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Wed, 9 Mar 2022 17:36:15 +0100 Subject: [PATCH 21/32] refactor: changed docs and use angles in radians instead of degrees --- lib/game/components/pathway.dart | 81 +++++++++++++------------ lib/game/pinball_game.dart | 1 - packages/geometry/lib/src/geometry.dart | 20 +++--- test/game/components/pathway_test.dart | 61 ++++++++++--------- 4 files changed, 86 insertions(+), 77 deletions(-) diff --git a/lib/game/components/pathway.dart b/lib/game/components/pathway.dart index d6582a91..3404f2d2 100644 --- a/lib/game/components/pathway.dart +++ b/lib/game/components/pathway.dart @@ -4,8 +4,9 @@ import 'package:flutter/material.dart'; import 'package:geometry/geometry.dart'; /// {@template pathway} -/// [Pathway] creates lines of various shapes that the ball can collide -/// with and move along. +/// [Pathway] creates lines of various shapes. +/// +/// [BodyComponent]s such as a Ball can collide and move along a [Pathway]. /// {@endtemplate} class Pathway extends BodyComponent { Pathway._({ @@ -20,20 +21,19 @@ class Pathway extends BodyComponent { ..style = PaintingStyle.stroke; } - /// {@macro pathway} - /// [Pathway.straight] creates a straight pathway for the ball. + /// Creates a uniform unidirectional (straight) [Pathway]. /// - /// given a [position] for the body, between a [start] and [end] points. - /// It creates two [ChainShape] separated by a [pathwayWidth]. - /// If [singleWall] is true, just one [ChainShape] is created - /// (like a wall instead of a pathway) - /// The pathway could be rotated by [rotation] in degrees. + /// Does so with two [ChainShape] separated by a [width]. Placed + /// at a [position] between [start] and [end] points. Can + /// be rotated by a given [rotation] in radians. + /// + /// If [singleWall] is true, just one [ChainShape] is created. factory Pathway.straight({ Color? color, required Vector2 position, required Vector2 start, required Vector2 end, - required double pathwayWidth, + required double width, double rotation = 0, bool singleWall = false, }) { @@ -41,14 +41,14 @@ class Pathway extends BodyComponent { final wall1 = [ start.clone(), end.clone(), - ].map((vector) => vector..rotate(radians(rotation))).toList(); + ].map((vector) => vector..rotate(rotation)).toList(); paths.add(wall1); if (!singleWall) { final wall2 = [ - start + Vector2(pathwayWidth, 0), - end + Vector2(pathwayWidth, 0), - ].map((vector) => vector..rotate(radians(rotation))).toList(); + start + Vector2(width, 0), + end + Vector2(width, 0), + ].map((vector) => vector..rotate(rotation)).toList(); paths.add(wall2); } @@ -59,21 +59,24 @@ class Pathway extends BodyComponent { ); } - /// {@macro pathway} - /// [Pathway.arc] creates an arc pathway for the ball. + /// Creates an arc [Pathway]. + /// + /// The [angle], in radians, specifies the size of the arc. For example, 2*pi + /// returns a complete circumference and minor angles a semi circumference. + /// + /// The center of the arc is placed at [position]. + /// + /// Does so with two [ChainShape] separated by a [width]. Which can be + /// rotated by a given [rotation] in radians. + /// + /// The outer radius is specified by [radius], whilst the inner one is + /// equivalent to [radius] - [width]. /// - /// The arc is created given a [position] for the body, a [radius] for the - /// circumference and an [angle] to specify the size of it (360 will return - /// a completed circumference and minor angles a semi circumference ). - /// It creates two [ChainShape] separated by a [pathwayWidth], like a circular - /// crown. The specified [radius] is for the outer arc, the inner one will - /// have a radius of radius-pathwayWidth. /// If [singleWall] is true, just one [ChainShape] is created. - /// The pathway could be rotated by [rotation] in degrees. factory Pathway.arc({ Color? color, required Vector2 position, - required double pathwayWidth, + required double width, required double radius, required double angle, double rotation = 0, @@ -92,7 +95,7 @@ class Pathway extends BodyComponent { if (!singleWall) { final wall2 = calculateArc( center: position, - radius: radius - pathwayWidth, + radius: radius - width, angle: angle, offsetAngle: rotation, ); @@ -106,38 +109,36 @@ class Pathway extends BodyComponent { ); } - /// {@macro pathway} - /// [Pathway.bezierCurve] creates a bezier curve pathway for the ball. + /// Creates a bezier curve [Pathway]. /// - /// The curve is created given a [position] for the body, and - /// with a list of control points specified by [controlPoints]. - /// First and last points set the beginning and end of the curve, all the - /// inner points between them set the bezier curve final shape. - /// It creates two [ChainShape] separated by a [pathwayWidth]. - /// If [singleWall] is true, just one [ChainShape] is created - /// (like a wall instead of a pathway) - /// The pathway could be rotated by [rotation] in degrees. + /// Does so with two [ChainShape] separated by a [width]. Which can be + /// rotated by a given [rotation] in radians. + /// + /// First and last [controlPoints] set the beginning and end of the curve, + /// inner points between them set its final shape. + /// + /// If [singleWall] is true, just one [ChainShape] is created. factory Pathway.bezierCurve({ Color? color, required Vector2 position, required List controlPoints, - required double pathwayWidth, + required double width, double rotation = 0, bool singleWall = false, }) { final paths = >[]; final wall1 = calculateBezierCurve(controlPoints: controlPoints) - .map((vector) => vector..rotate(radians(rotation))) + .map((vector) => vector..rotate(rotation)) .toList(); paths.add(wall1); if (!singleWall) { final wall2 = calculateBezierCurve( controlPoints: controlPoints - .map((vector) => vector + Vector2(pathwayWidth, -pathwayWidth)) + .map((vector) => vector + Vector2(width, -width)) .toList(), - ).map((vector) => vector..rotate(radians(rotation))).toList(); + ).map((vector) => vector..rotate(rotation)).toList(); paths.add(wall2); } @@ -158,13 +159,13 @@ class Pathway extends BodyComponent { ..position = _position; final body = world.createBody(bodyDef); - for (final path in _paths) { final chain = ChainShape() ..createChain( path.map(gameRef.screenToWorld).toList(), ); final fixtureDef = FixtureDef(chain); + body.createFixture(fixtureDef); } diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 7c701c09..6fe6e571 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -1,5 +1,4 @@ import 'dart:async'; - import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/game.dart'; diff --git a/packages/geometry/lib/src/geometry.dart b/packages/geometry/lib/src/geometry.dart index c992e5a2..8851ed25 100644 --- a/packages/geometry/lib/src/geometry.dart +++ b/packages/geometry/lib/src/geometry.dart @@ -4,7 +4,7 @@ import 'package:flame/extensions.dart'; /// Calculates all [Vector2]s of a circumference. /// /// Circumference is created from a [center] and a [radius] -/// Also semi circumference could be created, specifying its [angle] in degrees +/// Also semi circumference could be created, specifying its [angle] in radians /// and the offset start angle [offsetAngle] for this semi circumference. /// The higher the [precision], the more [Vector2]s will be calculated, /// achieving a more rounded arc. @@ -17,13 +17,12 @@ List calculateArc({ double offsetAngle = 0, int precision = 100, }) { - final stepAngle = radians(angle / (precision - 1)); - final stepOffset = radians(offsetAngle); + final stepAngle = angle / (precision - 1); final points = []; for (var i = 0; i < precision; i++) { - final xCoord = center.x + radius * math.cos((stepAngle * i) + stepOffset); - final yCoord = center.y - radius * math.sin((stepAngle * i) + stepOffset); + final xCoord = center.x + radius * math.cos((stepAngle * i) + offsetAngle); + final yCoord = center.y - radius * math.sin((stepAngle * i) + offsetAngle); final point = Vector2(xCoord, yCoord); points.add(point); @@ -35,11 +34,14 @@ List calculateArc({ /// Calculates all [Vector2]s of a bezier curve. /// /// A bezier curve of [controlPoints] that say how to create this curve. +/// /// First and last points specify the beginning and the end respectively /// of the curve. The inner points specify the shape of the curve and /// its turning points. -/// The [step] must be in range 0<=step<=1 and indicates the precision to -/// calculate the curve. +/// +/// The [step] must be between zero and one (inclusive), indicating the +/// precision to calculate the curve. +/// /// For more information read: https://en.wikipedia.org/wiki/B%C3%A9zier_curve List calculateBezierCurve({ required List controlPoints, @@ -77,7 +79,7 @@ List calculateBezierCurve({ return points; } -/// Method to calculate the binomial coefficient of 'n' and 'k' +/// Calculates the binomial coefficient of 'n' and 'k'. /// For more information read: https://en.wikipedia.org/wiki/Binomial_coefficient num binomial(num n, num k) { assert(0 <= k && k <= n, 'k ($k) and n ($n) must be in range 0 <= k <= n'); @@ -89,7 +91,7 @@ num binomial(num n, num k) { } } -/// Method to calculate the factorial of some number 'n' +/// Calculate the factorial of 'n'. /// For more information read: https://en.wikipedia.org/wiki/Factorial num factorial(num n) { assert(n >= 0, 'Factorial is not defined for negative number n ($n)'); diff --git a/test/game/components/pathway_test.dart b/test/game/components/pathway_test.dart index e1c10455..7f8357d8 100644 --- a/test/game/components/pathway_test.dart +++ b/test/game/components/pathway_test.dart @@ -1,4 +1,5 @@ // ignore_for_file: cascade_invocations, prefer_const_constructors +import 'dart:math' as math; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/material.dart'; @@ -10,20 +11,21 @@ void main() { final flameTester = FlameTester(PinballGame.new); group('Pathway', () { - const pathwayWidth = 50.0; + const width = 50.0; group('straight', () { group('color', () { flameTester.test( - 'has transparent color by default if not specified', + 'has transparent color by default when no color is specified', (game) async { final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), - pathwayWidth: pathwayWidth, + width: width, ); await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); expect(pathway.paint, isNotNull); expect( @@ -32,8 +34,9 @@ void main() { ); }, ); + flameTester.test( - 'has a color if set', + 'has a color when is specified', (game) async { const defaultColor = Colors.blue; @@ -42,9 +45,10 @@ void main() { position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), - pathwayWidth: pathwayWidth, + width: width, ); await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); expect(pathway.paint, isNotNull); expect(pathway.paint.color.value, equals(defaultColor.value)); @@ -59,9 +63,10 @@ void main() { position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), - pathwayWidth: pathwayWidth, + width: width, ); await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); }, ); @@ -75,11 +80,11 @@ void main() { position: position, start: Vector2(10, 10), end: Vector2(20, 20), - pathwayWidth: pathwayWidth, + width: width, ); await game.ensureAdd(pathway); - game.contains(pathway); + game.contains(pathway); expect(pathway.body.position, position); }, ); @@ -91,7 +96,7 @@ void main() { position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), - pathwayWidth: pathwayWidth, + width: width, ); await game.ensureAdd(pathway); @@ -102,13 +107,13 @@ void main() { group('fixtures', () { flameTester.test( - 'exists only one ChainShape if just one wall', + 'has only one ChainShape when singleWall is true', (game) async { final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), - pathwayWidth: pathwayWidth, + width: width, singleWall: true, ); await game.ensureAdd(pathway); @@ -121,13 +126,13 @@ void main() { ); flameTester.test( - 'exists two ChainShape if there is by default two walls', + 'has two ChainShape when singleWall is false (default)', (game) async { final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), end: Vector2(20, 20), - pathwayWidth: pathwayWidth, + width: width, ); await game.ensureAdd(pathway); @@ -147,11 +152,12 @@ void main() { (game) async { final pathway = Pathway.arc( position: Vector2.zero(), - pathwayWidth: pathwayWidth, - radius: 100, - angle: 90, + width: width, + radius: math.pi / 2, + angle: math.pi / 2, ); await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); }, ); @@ -163,13 +169,13 @@ void main() { final position = Vector2.all(10); final pathway = Pathway.arc( position: position, - pathwayWidth: pathwayWidth, - radius: 100, - angle: 90, + width: width, + radius: math.pi / 2, + angle: math.pi / 2, ); await game.ensureAdd(pathway); - game.contains(pathway); + game.contains(pathway); expect(pathway.body.position, position); }, ); @@ -179,9 +185,9 @@ void main() { (game) async { final pathway = Pathway.arc( position: Vector2.zero(), - pathwayWidth: pathwayWidth, - radius: 100, - angle: 90, + width: width, + radius: math.pi / 2, + angle: math.pi / 2, ); await game.ensureAdd(pathway); @@ -205,9 +211,10 @@ void main() { final pathway = Pathway.bezierCurve( position: Vector2.zero(), controlPoints: controlPoints, - pathwayWidth: pathwayWidth, + width: width, ); await game.ensureAdd(pathway); + expect(game.contains(pathway), isTrue); }, ); @@ -220,11 +227,11 @@ void main() { final pathway = Pathway.bezierCurve( position: position, controlPoints: controlPoints, - pathwayWidth: pathwayWidth, + width: width, ); await game.ensureAdd(pathway); - game.contains(pathway); + game.contains(pathway); expect(pathway.body.position, position); }, ); @@ -235,7 +242,7 @@ void main() { final pathway = Pathway.bezierCurve( position: Vector2.zero(), controlPoints: controlPoints, - pathwayWidth: pathwayWidth, + width: width, ); await game.ensureAdd(pathway); From 45dc583c86512ddf524d3157cffe1871f4dc19aa Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Wed, 9 Mar 2022 17:42:54 +0100 Subject: [PATCH 22/32] test: fixed bug with radius in radians :S --- test/game/components/pathway_test.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/game/components/pathway_test.dart b/test/game/components/pathway_test.dart index 7f8357d8..6b11a2b3 100644 --- a/test/game/components/pathway_test.dart +++ b/test/game/components/pathway_test.dart @@ -1,5 +1,6 @@ // ignore_for_file: cascade_invocations, prefer_const_constructors import 'dart:math' as math; +import 'package:flame/input.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/material.dart'; @@ -153,7 +154,7 @@ void main() { final pathway = Pathway.arc( position: Vector2.zero(), width: width, - radius: math.pi / 2, + radius: 100, angle: math.pi / 2, ); await game.ensureAdd(pathway); @@ -170,7 +171,7 @@ void main() { final pathway = Pathway.arc( position: position, width: width, - radius: math.pi / 2, + radius: 100, angle: math.pi / 2, ); await game.ensureAdd(pathway); @@ -186,7 +187,7 @@ void main() { final pathway = Pathway.arc( position: Vector2.zero(), width: width, - radius: math.pi / 2, + radius: 100, angle: math.pi / 2, ); await game.ensureAdd(pathway); From 696ace8bfff9f632fd03f858667d5596dd26fdce Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Wed, 9 Mar 2022 18:44:20 +0100 Subject: [PATCH 23/32] chore: removed unused import --- test/game/components/pathway_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/test/game/components/pathway_test.dart b/test/game/components/pathway_test.dart index 6b11a2b3..d3b82e96 100644 --- a/test/game/components/pathway_test.dart +++ b/test/game/components/pathway_test.dart @@ -1,6 +1,5 @@ // ignore_for_file: cascade_invocations, prefer_const_constructors import 'dart:math' as math; -import 'package:flame/input.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/material.dart'; From 85cbe42958a8c0f8902463f0c9e980036f93b3b4 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Wed, 9 Mar 2022 18:52:25 +0100 Subject: [PATCH 24/32] refactor: renamed wall1 and wall2 vars --- lib/game/components/pathway.dart | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/game/components/pathway.dart b/lib/game/components/pathway.dart index 3404f2d2..d41ce3da 100644 --- a/lib/game/components/pathway.dart +++ b/lib/game/components/pathway.dart @@ -38,18 +38,20 @@ class Pathway extends BodyComponent { bool singleWall = false, }) { final paths = >[]; - final wall1 = [ + + // TODO(ruialonso): Refactor repetitive logic + final firstWall = [ start.clone(), end.clone(), ].map((vector) => vector..rotate(rotation)).toList(); - paths.add(wall1); + paths.add(firstWall); if (!singleWall) { - final wall2 = [ + final secondWall = [ start + Vector2(width, 0), end + Vector2(width, 0), ].map((vector) => vector..rotate(rotation)).toList(); - paths.add(wall2); + paths.add(secondWall); } return Pathway._( @@ -84,22 +86,23 @@ class Pathway extends BodyComponent { }) { final paths = >[]; - final wall1 = calculateArc( + // TODO(ruialonso): Refactor repetitive logic + final outerWall = calculateArc( center: position, radius: radius, angle: angle, offsetAngle: rotation, ); - paths.add(wall1); + paths.add(outerWall); if (!singleWall) { - final wall2 = calculateArc( + final innerWall = calculateArc( center: position, radius: radius - width, angle: angle, offsetAngle: rotation, ); - paths.add(wall2); + paths.add(innerWall); } return Pathway._( @@ -128,18 +131,19 @@ class Pathway extends BodyComponent { }) { final paths = >[]; - final wall1 = calculateBezierCurve(controlPoints: controlPoints) + // TODO(ruialonso): Refactor repetitive logic + final firstWall = calculateBezierCurve(controlPoints: controlPoints) .map((vector) => vector..rotate(rotation)) .toList(); - paths.add(wall1); + paths.add(firstWall); if (!singleWall) { - final wall2 = calculateBezierCurve( + final secondWall = calculateBezierCurve( controlPoints: controlPoints .map((vector) => vector + Vector2(width, -width)) .toList(), ).map((vector) => vector..rotate(rotation)).toList(); - paths.add(wall2); + paths.add(secondWall); } return Pathway._( From 3a092f8a1c33700081429d9d6dda8aa3d6912af1 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Wed, 9 Mar 2022 18:55:30 +0100 Subject: [PATCH 25/32] test: removed old group --- packages/geometry/test/src/geometry_test.dart | 251 +++++++++--------- 1 file changed, 125 insertions(+), 126 deletions(-) diff --git a/packages/geometry/test/src/geometry_test.dart b/packages/geometry/test/src/geometry_test.dart index 6ecf66c8..a3040a9c 100644 --- a/packages/geometry/test/src/geometry_test.dart +++ b/packages/geometry/test/src/geometry_test.dart @@ -11,149 +11,148 @@ class Binomial { } void main() { - group('Maths', () { - group('calculateArc', () { - test('returns by default 100 points as indicated by precision', () { - final points = calculateArc( - center: Vector2.zero(), - radius: 100, - angle: 90, - ); - expect(points.length, 100); - }); - test('returns as many points as indicated by precision', () { - final points = calculateArc( - center: Vector2.zero(), - radius: 100, - angle: 90, - precision: 50, - ); - expect(points.length, 50); - }); + group('calculateArc', () { + test('returns by default 100 points as indicated by precision', () { + final points = calculateArc( + center: Vector2.zero(), + radius: 100, + angle: 90, + ); + expect(points.length, 100); }); - group('calculateBezierCurve', () { - test('fails if step not in range', () { - expect( - () => calculateBezierCurve( - controlPoints: [ - Vector2(0, 0), - Vector2(10, 10), - ], - step: 2, - ), - throwsAssertionError, - ); - }); - - test('fails if not enough control points', () { - expect( - () => calculateBezierCurve(controlPoints: [Vector2.zero()]), - throwsAssertionError, - ); - expect( - () => calculateBezierCurve(controlPoints: []), - throwsAssertionError, - ); - }); + test('returns as many points as indicated by precision', () { + final points = calculateArc( + center: Vector2.zero(), + radius: 100, + angle: 90, + precision: 50, + ); + expect(points.length, 50); + }); + }); - test('returns by default 1000 points as indicated by step', () { - final points = calculateBezierCurve( + group('calculateBezierCurve', () { + test('fails if step not in range', () { + expect( + () => calculateBezierCurve( controlPoints: [ Vector2(0, 0), Vector2(10, 10), ], - ); - expect(points.length, 1000); - }); + step: 2, + ), + throwsAssertionError, + ); + }); - test('returns as many points as indicated by step', () { - final points = calculateBezierCurve( - controlPoints: [ - Vector2(0, 0), - Vector2(10, 10), - ], - step: 0.01, - ); - expect(points.length, 100); - }); + test('fails if not enough control points', () { + expect( + () => calculateBezierCurve(controlPoints: [Vector2.zero()]), + throwsAssertionError, + ); + expect( + () => calculateBezierCurve(controlPoints: []), + throwsAssertionError, + ); }); - group('binomial', () { - test('fails if k is negative', () { - expect(() => binomial(1, -1), throwsAssertionError); - }); + test('returns by default 1000 points as indicated by step', () { + final points = calculateBezierCurve( + controlPoints: [ + Vector2(0, 0), + Vector2(10, 10), + ], + ); + expect(points.length, 1000); + }); - test('fails if n is negative', () { - expect(() => binomial(-1, 1), throwsAssertionError); - }); + test('returns as many points as indicated by step', () { + final points = calculateBezierCurve( + controlPoints: [ + Vector2(0, 0), + Vector2(10, 10), + ], + step: 0.01, + ); + expect(points.length, 100); + }); + }); - test('fails if n < k', () { - expect(() => binomial(1, 2), throwsAssertionError); - }); + group('binomial', () { + test('fails if k is negative', () { + expect(() => binomial(1, -1), throwsAssertionError); + }); - test('for a specific input gives a correct value', () { - final binomialInputsToExpected = { - Binomial(n: 0, k: 0): 1, - Binomial(n: 1, k: 0): 1, - Binomial(n: 1, k: 1): 1, - Binomial(n: 2, k: 0): 1, - Binomial(n: 2, k: 1): 2, - Binomial(n: 2, k: 2): 1, - Binomial(n: 3, k: 0): 1, - Binomial(n: 3, k: 1): 3, - Binomial(n: 3, k: 2): 3, - Binomial(n: 3, k: 3): 1, - Binomial(n: 4, k: 0): 1, - Binomial(n: 4, k: 1): 4, - Binomial(n: 4, k: 2): 6, - Binomial(n: 4, k: 3): 4, - Binomial(n: 4, k: 4): 1, - Binomial(n: 5, k: 0): 1, - Binomial(n: 5, k: 1): 5, - Binomial(n: 5, k: 2): 10, - Binomial(n: 5, k: 3): 10, - Binomial(n: 5, k: 4): 5, - Binomial(n: 5, k: 5): 1, - Binomial(n: 6, k: 0): 1, - Binomial(n: 6, k: 1): 6, - Binomial(n: 6, k: 2): 15, - Binomial(n: 6, k: 3): 20, - Binomial(n: 6, k: 4): 15, - Binomial(n: 6, k: 5): 6, - Binomial(n: 6, k: 6): 1, - }; - binomialInputsToExpected.forEach((input, value) { - expect(binomial(input.n, input.k), value); - }); - }); + test('fails if n is negative', () { + expect(() => binomial(-1, 1), throwsAssertionError); + }); + + test('fails if n < k', () { + expect(() => binomial(1, 2), throwsAssertionError); }); - group('factorial', () { - test('fails if negative number', () { - expect(() => factorial(-1), throwsAssertionError); + test('for a specific input gives a correct value', () { + final binomialInputsToExpected = { + Binomial(n: 0, k: 0): 1, + Binomial(n: 1, k: 0): 1, + Binomial(n: 1, k: 1): 1, + Binomial(n: 2, k: 0): 1, + Binomial(n: 2, k: 1): 2, + Binomial(n: 2, k: 2): 1, + Binomial(n: 3, k: 0): 1, + Binomial(n: 3, k: 1): 3, + Binomial(n: 3, k: 2): 3, + Binomial(n: 3, k: 3): 1, + Binomial(n: 4, k: 0): 1, + Binomial(n: 4, k: 1): 4, + Binomial(n: 4, k: 2): 6, + Binomial(n: 4, k: 3): 4, + Binomial(n: 4, k: 4): 1, + Binomial(n: 5, k: 0): 1, + Binomial(n: 5, k: 1): 5, + Binomial(n: 5, k: 2): 10, + Binomial(n: 5, k: 3): 10, + Binomial(n: 5, k: 4): 5, + Binomial(n: 5, k: 5): 1, + Binomial(n: 6, k: 0): 1, + Binomial(n: 6, k: 1): 6, + Binomial(n: 6, k: 2): 15, + Binomial(n: 6, k: 3): 20, + Binomial(n: 6, k: 4): 15, + Binomial(n: 6, k: 5): 6, + Binomial(n: 6, k: 6): 1, + }; + binomialInputsToExpected.forEach((input, value) { + expect(binomial(input.n, input.k), value); }); + }); + }); + + group('factorial', () { + test('fails if negative number', () { + expect(() => factorial(-1), throwsAssertionError); + }); - test('for a specific input gives a correct value', () { - final factorialInputsToExpected = { - 0: 1, - 1: 1, - 2: 2, - 3: 6, - 4: 24, - 5: 120, - 6: 720, - 7: 5040, - 8: 40320, - 9: 362880, - 10: 3628800, - 11: 39916800, - 12: 479001600, - 13: 6227020800, - }; - factorialInputsToExpected.forEach((input, expected) { - expect(factorial(input), expected); - }); + test('for a specific input gives a correct value', () { + final factorialInputsToExpected = { + 0: 1, + 1: 1, + 2: 2, + 3: 6, + 4: 24, + 5: 120, + 6: 720, + 7: 5040, + 8: 40320, + 9: 362880, + 10: 3628800, + 11: 39916800, + 12: 479001600, + 13: 6227020800, + }; + factorialInputsToExpected.forEach((input, expected) { + expect(factorial(input), expected); }); }); }); From 78eb18eb0e14d763fb6aea1e4ebd80e21a1e95a3 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Wed, 9 Mar 2022 19:33:25 +0100 Subject: [PATCH 26/32] chore: updated package geometry readme file --- packages/geometry/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/geometry/README.md b/packages/geometry/README.md index cbc4cca1..f0841d82 100644 --- a/packages/geometry/README.md +++ b/packages/geometry/README.md @@ -1,9 +1,9 @@ -# maths +# geometry [![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link] [![License: MIT][license_badge]][license_link] -A Very Good Project created by Very Good CLI. +Helper package to calculate points of lines, arcs and curves for the pathways of the ball. [license_badge]: https://img.shields.io/badge/license-MIT-blue.svg [license_link]: https://opensource.org/licenses/MIT From f75d5514cc5cfba51735162c76933b6ec124e6ba Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Wed, 9 Mar 2022 19:47:23 +0100 Subject: [PATCH 27/32] doc: geometry doc refactored --- packages/geometry/lib/src/geometry.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/geometry/lib/src/geometry.dart b/packages/geometry/lib/src/geometry.dart index 8851ed25..dceb4e9e 100644 --- a/packages/geometry/lib/src/geometry.dart +++ b/packages/geometry/lib/src/geometry.dart @@ -3,10 +3,11 @@ import 'package:flame/extensions.dart'; /// Calculates all [Vector2]s of a circumference. /// -/// Circumference is created from a [center] and a [radius] -/// Also semi circumference could be created, specifying its [angle] in radians -/// and the offset start angle [offsetAngle] for this semi circumference. -/// The higher the [precision], the more [Vector2]s will be calculated, +/// A circumference can be achieved by specifying a [center] and a [radius]. +/// In addition, a semi-circle can be achieved by specifying its [angle] and an +/// [offsetAngle] (both in radians). +/// +/// The higher the [precision], the more [Vector2]s will be calculated; /// achieving a more rounded arc. /// /// For more information read: https://en.wikipedia.org/wiki/Trigonometric_functions. @@ -80,6 +81,7 @@ List calculateBezierCurve({ } /// Calculates the binomial coefficient of 'n' and 'k'. +/// /// For more information read: https://en.wikipedia.org/wiki/Binomial_coefficient num binomial(num n, num k) { assert(0 <= k && k <= n, 'k ($k) and n ($n) must be in range 0 <= k <= n'); @@ -92,6 +94,7 @@ num binomial(num n, num k) { } /// Calculate the factorial of 'n'. +/// /// For more information read: https://en.wikipedia.org/wiki/Factorial num factorial(num n) { assert(n >= 0, 'Factorial is not defined for negative number n ($n)'); From c1a25df01a6cd3eddf00ed2e03a030f5e8408f4c Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Wed, 9 Mar 2022 18:39:00 -0300 Subject: [PATCH 28/32] feat: adding placeholder art for the flippers --- assets/images/components/flipper.png | Bin 0 -> 8287 bytes lib/game/components/flipper.dart | 28 ++++++++++++++++++------- lib/game/game_assets.dart | 1 + test/game/components/anchor_test.dart | 3 +++ test/game/components/pathway_test.dart | 13 ++++++++++++ test/game/components/plunger_test.dart | 1 + test/game/components/wall_test.dart | 1 + 7 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 assets/images/components/flipper.png diff --git a/assets/images/components/flipper.png b/assets/images/components/flipper.png new file mode 100644 index 0000000000000000000000000000000000000000..f63974c482a57307fcd6bbcedd306710a10703ad GIT binary patch literal 8287 zcmX9^bzD>58z#r-5#ne@Hv<9b77!2-K}uRcLO`j}0;7?Xbc9l)OUcpQAl(Sc=Km^&l`fAKe*`6qAlHkJ2(Bg~q5}#8JNRWU$XKY-N{$t#4OI71oW-~iKnZcnq5dnU$4;Tbk%@#6?Ss9NK@j(vqhWdVO)Ql0Dzz z@5}tmMCl_&<69PxJz!;e+S6Yu)2vG}yB*v_p_M62b23pS)ykRVQgu{QnOrm5kFO!< z^dTsbn1+V?!o{$@(%~1~ZQKFU#I*0w*Ttpc#Mj!|nwKW}tirsNX=PlE6(byp6{IeF znUa!1Oh->|5(;{UIN^w?s(Ntx$$1Kl4S^rl+#Gvjj&PNX8|> z&3;rB1hYkatag>)eIidBZ?2y^vu0pHu%Y=QqIc+knUt zamr$7zHSjhgTl_Lp3Ke)mTG6!8_TG9hPhmp8XrhktLq|3ma9u`AziqL6E(2)!zYv! z4%8U4gK#)lS>Uj2;o1J+fSHTS0j^R+>jol@ zhoWim4>X=u)<=~7k!{RL2l_>>YUW8)w-tX!sS?c`RlQMtM3xJ1+X&J zxcCRpsE(i514MxX|0oVTC4zX0^aOChVRO(J0e^r0R$@&LeSH9VPr$}d`VFKiwR5%| z#ifnt<9wFU)$@tISFq*MO5AA{`cAdhm{j1qD@?#yQOK$KFJ{+j>?aD^`Fo5QnwMV| zeLiWrnVOkFFNS#Ca;>~?MiJZ(ejpfm9SOv~qW~!(ej!=~;j=geD-#peE_2m?w8VT~ zi2+=UrnP!zjeBON@XI#T35Wi1->cnolaJFwtd=;kip?<iF`nJk7%h=?ko`H4P5-!*eEDdN-DB6D9fXi3# zcd(AnE%ncZ@7GN8eBv*1HIkG9{%XJ~%q~58?y`)!dYdmm^truv{bQryq;Lb!4c^L)a&(j1b`tby_0(>`{a%}d6`+xFO5+?K!$gC zyoIrwCYOaK)sNVShj-AAxGcM-A&1~8@IA2j-9qimsxWWeZ5tS6V?-4S`XY%=edtJ% z){c;Ln=O>Tv!5Ot)7kxSEx!g76&DvlP6xAkU?1|o9IatIng8D zLQml8?PgyB+wSIg?g(-Ap9|~WSbeEDv%Q0ZR+sgjPxl7emnzehCa(%le;_3}Z;2F8VXb!&15whxH+yw+b2WC+ukh3= zwK!|I)3rRQn++RsSOLs&y@m8)M>y!)V87&!4y(Cp8>fNKJo(?F%cnrthO+J7ALzw8 zuoIbb^2ib8Lfz-F6s<1ymv%2&?`3^=%IX+uR{hxF5`&U1HScbov;vcd|3R>OfK+Bc zxNyqM?w<_Da!bur!C~3c0Qx60Taj3P)aCVSQHx)TvzjmKr!3CLx|7_}_t}cnZr)e6 zT6O}|s)gEq$JvRbJ0|tNsmnIQNPITom*N&+*P>xNl-54Ku&}dXd4bDm+R0!i3Ip%( zcbBXKWo1r}RNxARKn4FA&pl($>(ebi{l7nJ_dMnIc%s`)QbIcdvnp^5d-=^!v(N9` zpJhU@vH|KxVU`lI6E7ZCkP$vT@(@=P+Zs`0_U{U~>}0NfWQSsW{V08CkCBm4USK*i zk=D<|WI+Pb#f*U>p9f@3o`cm8Q_>%TZ5Ds`Z>`PkUlU^04+N3aP|3|yWxOG$Vizwx|S@(Lfnh}!;G zZYZ^)&IPbPf&^9|gCPNr@-7LfIDbRS33!-noew{7u;O*7V1-wV%3dJsl9eLVs3N

=5p zF8@436APi@f8{?aYKnVVs4aOugxSPdphDD2olABqb*prOI?J?y+4C7)+FQ;M?`Q*p z*4sZqHGCB)k=+*GX#zq<5{AN`T+g#g`-qgn*a5j*wg-W*pa&ZvBaN_&5nNr=jOUy@ zuR8}sM+(O^=DM9cV0A00UDCYLvNP=2mB!ids=#1lkZ|pANeyTVJL`=oLv`1m3#5nb*xjFnf#~z!-`sZ={9ot+c9(W z7OxVE0^W3)N$=l70tSDwZ}-*jZlWLj+vk}NBL?-v!My8V!8Nx`RA=;z!zFAM!qa3UoiSIE9^^BBGgTNcq7cs^0H>nD30Ma#(L@kW6{7v@*qbmjA3g zIQca7p&saetP z_fz;lOTQJH@_GtIm_OH&yGL4Mm%m9Mb(6a)BPI^O=Av%x9`k@JJO$of&$QZBv|Jab zQr~+;s;rsV2wyh1q0MF|Cr{5uHBmWzL|sMz1EhLDdG(Lo$3m>}vs<*z=5 zn84D1aimLaD>XObPZPNffwEPHNAjEI*e78UuKz!!5R!o7q->b}Rq4Bhtn;lW`l1E) zelxT~J3ok^~JF$3LyuwfNnOh;>mT zRrgELlvu<^{_+T5P$dYq7Rd#m^?Q?Dc!bgSb*7k= z;sb{?8>17jwNQOUJKlZI;}(kT6nYOOwBX)?WqHJF3~cT@^H&zPoX6yzjBSK@Sa#r$lPlBhRL;^*oh%&$%uM(-Ra6{Ajf{*a5-4|rZ{x_q;5wVsAed}1{z|xsmbvFrB^)JsBYPpuGHf7teO}b_h6zcS;UZ_&tW@(+X$bf0^tVF2~Du1R;H9#jDu!5 zCX_Nto=74cT#@SG2dOswjd1}!?R3q zI3WAt*7YToz{B!KyNj&O`0d8y*8U=gZ65zjbCTQF;oao1Zxh`oyG!oir?H)ZiEDQ; za;7f{aaJ%BX<^^KXiQ^7;&a>t))Nyg$WYSoR!S4JNAmDe@STuSqD*bxKF3>#%HSviZP&lsJ9}lhUa)ueXm5bwC$ry!#@};HH3$EDFx7c2}39;l3iBO=ASNa=l3I> zkAUDjW5<^xQPO((IIgPL$>z=KM}6`vz0Df&FRGNmL2pF1kRN3??H6kAJ9gqwlo;R` zeLo_PZrLI>zr^_>{?3>i+R3jAxKQH8fod|U|3E4-q*N+#Z+`qVuL>6&Te_JM=vJ0R z=d(?{b3!(cE3fu*We=s&Uz%YgHtPZIiO{#U7tbthoP!NEvt)e{+h2==d{P8@6Z9Sc~fP2dxINfd%Eus?r465inAb2_f39GQX4 zQ0usPk4bWjNr#y4FMAPgug0pje&BSdS-)=y#^aUJJSye@C3a%s_RXfGsS=P{4eNzw z>)oCU`qA_yA7<^6<~&#KhnUJqJ(`m_v819ULy>4Y5;XsELnkj}x`T~iiarmfF;+G~ zQdk=niluwjemftceQ|mkcs{l9nVlxfTDD zV*L^@`?dpBEr&%S%k1)CNWH@hz24Q^dzZi)lR^o@@y+SKIj)B$WO)~uRAMrj`Z;0a zU96lbIuz@}z1-dDL>Agh37F9Tru->qQqzFVq5K*maNPgoXTGMg^;596qkQ=^&E z2GS-U*x~WwC2x4Q4zPQaxSwfwoH^_JYGCAjk~S3sV;h3XO`FaU3}f5+N)ZR_l#x^2 zm3-{&fzfVcF4Jyrmr>qM?ZNon17(+i`oU_yHM%HgBK6{4C9d9mV&H$v(yi$>>r;tr zm!zB&Q-ObE#siHKWgWUY44|=v;4pz?jisM+bCMF=pGp!@BrQLFoXK@U|4uD&t9*D} zWfhuT`^x%?u|Z_Hf*d6%Ikf1 zHp94K#ywL3S~HZ&Y_Y`T^UP=gfsZdCgv)CP7uS_&XhodNvHH~4a7JNw9TwQgcFHK# zT;X!S&AY<8H&50B8|(97_g<9!H$;Orbmx#+q=tttZ(YdFt)n#ACullgY0+_XW#zH&WWnLI%r4-`bz z#Pn3E*g+3KZuvwNd=`#nsxJg=E1?bMopg8ggS*Fi0YApiYv#y7x*lS3z(&e_9z@cV z=&*q#x@Hl-wyy3G@>%h-pGglE&Y|1H)$)f@Nz$O>Hv*(aQ1>pRVkw3+l#ni0f z#`DN|Sui%9ib}jB%7QvL^}0E}v0ErhDIz3Y%l4^Ay5I1DtmH4`7-@=)Y&)3#^AavE z6&_;=r{Y>DHAzRONZ?<;cUI#$!$Qrcx`M z{ySTQ50MB=&mv>vUyj^VZB%1hwcy+&5F!3s0Jo%21`%AyM)^ra)*Fx){LtJ_xbB#3 zI467Js2Hc{{jW2Y?vlfo4y_iPY%tukAE|#$il0~Js3Nq1kVM1@`sU^)@XyR?)n~Z} zHiu@j}>AWdCTOEY=h|xO(eb(Q?-sbc?<_d`0u0Opc@Ia zX`~8{=V}Kt>mO77&+Qn$zvw6^C~zLRVo3^1<}-LD8&lyRNLWGy7l?mBFu3#KY`-JV z@9Ix#`e%mK&K5QLJ?=Rt2==l6Tg;z~&l=Anld=s>Q$C^lziJot`Av8u9 zx+yqvWd`XoLtzTO=F768B73w@z)^G%ZWo>I3qV{gCK9@hcsDg=&Y1ZMG@Xn{m(2UA z%imla$cNi&XUQ}ad!(31bl|dSL4nc?n{l~bk5d%g1{DiwhD z;v`O?X%&(5{GQa->>GhClqv_VnRx+FP82!70dw7hJlzJmMJVF%$S470!- z&{o$G0{ql)>QnZxa+n2WX*l)filX`6 zY4VW#J?75|2`*-V^saSBWBwVx(XV;>nk148jrD`|JAu)c!}yPtvx;I=9)yLjVEV=t zCQ%jNQI!W5QqpZ#y79K(B@pgSgCzv%QgA^UCB(4=$~_Inn;d-lNj*ycha91=vtEhZ z3JDWwCI1d>&un%BeaPif{90$kTsh~us`Tne00!I*mR?UdV%5G|=)rVZ(r$JJ_$u<0 zryPbSof>8v+Ha|;#dw%VUQ^C*q`;@{|9H$^6}cfne%#~^8e$ufJ`s_lFJ4~3&IqdX zkBzdstg4#n#ht+Fe^m8^aX5r8Lx~@nCHKGa^m&qo+A8=mTWvGQ>l~krGwD}W&-7js zG{922suy1m(jqBGC#oOp+x_Nk1qesQ=a`5LLIoU%gk6O;_Uh_%qr!z1zjkUqb81b1 zulb*?Qqpi;lIW^@%(M2wdQNqxEnBNS(EXb;#3#W z2|*^3Yi;F}7kPfOO7f&%=1sx}x`eRUV1f7!)>qq9F3as#1u2Mc2 zA|EnQ-?Nym3{@+=^jh}reIFa$xaU>NT)8*GXl6SMWTaAp9>vCzZT9pxvLMvHz7_Zb zy`%?QV=$#B#OwatH!^qAvGd;xg1uAWc^I;+va5 zC28B_0>rFWG?L*~A;SjUmDx2d;v;2 zxUd5{3dA4WepEhV{pS{XIy%oX^VXI;e(Dy-0%FHt1tXG{{CpcNW#vv^#0l|YSy=38 z%gw%9?YJcclM~cfU28u?fZ;ZHoGmLdTiIe!h}q(Ec-eOiLzy>Ln=EC_9UaAH+X?F+ zJht96>xZ2AE-~^wOoS%!-fC=H^gqKEJeIFwI|nh%=sgUinEqySof>5Lq$EG^pPkJ{ zu3ld0iCrxFx(KFt88U``4&8g-lD1$$G&WTt^qrLaaVhyBS=hc!CmA6HF+RIj;(RCF zc*hx%u#wm_@(8|9&lwChXNXnyzyZYKYOAkiAZKG0wo?ewpeA9Wu`6wD?-u^f=JC-f ztCS&yacmyJ()=-fGHTtdh8vL3#(vX5`O}NT%3z~)#qvof5@|}qzBWA z*Gx}O+lN`Q8`Oz?S(;(*sLbH7JRZ|g|zEtM^PY6t3-SCANHbjRaM2((*&?im!{Q1>%u65@9 zn$`m`v171yFUzjK zQq3Zx#Rwaxx!|)EBFWcK7yHt0~5#TAg9{QIGBPe2ctK8qYCL9!HZvqu8) zjMPxW3QsEj5lN>+(>jR9o}bFF&!_}s(3YywMy+Mf)_z*mr@oP1wEQ~1jTcl5g;`L= z^}2|WSj(dQSP}{d6otq=p2;wgizygQ_G+K?B~#Wp_y><>%m(0Y$6?@F;XYG~Q$DtV z;VkIuIR#DzL`UqOCU!&+DU{VTQlkB(`_=IueeyH<)@O zxnRv`uT|9&HHw$FCU#z!ngYoiGOBU>lC*QSOS1Jls12GA4;{X}I&q`*b!P<4254@S z*)AruZdu%r?uLTw@^)mFZ6*~_c4g1cU+lV=+VTAGfO<``H#C-Zl=Y>)R=hC-bUV3_MCdF$?@*`j5#@kVo4dpevK=z zPy>XT95OgrbuevqPqV6hO;vgSYj>^&PeZ%~7kitve5LWeb9y6>$=Q|IYZc?{yooK- z%*;&cA-U4XCublWF$ESqK(_vz7KQ|qw~AR>t81;SS4=4=dRZt{HEYAk+`ML!s4)wK6v9)oJ2bi^nGmJO+Z7)1R_2Lk z9azVZPeyKTHA!u_2gi@{0ygV@wS_!%|8?9nq*@WO=S35jdxa8$L~knkOMbtqW`&T zh6rqK6w@?z6iFY-MVvGpcc+k3B(W544>k@AvE>cH@U%n_ z5FtQ_9vs(u&rTD4hiknXt90b#Ae*NYv3^zhygD6?c|jRLB*vS8*fcxVywl4Va*`E$Vgd?VaQOyDveO`lg8zzGa5tcq52#66_8Ib7EjJ z0g;yAb+C1++ej}V1mV>KmNWnOX0<-tO>nQx>%)lGFVLf8jb_cN)?AS@B;!Bg{2iXV z_2E=c$iP3Hm2#XxECBPz%1;>~|Awot@gmwMV*caQbF?Wij|y=$mstO!*ugTx*n#X1 yA(&4B=>B~R@E?=CO^t1J=pj`(De&L$FSDk6Z!V-OfE+Um>**71m2xGE;Qs;K{rQRj literal 0 HcmV?d00001 diff --git a/lib/game/components/flipper.dart b/lib/game/components/flipper.dart index bd071b93..509eab7a 100644 --- a/lib/game/components/flipper.dart +++ b/lib/game/components/flipper.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'dart:math' as math; +import 'package:flame/components.dart' show SpriteComponent; import 'package:flame/input.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pinball/game/game.dart'; @@ -12,19 +12,15 @@ import 'package:pinball/game/game.dart'; /// /// [Flipper] can be controlled by the player in an arc motion. /// {@endtemplate flipper} -class Flipper extends BodyComponent with KeyboardHandler { +class Flipper extends PositionBodyComponent with KeyboardHandler { /// {@macro flipper} Flipper._({ required Vector2 position, required this.side, required List keys, }) : _position = position, - _keys = keys { - // TODO(alestiago): Use sprite instead of color when provided. - paint = Paint() - ..color = const Color(0xFF00FF00) - ..style = PaintingStyle.fill; - } + _keys = keys, + super(size: Vector2(width, height)); /// A left positioned [Flipper]. Flipper.left({ @@ -50,6 +46,8 @@ class Flipper extends BodyComponent with KeyboardHandler { ], ); + static const spritePath = 'components/flipper.png'; + /// The width of the [Flipper]. static const width = 12.0; @@ -75,6 +73,20 @@ class Flipper extends BodyComponent with KeyboardHandler { /// [onKeyEvent] method listens to when one of these keys is pressed. final List _keys; + @override + Future onLoad() async { + await super.onLoad(); + final sprite = await gameRef.loadSprite(spritePath); + positionComponent = SpriteComponent( + sprite: sprite, + size: Vector2(width, height), + ); + + if (side == BoardSide.right) { + positionComponent?.flipHorizontally(); + } + } + /// Applies downward linear velocity to the [Flipper], moving it to its /// resting position. void _moveDown() { diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 964aeda1..778e2bc2 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -6,6 +6,7 @@ extension PinballGameAssetsX on PinballGame { Future preLoadAssets() async { await Future.wait([ images.load(Ball.spritePath), + images.load(Flipper.spritePath), ]); } } diff --git a/test/game/components/anchor_test.dart b/test/game/components/anchor_test.dart index 5cc37eca..a5aa7d2d 100644 --- a/test/game/components/anchor_test.dart +++ b/test/game/components/anchor_test.dart @@ -15,6 +15,7 @@ void main() { 'loads correctly', (game) async { final anchor = Anchor(position: Vector2.zero()); + await game.ready(); await game.ensureAdd(anchor); expect(game.contains(anchor), isTrue); @@ -25,6 +26,7 @@ void main() { flameTester.test( 'positions correctly', (game) async { + await game.ready(); final position = Vector2.all(10); final anchor = Anchor(position: position); await game.ensureAdd(anchor); @@ -37,6 +39,7 @@ void main() { flameTester.test( 'is static', (game) async { + await game.ready(); final anchor = Anchor(position: Vector2.zero()); await game.ensureAdd(anchor); diff --git a/test/game/components/pathway_test.dart b/test/game/components/pathway_test.dart index d3b82e96..036309b1 100644 --- a/test/game/components/pathway_test.dart +++ b/test/game/components/pathway_test.dart @@ -18,6 +18,7 @@ void main() { flameTester.test( 'has transparent color by default when no color is specified', (game) async { + await game.ready(); final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), @@ -38,6 +39,7 @@ void main() { flameTester.test( 'has a color when is specified', (game) async { + await game.ready(); const defaultColor = Colors.blue; final pathway = Pathway.straight( @@ -59,6 +61,7 @@ void main() { flameTester.test( 'loads correctly', (game) async { + await game.ready(); final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), @@ -75,6 +78,7 @@ void main() { flameTester.test( 'positions correctly', (game) async { + await game.ready(); final position = Vector2.all(10); final pathway = Pathway.straight( position: position, @@ -92,6 +96,7 @@ void main() { flameTester.test( 'is static', (game) async { + await game.ready(); final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), @@ -109,6 +114,7 @@ void main() { flameTester.test( 'has only one ChainShape when singleWall is true', (game) async { + await game.ready(); final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), @@ -128,6 +134,7 @@ void main() { flameTester.test( 'has two ChainShape when singleWall is false (default)', (game) async { + await game.ready(); final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), @@ -150,6 +157,7 @@ void main() { flameTester.test( 'loads correctly', (game) async { + await game.ready(); final pathway = Pathway.arc( position: Vector2.zero(), width: width, @@ -166,6 +174,7 @@ void main() { flameTester.test( 'positions correctly', (game) async { + await game.ready(); final position = Vector2.all(10); final pathway = Pathway.arc( position: position, @@ -183,6 +192,7 @@ void main() { flameTester.test( 'is static', (game) async { + await game.ready(); final pathway = Pathway.arc( position: Vector2.zero(), width: width, @@ -208,6 +218,7 @@ void main() { flameTester.test( 'loads correctly', (game) async { + await game.ready(); final pathway = Pathway.bezierCurve( position: Vector2.zero(), controlPoints: controlPoints, @@ -223,6 +234,7 @@ void main() { flameTester.test( 'positions correctly', (game) async { + await game.ready(); final position = Vector2.all(10); final pathway = Pathway.bezierCurve( position: position, @@ -239,6 +251,7 @@ void main() { flameTester.test( 'is static', (game) async { + await game.ready(); final pathway = Pathway.bezierCurve( position: Vector2.zero(), controlPoints: controlPoints, diff --git a/test/game/components/plunger_test.dart b/test/game/components/plunger_test.dart index 67e215fd..835bbc65 100644 --- a/test/game/components/plunger_test.dart +++ b/test/game/components/plunger_test.dart @@ -16,6 +16,7 @@ void main() { flameTester.test( 'loads correctly', (game) async { + await game.ready(); final plunger = Plunger(position: Vector2.zero()); await game.ensureAdd(plunger); diff --git a/test/game/components/wall_test.dart b/test/game/components/wall_test.dart index 8151055e..7f3c1c9c 100644 --- a/test/game/components/wall_test.dart +++ b/test/game/components/wall_test.dart @@ -37,6 +37,7 @@ void main() { flameTester.test( 'loads correctly', (game) async { + await game.ready(); final wall = Wall( start: Vector2.zero(), end: Vector2(100, 0), From 61ba65ce619efbed37318bc12e8cbbe1009d9b8b Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Thu, 10 Mar 2022 09:52:01 +0000 Subject: [PATCH 29/32] feat: include `public_member_api_docs` (#13) * feat: included public_member_api_docs lint rule * docs: included ignore comment * docs: included Ball method comment --- analysis_options.yaml | 3 --- lib/app/view/app.dart | 2 ++ lib/bootstrap.dart | 2 ++ lib/game/bloc/game_bloc.dart | 2 ++ lib/game/bloc/game_event.dart | 8 ++++++++ lib/game/bloc/game_state.dart | 2 ++ lib/game/components/ball.dart | 5 +++++ lib/game/pinball_game.dart | 2 ++ lib/game/view/pinball_game_page.dart | 2 ++ lib/l10n/l10n.dart | 2 ++ lib/landing/view/landing_page.dart | 2 ++ 11 files changed, 29 insertions(+), 3 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 07aa1dab..44aef9ac 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1 @@ include: package:very_good_analysis/analysis_options.2.4.0.yaml -linter: - rules: - public_member_api_docs: false diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 7e3fdf17..cf6213e9 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -5,6 +5,8 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +// ignore_for_file: public_member_api_docs + import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:pinball/l10n/l10n.dart'; diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index c612b584..34fcc47a 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -5,6 +5,8 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +// ignore_for_file: public_member_api_docs + import 'dart:async'; import 'dart:developer'; diff --git a/lib/game/bloc/game_bloc.dart b/lib/game/bloc/game_bloc.dart index 3b5c16b0..31aa0498 100644 --- a/lib/game/bloc/game_bloc.dart +++ b/lib/game/bloc/game_bloc.dart @@ -1,3 +1,5 @@ +// ignore_for_file: public_member_api_docs + import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; diff --git a/lib/game/bloc/game_event.dart b/lib/game/bloc/game_event.dart index 417f6322..fa57cbff 100644 --- a/lib/game/bloc/game_event.dart +++ b/lib/game/bloc/game_event.dart @@ -1,3 +1,5 @@ +// ignore_for_file: public_member_api_docs + part of 'game_bloc.dart'; @immutable @@ -5,16 +7,22 @@ abstract class GameEvent extends Equatable { const GameEvent(); } +/// {@template ball_lost_game_event} /// Event added when a user drops a ball off the screen. +/// {@endtemplate} class BallLost extends GameEvent { + /// {@macro ball_lost_game_event} const BallLost(); @override List get props => []; } +/// {@template scored_game_event} /// Event added when a user increases their score. +/// {@endtemplate} class Scored extends GameEvent { + /// {@macro scored_game_event} const Scored({ required this.points, }) : assert(points > 0, 'Points must be greater than 0'); diff --git a/lib/game/bloc/game_state.dart b/lib/game/bloc/game_state.dart index 8a5ab298..f8456518 100644 --- a/lib/game/bloc/game_state.dart +++ b/lib/game/bloc/game_state.dart @@ -1,3 +1,5 @@ +// ignore_for_file: public_member_api_docs + part of 'game_bloc.dart'; /// {@template game_state} diff --git a/lib/game/components/ball.dart b/lib/game/components/ball.dart index 2d9dddf0..aeb9be69 100644 --- a/lib/game/components/ball.dart +++ b/lib/game/components/ball.dart @@ -3,8 +3,13 @@ import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/game.dart'; +/// {@template ball} +/// A solid, [BodyType.dynamic] sphere that rolls and bounces along the +/// [PinballGame]. +/// {@endtemplate} class Ball extends PositionBodyComponent with BlocComponent { + /// {@macro ball} Ball({ required Vector2 position, }) : _position = position, diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 501ea514..33ea5eda 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -1,3 +1,5 @@ +// ignore_for_file: public_member_api_docs + import 'dart:async'; import 'package:flame/input.dart'; import 'package:flame_bloc/flame_bloc.dart'; diff --git a/lib/game/view/pinball_game_page.dart b/lib/game/view/pinball_game_page.dart index 8a9a981c..a49ff0c1 100644 --- a/lib/game/view/pinball_game_page.dart +++ b/lib/game/view/pinball_game_page.dart @@ -1,3 +1,5 @@ +// ignore_for_file: public_member_api_docs + import 'package:flame/game.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart index 766b5e31..548a81a6 100644 --- a/lib/l10n/l10n.dart +++ b/lib/l10n/l10n.dart @@ -5,6 +5,8 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +// ignore_for_file: public_member_api_docs + import 'package:flutter/widgets.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; diff --git a/lib/landing/view/landing_page.dart b/lib/landing/view/landing_page.dart index a688dee1..c705f084 100644 --- a/lib/landing/view/landing_page.dart +++ b/lib/landing/view/landing_page.dart @@ -1,3 +1,5 @@ +// ignore_for_file: public_member_api_docs + import 'package:flutter/material.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball/l10n/l10n.dart'; From 6ed02030542c1ad06eb5a6e08fa3a701b13fb206 Mon Sep 17 00:00:00 2001 From: Erick Date: Thu, 10 Mar 2022 09:46:00 -0300 Subject: [PATCH 30/32] Update lib/game/components/flipper.dart Co-authored-by: Alejandro Santiago --- lib/game/components/flipper.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/game/components/flipper.dart b/lib/game/components/flipper.dart index 509eab7a..2ce95fac 100644 --- a/lib/game/components/flipper.dart +++ b/lib/game/components/flipper.dart @@ -79,7 +79,7 @@ class Flipper extends PositionBodyComponent with KeyboardHandler { final sprite = await gameRef.loadSprite(spritePath); positionComponent = SpriteComponent( sprite: sprite, - size: Vector2(width, height), + size: size, ); if (side == BoardSide.right) { From e9776a8d5ef6f643ad04d2ed49856475d059918d Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Thu, 10 Mar 2022 13:38:56 +0000 Subject: [PATCH 31/32] docs: included missing documentation (#29) * chore: ignored lint rue * docs: documented ball.dart * docs: ignored lint rule * docs: documented wall.dart * docs: documented game_over_dialog.dart * docs: fixed typo * docs: included TODO comments --- lib/game/components/ball.dart | 17 ++++++++++++----- lib/game/components/flipper.dart | 1 + lib/game/components/wall.dart | 6 ++++++ lib/game/view/widgets/game_over_dialog.dart | 5 +++++ lib/theme/cubit/theme_cubit.dart | 3 +++ lib/theme/cubit/theme_state.dart | 3 +++ 6 files changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/game/components/ball.dart b/lib/game/components/ball.dart index aeb9be69..cdd1296c 100644 --- a/lib/game/components/ball.dart +++ b/lib/game/components/ball.dart @@ -13,24 +13,26 @@ class Ball extends PositionBodyComponent Ball({ required Vector2 position, }) : _position = position, - super(size: ballSize); - - static final ballSize = Vector2.all(2); + super(size: Vector2.all(2)); + /// The initial position of the [Ball] body. final Vector2 _position; + /// Asset location of the sprite that renders with the [Ball]. + /// + /// Sprite is preloaded by [PinballGameAssetsX]. static const spritePath = 'components/ball.png'; @override Future onLoad() async { await super.onLoad(); final sprite = await gameRef.loadSprite(spritePath); - positionComponent = SpriteComponent(sprite: sprite, size: ballSize); + positionComponent = SpriteComponent(sprite: sprite, size: size); } @override Body createBody() { - final shape = CircleShape()..radius = ballSize.x / 2; + final shape = CircleShape()..radius = size.x / 2; final fixtureDef = FixtureDef(shape)..density = 1; @@ -42,6 +44,11 @@ class Ball extends PositionBodyComponent return world.createBody(bodyDef)..createFixture(fixtureDef); } + /// Removes the [Ball] from a [PinballGame]; spawning a new [Ball] if + /// any are left. + /// + /// Triggered by [BottomWallBallContactCallback] when the [Ball] falls into + /// a [BottomWall]. void lost() { shouldRemove = true; diff --git a/lib/game/components/flipper.dart b/lib/game/components/flipper.dart index bd071b93..2797b07c 100644 --- a/lib/game/components/flipper.dart +++ b/lib/game/components/flipper.dart @@ -148,6 +148,7 @@ class Flipper extends BodyComponent with KeyboardHandler { // TODO(erickzanardo): Remove this once the issue is solved: // https://github.com/flame-engine/flame/issues/1417 + // ignore: public_member_api_docs final Completer hasMounted = Completer(); @override diff --git a/lib/game/components/wall.dart b/lib/game/components/wall.dart index b784b8cb..f5a15af5 100644 --- a/lib/game/components/wall.dart +++ b/lib/game/components/wall.dart @@ -6,13 +6,18 @@ import 'package:pinball/game/components/components.dart'; /// {@template wall} /// A continuos generic and [BodyType.static] barrier that divides a game area. /// {@endtemplate} +// TODO(alestiago): Remove [Wall] for [Pathway.straight]. class Wall extends BodyComponent { + /// {@macro wall} Wall({ required this.start, required this.end, }); + /// The [start] of the [Wall]. final Vector2 start; + + /// The [end] of the [Wall]. final Vector2 end; @override @@ -39,6 +44,7 @@ class Wall extends BodyComponent { /// [BottomWallBallContactCallback]. /// {@endtemplate} class BottomWall extends Wall { + /// {@macro bottom_wall} BottomWall(Forge2DGame game) : super( start: game.screenToWorld(game.camera.viewport.effectiveSize), diff --git a/lib/game/view/widgets/game_over_dialog.dart b/lib/game/view/widgets/game_over_dialog.dart index 586d6c56..9d1c61b0 100644 --- a/lib/game/view/widgets/game_over_dialog.dart +++ b/lib/game/view/widgets/game_over_dialog.dart @@ -1,6 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:pinball/game/game.dart'; +/// {@template game_over_dialog} +/// [Dialog] displayed when the [PinballGame] is over. +/// {@endtemplate} class GameOverDialog extends StatelessWidget { + /// {@macro game_over_dialog} const GameOverDialog({Key? key}) : super(key: key); @override diff --git a/lib/theme/cubit/theme_cubit.dart b/lib/theme/cubit/theme_cubit.dart index 7ba79e59..94eba4a6 100644 --- a/lib/theme/cubit/theme_cubit.dart +++ b/lib/theme/cubit/theme_cubit.dart @@ -1,3 +1,6 @@ +// ignore_for_file: public_member_api_docs +// TODO(allisonryan0002): Document this section when the API is stable. + import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:pinball_theme/pinball_theme.dart'; diff --git a/lib/theme/cubit/theme_state.dart b/lib/theme/cubit/theme_state.dart index 13b3ea5f..078f5c84 100644 --- a/lib/theme/cubit/theme_state.dart +++ b/lib/theme/cubit/theme_state.dart @@ -1,3 +1,6 @@ +// ignore_for_file: public_member_api_docs +// TODO(allisonryan0002): Document this section when the API is stable. + part of 'theme_cubit.dart'; class ThemeState extends Equatable { From b92ee475a29b53e931b0b802cc6a8b3d5135d5f5 Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Thu, 10 Mar 2022 10:44:22 -0300 Subject: [PATCH 32/32] fix: misisng doc --- lib/game/components/flipper.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/game/components/flipper.dart b/lib/game/components/flipper.dart index 0a35d24a..16754ed3 100644 --- a/lib/game/components/flipper.dart +++ b/lib/game/components/flipper.dart @@ -46,6 +46,9 @@ class Flipper extends PositionBodyComponent with KeyboardHandler { ], ); + /// Asset location of the sprite that renders with the [Flipper]. + /// + /// Sprite is preloaded by [PinballGameAssetsX]. static const spritePath = 'components/flipper.png'; /// The width of the [Flipper].