2

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;
}
András Szepesházi
  • 6,483
  • 5
  • 45
  • 59

1 Answers1

3

First you'll have to set the ParallaxComponent to not move the component itself with the camera (this was a bug from our side, it will be default in the next version).

You do this by setting positionType = PositionType.viewport; in the component (or override that field).

Now you can connect the camera.position to the baseVelocity of your parallax, something like this for example:

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;
}

(The resulting code was written by András Szepesházi).

spydon
  • 9,372
  • 6
  • 33
  • 63
  • Thanks! I've been trying along the same lines but without success. I've also tried your exact solution copy-paste and did not have the desired effect. I'll prepare a minimal repo and post it here. Would greatly appreciate if you could have a look. Cheers – András Szepesházi Feb 15 '22 at 22:35
  • Interesting, what effect does it have? I didn't try it before I replied. – spydon Feb 16 '22 at 07:42
  • Your approach was on spot, I just had to fix a couple of small issues (no call to super.update(), and the velocity calculation was off a bit). I'll add the updated version to my answer. Many thanks for the help! – András Szepesházi Feb 16 '22 at 10:04
  • 1
    Great, I updated my answer so that it is easier for people coming across this question to see the answer. If you want to you could submit a PR with an example for this for the Flame examples site. :) – spydon Feb 16 '22 at 19:17