mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 10:38:17 +08:00
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:
@ -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).
|
||||
|
||||
|
||||
@ -22,7 +22,6 @@ class AdvancedParallaxGame extends BaseGame {
|
||||
final parallax = ParallaxComponent.fromParallax(
|
||||
Parallax(
|
||||
await Future.wait(layers),
|
||||
size,
|
||||
baseVelocity: Vector2(20, 0),
|
||||
),
|
||||
);
|
||||
|
||||
@ -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),
|
||||
);
|
||||
|
||||
@ -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),
|
||||
);
|
||||
|
||||
@ -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',
|
||||
);
|
||||
}
|
||||
|
||||
22
examples/lib/stories/parallax/small_parallax.dart
Normal file
22
examples/lib/stories/parallax/small_parallax.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@ -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>()),
|
||||
|
||||
42
packages/flame/test/components/parallax_test.dart
Normal file
42
packages/flame/test/components/parallax_test.dart
Normal 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user