diff --git a/doc/components.md b/doc/components.md index 9a6ec7810..ea6afd298 100644 --- a/doc/components.md +++ b/doc/components.md @@ -355,7 +355,6 @@ final layers = images.map((image) => ParallaxLayer(await image, velocityMultipli final parallaxComponent = ParallaxComponent.fromParallax( Parallax( await Future.wait(layers), - size, // size is a property on the Game class baseVelocity: Vector2(50, 0), ), ); @@ -376,6 +375,9 @@ The `Parallax` file contains an extension of the game which adds `loadParallax`, and `loadParallaxImage` so that it automatically uses your game's image cache instead of the global one. The same goes for the `ParallaxComponent` file, but that provides `loadParallaxComponent`. +If you want a fullscreen `ParallaxComponent` simply omit the `size` argument and it will take the +size of the game, it will also resize to fullscreen when the game changes size or orientation. + Three example implementations can be found in the [examples directory](https://github.com/flame-engine/flame/tree/main/examples/lib/stories/parallax). diff --git a/examples/lib/stories/parallax/advanced.dart b/examples/lib/stories/parallax/advanced.dart index 5dbb58841..4d49b5981 100644 --- a/examples/lib/stories/parallax/advanced.dart +++ b/examples/lib/stories/parallax/advanced.dart @@ -22,7 +22,6 @@ class AdvancedParallaxGame extends BaseGame { final parallax = ParallaxComponent.fromParallax( Parallax( await Future.wait(layers), - size, baseVelocity: Vector2(20, 0), ), ); diff --git a/examples/lib/stories/parallax/component.dart b/examples/lib/stories/parallax/component.dart index a790eba38..c1b5a69d9 100644 --- a/examples/lib/stories/parallax/component.dart +++ b/examples/lib/stories/parallax/component.dart @@ -21,7 +21,6 @@ class MyParallaxComponent extends ParallaxComponent 'parallax/trees.png', 'parallax/foreground-trees.png', ], - size, baseVelocity: Vector2(20, 0), velocityMultiplierDelta: Vector2(1.8, 1.0), ); diff --git a/examples/lib/stories/parallax/no_fcs.dart b/examples/lib/stories/parallax/no_fcs.dart index 2f291eec7..5684258c5 100644 --- a/examples/lib/stories/parallax/no_fcs.dart +++ b/examples/lib/stories/parallax/no_fcs.dart @@ -22,7 +22,7 @@ class NoFCSParallaxGame extends Game { 'parallax/trees.png', 'parallax/foreground-trees.png', ], - size, + size: size, baseVelocity: Vector2(20, 0), velocityMultiplierDelta: Vector2(1.8, 1.0), ); diff --git a/examples/lib/stories/parallax/parallax.dart b/examples/lib/stories/parallax/parallax.dart index a3a379535..60a3d556b 100644 --- a/examples/lib/stories/parallax/parallax.dart +++ b/examples/lib/stories/parallax/parallax.dart @@ -6,6 +6,7 @@ import 'advanced.dart'; import 'basic.dart'; import 'component.dart'; import 'no_fcs.dart'; +import 'small_parallax.dart'; void addParallaxStories(Dashbook dashbook) { dashbook.storiesOf('Parallax') @@ -13,20 +14,33 @@ void addParallaxStories(Dashbook dashbook) { 'Basic', (_) => GameWidget(game: BasicParallaxGame()), codeLink: baseLink('parallax/basic.dart'), + info: 'Shows the simplest way to use a fullscreen ParallaxComponent', ) ..add( 'Component', (_) => GameWidget(game: ComponentParallaxGame()), codeLink: baseLink('parallax/component.dart'), + info: 'Shows how to do initiation and loading of assets from within an ' + 'extended ParallaxComponent', + ) + ..add( + 'Non-fullscreen', + (_) => GameWidget(game: SmallParallaxGame()), + codeLink: baseLink('parallax/small_parallax.dart'), + info: 'Shows how to create a smaller parallax in the center of the ' + 'screen', ) ..add( 'No FCS', (_) => GameWidget(game: NoFCSParallaxGame()), codeLink: baseLink('parallax/no_fcs.dart'), + info: "Shows how to use the parallax without Flame's component system", ) ..add( 'Advanced', (_) => GameWidget(game: AdvancedParallaxGame()), codeLink: baseLink('parallax/advanced.dart'), + info: 'Shows how to create a parallax with different velocity deltas on ' + 'each layer', ); } diff --git a/examples/lib/stories/parallax/small_parallax.dart b/examples/lib/stories/parallax/small_parallax.dart new file mode 100644 index 000000000..75377d29c --- /dev/null +++ b/examples/lib/stories/parallax/small_parallax.dart @@ -0,0 +1,22 @@ +import 'package:flame/components.dart'; +import 'package:flame/game.dart'; + +class SmallParallaxGame extends BaseGame { + @override + Future onLoad() async { + final component = await loadParallaxComponent( + [ + 'parallax/bg.png', + 'parallax/mountain-far.png', + 'parallax/mountains.png', + 'parallax/trees.png', + 'parallax/foreground-trees.png', + ], + size: Vector2.all(200), + baseVelocity: Vector2(20, 0), + velocityMultiplierDelta: Vector2(1.8, 1.0), + ) + ..position = size / 2; + add(component); + } +} diff --git a/packages/flame/CHANGELOG.md b/packages/flame/CHANGELOG.md index 6c1e9c3c7..409960d7b 100644 --- a/packages/flame/CHANGELOG.md +++ b/packages/flame/CHANGELOG.md @@ -7,6 +7,8 @@ - Add possibility to dynamically change priority of components - Add onCollisionEnd to make it possible for the user to easily detect when a collision ends - Adding test coverage to packages + - Possibility to have non-fullscreen ParallaxComponent + - No need to send size in ParallaxComponent.fromParallax since Parallax already contains it - Fix Text Rendering not working properly - Add more useful methods to the IsometricTileMap component diff --git a/packages/flame/lib/src/components/parallax_component.dart b/packages/flame/lib/src/components/parallax_component.dart index 224ee85f6..aa248ea37 100644 --- a/packages/flame/lib/src/components/parallax_component.dart +++ b/packages/flame/lib/src/components/parallax_component.dart @@ -20,10 +20,9 @@ extension ParallaxComponentExtension on Game { Alignment alignment = Alignment.bottomLeft, LayerFill fill = LayerFill.height, }) async { - final componentSize = size ?? this.size; final component = await ParallaxComponent.load( paths, - size: componentSize, + size: size, baseVelocity: baseVelocity, velocityMultiplierDelta: velocityMultiplierDelta, repeat: repeat, @@ -32,13 +31,14 @@ extension ParallaxComponentExtension on Game { images: images, ); - return component..size.setFrom(componentSize); + return component; } } /// A full parallax, several layers of images drawn out on the screen and each /// layer moves with different velocities to give an effect of depth. class ParallaxComponent extends PositionComponent { + bool isFullscreen = true; Parallax? _parallax; Parallax? get parallax => _parallax; @@ -51,21 +51,31 @@ class ParallaxComponent extends PositionComponent { ParallaxComponent({ Vector2? position, Vector2? size, - }) : super(position: position, size: size); + }) : super(position: position, size: size) { + if (size != null) { + isFullscreen = false; + } + } /// Creates a component from a [Parallax] object. - ParallaxComponent.fromParallax( - this._parallax, { + factory ParallaxComponent.fromParallax( + Parallax parallax, { Vector2? position, - Vector2? size, - }) : super(position: position, size: size); + }) { + return ParallaxComponent( + position: position, + size: parallax.isSized ? parallax.size : null, + )..parallax = parallax; + } @mustCallSuper @override void onGameResize(Vector2 size) { super.onGameResize(size); - this.size.setFrom(size); - parallax?.resize(size); + if (isFullscreen) { + this.size.setFrom(size); + parallax?.resize(size); + } } @override @@ -111,7 +121,7 @@ class ParallaxComponent extends PositionComponent { final component = ParallaxComponent.fromParallax( await Parallax.load( paths, - size, + size: size, baseVelocity: baseVelocity, velocityMultiplierDelta: velocityMultiplierDelta, repeat: repeat, diff --git a/packages/flame/lib/src/parallax.dart b/packages/flame/lib/src/parallax.dart index 16509eaf7..7b84f5b5e 100644 --- a/packages/flame/lib/src/parallax.dart +++ b/packages/flame/lib/src/parallax.dart @@ -13,8 +13,8 @@ import 'game/game.dart'; extension ParallaxExtension on Game { Future loadParallax( - List paths, - Vector2? size, { + List paths, { + Vector2? size, Vector2? baseVelocity, Vector2? velocityMultiplierDelta, ImageRepeat repeat = ImageRepeat.repeatX, @@ -23,7 +23,7 @@ extension ParallaxExtension on Game { }) { return Parallax.load( paths, - size, + size: size, baseVelocity: baseVelocity, velocityMultiplierDelta: velocityMultiplierDelta, repeat: repeat, @@ -193,6 +193,9 @@ class ParallaxLayer { } void render(Canvas canvas) { + if (_paintArea.isEmpty) { + return; + } paintImage( canvas: canvas, image: parallaxImage.image, @@ -235,17 +238,27 @@ enum LayerFill { height, width, none } /// layer moves with different velocities to give an effect of depth. class Parallax { late Vector2 baseVelocity; - Vector2 _size = Vector2.zero(); late Rect _clipRect; final List layers; + bool isSized = false; + late final Vector2 _size; + + /// Do not modify this directly, since the layers won't be resized if you do + Vector2 get size => _size; + set size(Vector2 newSize) { + resize(newSize); + } + Parallax( - this.layers, - Vector2? size, { + this.layers, { + Vector2? size, Vector2? baseVelocity, }) { this.baseVelocity = baseVelocity ?? Vector2.zero(); - resize(size ?? _size); + if (size != null) { + resize(size); + } } /// The base offset of the parallax, can be used in an outer update loop @@ -255,11 +268,15 @@ class Parallax { /// If the `ParallaxComponent` isn't used your own wrapper needs to call this /// on creation. void resize(Vector2 newSize) { - if (newSize != _size) { - _size = newSize; - _clipRect = newSize.toRect(); - layers.forEach((layer) => layer.resize(newSize)); + if (!isSized) { + _size = Vector2.zero(); } + if (newSize != _size || !isSized) { + _size.setFrom(newSize); + _clipRect = _size.toRect(); + layers.forEach((layer) => layer.resize(_size)); + } + isSized |= true; } void update(double dt) { @@ -288,8 +305,8 @@ class Parallax { /// used can also be passed in. /// If no image cache is set, the global flame cache is used. static Future load( - List paths, - Vector2? size, { + List paths, { + Vector2? size, Vector2? baseVelocity, Vector2? velocityMultiplierDelta, ImageRepeat repeat = ImageRepeat.repeatX, @@ -322,7 +339,7 @@ class Parallax { ); return Parallax( layers, - size, + size: size, baseVelocity: baseVelocity, ); } diff --git a/packages/flame/test/anchor_test.dart b/packages/flame/test/anchor_test.dart index 27e57ac71..9a2ff0ff4 100644 --- a/packages/flame/test/anchor_test.dart +++ b/packages/flame/test/anchor_test.dart @@ -4,7 +4,7 @@ import 'package:test/test.dart'; void main() { group('Anchor', () { - test('can parse to and from string', () async { + test('can parse to and from string', () { expect(Anchor.center.toString(), 'center'); expect(Anchor.valueOf('topRight'), Anchor.topRight); @@ -16,12 +16,12 @@ void main() { } }); - test('can parse custom anchor', () async { + test('can parse custom anchor', () { expect(const Anchor(0.2, 0.2).toString(), 'Anchor(0.2, 0.2)'); expect(Anchor.valueOf('Anchor(0.2, 0.2)'), const Anchor(0.2, 0.2)); }); - test('fail to parse invalid anchor', () async { + test('fail to parse invalid anchor', () { expect( () => Anchor.valueOf('foobar'), throwsA(const TypeMatcher()), diff --git a/packages/flame/test/components/parallax_test.dart b/packages/flame/test/components/parallax_test.dart new file mode 100644 index 000000000..934ad0d36 --- /dev/null +++ b/packages/flame/test/components/parallax_test.dart @@ -0,0 +1,42 @@ +import 'package:flame/components.dart'; +import 'package:flame/game.dart'; +import 'package:test/test.dart'; + +class ParallaxGame extends BaseGame { + late final ParallaxComponent parallaxComponent; + late final Vector2? parallaxSize; + + ParallaxGame({this.parallaxSize}) { + onResize(Vector2.all(500)); + } + + @override + Future onLoad() async { + parallaxComponent = await loadParallaxComponent( + [], + size: parallaxSize, + baseVelocity: Vector2(20, 0), + velocityMultiplierDelta: Vector2(1.8, 1.0), + ); + add(parallaxComponent); + } +} + +void main() { + group('parallax test', () { + test('can have non-fullscreen ParallaxComponent', () async { + final parallaxSize = Vector2.all(100); + final game = ParallaxGame(parallaxSize: parallaxSize.clone()); + await game.onLoad(); + game.update(0); + expect(game.parallaxComponent.size, parallaxSize); + }); + + test('can have fullscreen ParallaxComponent', () async { + final game = ParallaxGame(); + await game.onLoad(); + game.update(0); + expect(game.parallaxComponent.size, game.size); + }); + }); +} diff --git a/scripts/check_coverage.dart b/scripts/check_coverage.dart index 987dbf863..6a57aa2c1 100644 --- a/scripts/check_coverage.dart +++ b/scripts/check_coverage.dart @@ -3,8 +3,8 @@ import 'dart:io'; void main(args) { final coverageSummary = args[0] as String; final currentRaw = coverageSummary.replaceFirstMapped( - RegExp(r".* (\d+\.\d+)%.*"), - (matches) => '${matches[1]}', + RegExp(r".* (\d+\.\d+)%.*"), + (matches) => '${matches[1]}', ); final current = double.parse(currentRaw);