Using Flutter Flame I'm trying to create a parallax background for a side scrolling game, where the camera focuses on the hero. I've only found examples of a constant speed scrolling parallax, but not one that respects the camera movement in the game world.
If I give a non zero baseVelocity
to the parallax, it will keep scrolling even if the camera is not moving. If I set baseVelocity
to zero, the parallax backgrounds will move as the camera moves, but the actual parallax effect is not visible (i.e. all layers just scroll uniformly as the camera moves).
Is there a setup where I can use camera.followComponent
and the built-in ParallaxComponent
so that the background layers will move with a parallax effect, when the camera follows the subject? Or do I have to implement my own parallax background that respects camera movements?
This is what I've tried:
Parallax background component:
class BackgroundComponent extends ParallaxComponent {
double? lastCameraX;
@override
Future<void> onLoad() async {
super.onLoad();
parallax = await gameRef.loadParallax(
_imageNames,
// baseVelocity: Vector2(0.0, 0.0),
velocityMultiplierDelta: Vector2(1.8, 1.0),
alignment: Alignment.center,
);
}
static final _imageNames = [
ParallaxImageData('background/parallax-mountain-bg.png'),
ParallaxImageData('background/parallax-mountain-mountain-far.png'),
ParallaxImageData('background/parallax-mountain-mountains.png'),
ParallaxImageData('background/parallax-mountain-trees.png'),
ParallaxImageData('background/parallax-mountain-foreground-trees.png'),
];
}
FlameGame onload:
@override
Future<void> onLoad() async {
await super.onLoad();
Flame.device.setLandscape();
add(BackgroundComponent()); // This is the parallax background
final hero= Hero();
add(hero);
camera.speed = 1;
camera.followComponent(hero, relativeOffset: const Anchor(0.2, 1));
}
UPDATE Based on spydon's answer I ended up with the following parallax component, and it works now as intended. I.e. the parallax effect is tied to the camera movement.
class BackgroundComponent extends ParallaxComponent {
Vector2 lastCameraPosition = Vector2.zero();
// here comes onLoad(), loading the actual images - not relevant
// ...
@override
Future<void> update(double dt) async {
super.update(dt);
final cameraPosition = gameRef.camera.position;
final delta = dt > threshold ? 1.0 / (dt * framesPerSec) : 1.0;
final baseVelocity = cameraPosition
..sub(lastCameraPosition)
..multiply(backgroundVelocity)
..multiply(Vector2(delta, delta));
parallax!.baseVelocity.setFrom(baseVelocity);
lastCameraPosition.setFrom(gameRef.camera.position);
}
static final backgroundVelocity = Vector2(3.0, 0);
static const framesPerSec = 60.0;
static const threshold = 0.005;
}