2
\$\begingroup\$

I'm writing a simple game using Phaser 3.5.1. I made a 20X20 over world tile map for my hero to walk around on with the camera following the hero. I was able to implement tile based movement using tweens. This works but whenever the camera moves 1 pixel lines flash on the tile map perpendicular to the direction I move. Example uses a static map layer:

Static map layer no rounded pixels

As you can see I was also getting some weird artifacts on my sprite even when the camera wasn't moving, but after reading about rounded pixels I was able to eliminate that on my sprite by setting roundedPixels: true in my game config, but now the sprite jitters when the lines appear. Example uses static map layer with rounded pixels:

Static map layer with rounded pixels

I also experimented with static vs dynamic layers for my map. When I have a dynamic map layer without rounded pixels the result is the same as my static map layer without rounded pixels.

Dynamic map layer no rounded pixels

When I use rounded pixels on a dynamic map everything gets really jittery. Interestingly when the camera doesn't move the map shakes, and when the camera does move the sprite jitters.

Dynamic map with rounded pixels

Regardless of the map type or rounded pixels setting, the longer the tween's duration the worse the problem gets. In my examples I used 2 second tweens to emphasize the problem.

How do I fix this problem? In a perfect world I'd like to keep using tweens for tile based movement, keep a static map, and just update some config value. If that isn't going to work what approach should I take?


I'm happy to add code to my question if people think it would be helpful.

\$\endgroup\$
1
  • \$\begingroup\$ your answer fixed this same issue for myself! Thank you for asking the question and finding the answer. \$\endgroup\$ Commented Jun 26, 2021 at 23:09

2 Answers 2

3
\$\begingroup\$

I was able to fix the problem by setting following a point instead of following my hero sprite. The core problem was the camera wasn't moving in round pixels. By making that point move in round pixels the problem was solved for both static and dynamic map layers. That combined with setting the round pixels parameter for the game cleaned up the jitter, lines, and artifacts around the sprite during motion. I got the idea from an old forum post. My solution is just an extremely simplified version of lewster32's suggestion.

My initial code in the create method looked like:

create() {
    // Create and display the overworld map.
    this.map = this.make.tilemap({ key: "overworld" });
    this.tileset = this.map.addTilesetImage("dragTilesSmall", "tiles");
    this.activeLayer = this.map.createStaticLayer("overworld", this.tileset, 0, 0);

    // The hero of the game
    this.hero = new Hero({ scene: this, key: "playerSprites", x: 16 * 5, y: 16 * 4 });

    // Setup the camera
    this.cameras.main.startFollow(this.hero);
    this.cameras.main.setBounds(0, 0, this.activeLayer.width * this.activeLayer.scaleX, this.activeLayer.height * this.activeLayer.scaleY);

    // Setup the inputs
    this.input.keyboard.on("keydown", this.handleKey, this);
}

The only thing that changed in the create function was I made the camera section follow a point instead of the hero. To update the location of the point I added an update function with two lines of code.

// This is the new "Setup camera section"
this.cameras.main.setBounds(0, 0, this.activeLayer.width * this.activeLayer.scaleX, this.activeLayer.height * this.activeLayer.scaleY);
this.cameraDolly = new Phaser.Geom.Point(this.hero.x, this.hero.y);
this.cameras.main.startFollow(this.cameraDolly);

// This is the update function I added
update() {
    // Move the camera dolly in round pixels
    this.cameraDolly.x = Math.floor(this.hero.x);
    this.cameraDolly.y = Math.floor(this.hero.y);
}

To set the roundPixels property you just need to add it to the game config. here's what mine looks like:

const config = {
    parent: "GameContainer",
    width: 160,
    height: 160,
    pixelArt: true,
    roundPixels: true,
    scene: [BootScene, MainScene],
    zoom: 5
};

const game = new Phaser.Game(config);
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks for sharing the solution. I was having a similar problem, where the tiles were rendered with an additional 1px line of the adjacent tile (in the tileset image). I found that, to fix the issue, roundPixels is not very relevant in my case, but setting antialias: false did the trick. \$\endgroup\$
    – afsantos
    Commented May 10, 2018 at 17:04
1
\$\begingroup\$

For me, nothing that I've found online worked so far. But I've managed to find a solution though. The difference is that my game was fully controlled by the backend, so no physics involved on the frontend. Only a dummy change of position.

Using the solution Erik mentioned made my character move jittery which is not nice but might work for some games.

The solution that worked for me on Phaser3:

this.camera.startFollow(hero, true); // second parameter sets roundPixels to true on camera

Nothing new so far. I bet most of the answers that you found said this.

The trick is actually in the movement. There is a term called Sub-Pixel rendering. The thing is that if you move the character by fractions of pixels, you will have render issues somewhere. ( This is especially on elements that are followed by camera )

Just use Math.round(newX), Math.round(newY) when changing position for your hero.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .