diff --git a/lib/game/components/ball.dart b/lib/game/components/ball.dart new file mode 100644 index 00000000..ce2e4335 --- /dev/null +++ b/lib/game/components/ball.dart @@ -0,0 +1,28 @@ +import 'package:flame_forge2d/body_component.dart'; +import 'package:flutter/material.dart'; +import 'package:forge2d/forge2d.dart'; + +class Ball extends BodyComponent { + Ball({ + required Vector2 position, + }) : _position = position { + // TODO(alestiago): Use asset instead of color when provided. + paint = Paint()..color = const Color(0xFFFFFFFF); + } + + final Vector2 _position; + + @override + Body createBody() { + final shape = CircleShape()..radius = 2; + + final fixtureDef = FixtureDef(shape)..density = 1; + + final bodyDef = BodyDef() + ..userData = this + ..position = _position + ..type = BodyType.dynamic; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } +} diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart new file mode 100644 index 00000000..6a26208c --- /dev/null +++ b/lib/game/components/components.dart @@ -0,0 +1 @@ +export 'ball.dart'; diff --git a/lib/game/game.dart b/lib/game/game.dart index ec8e0824..0a8dac1b 100644 --- a/lib/game/game.dart +++ b/lib/game/game.dart @@ -1,2 +1,3 @@ +export 'components/components.dart'; export 'pinball_game.dart'; export 'view/pinball_game_page.dart'; diff --git a/test/game/components/ball_test.dart b/test/game/components/ball_test.dart new file mode 100644 index 00000000..c4576c68 --- /dev/null +++ b/test/game/components/ball_test.dart @@ -0,0 +1,83 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/game/game.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('Ball', () { + final flameTester = FlameTester(PinballGame.new); + + flameTester.test( + 'loads correctly', + (game) async { + final ball = Ball(position: Vector2.zero()); + await game.ensureAdd(ball); + + expect(game.contains(ball), isTrue); + }, + ); + + group('body', () { + flameTester.test( + 'positions correctly', + (game) async { + final position = Vector2.all(10); + final ball = Ball(position: position); + await game.ensureAdd(ball); + game.contains(ball); + + expect(ball.body.position, position); + }, + ); + + flameTester.test( + 'is dynamic', + (game) async { + final ball = Ball(position: Vector2.zero()); + await game.ensureAdd(ball); + + expect(ball.body.bodyType, equals(BodyType.dynamic)); + }, + ); + }); + + group('first fixture', () { + flameTester.test( + 'exists', + (game) async { + final ball = Ball(position: Vector2.zero()); + await game.ensureAdd(ball); + + expect(ball.body.fixtures[0], isA()); + }, + ); + + flameTester.test( + 'is dense', + (game) async { + final ball = Ball(position: Vector2.zero()); + await game.ensureAdd(ball); + + final fixture = ball.body.fixtures[0]; + expect(fixture.density, greaterThan(0)); + }, + ); + + flameTester.test( + 'shape is circular', + (game) async { + final ball = Ball(position: Vector2.zero()); + await game.ensureAdd(ball); + + final fixture = ball.body.fixtures[0]; + expect(fixture.shape.shapeType, equals(ShapeType.circle)); + expect(fixture.shape.radius, equals(2)); + }, + ); + }); + }); +}