Fix parallax size (#800)

* Non-fullscreen ParallaxComponent

* Updated examples with no size needed

* Formatting

* Add docs for fullscreen parallax

* Fix formatting

* Remove need of nullable size

* Add basic parallax sizing test

* Fix formatting

* Remove unused imports

* Info text for the parallax examples
This commit is contained in:
Lukas Klingsbo
2021-05-20 16:58:49 +02:00
committed by GitHub
parent bc5decd193
commit 8f7c773f82
12 changed files with 141 additions and 34 deletions

View File

@ -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).

View File

@ -22,7 +22,6 @@ class AdvancedParallaxGame extends BaseGame {
final parallax = ParallaxComponent.fromParallax(
Parallax(
await Future.wait(layers),
size,
baseVelocity: Vector2(20, 0),
),
);

View File

@ -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),
);

View File

@ -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),
);

View File

@ -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',
);
}

View File

@ -0,0 +1,22 @@
import 'package:flame/components.dart';
import 'package:flame/game.dart';
class SmallParallaxGame extends BaseGame {
@override
Future<void> 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);
}
}

View File

@ -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

View File

@ -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,22 +51,32 @@ 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);
if (isFullscreen) {
this.size.setFrom(size);
parallax?.resize(size);
}
}
@override
void update(double dt) {
@ -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,

View File

@ -13,8 +13,8 @@ import 'game/game.dart';
extension ParallaxExtension on Game {
Future<Parallax> loadParallax(
List<String> paths,
Vector2? size, {
List<String> 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<ParallaxLayer> 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<Parallax> load(
List<String> paths,
Vector2? size, {
List<String> 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,
);
}

View File

@ -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<AssertionError>()),

View File

@ -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<void> 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);
});
});
}