phaserjs / phaser

Phaser is a fun, free and fast 2D game framework for making HTML5 games for desktop and mobile web browsers, supporting Canvas and WebGL rendering.
https://phaser.io
MIT License
36.94k stars 7.08k forks source link

Camera lerp + velocity = quick teleport (noticable in an example) #5018

Closed Kosmoon closed 7 months ago

Kosmoon commented 4 years ago

In this example: Phaser 3 example there is a slight quick random teleportation, the sprite stops and teleports quickly.

To reproduce it, go in one direction for a few seconds and watch the sprite closely. (it seems to happen more often if you lose the window focus and come back to it)

It occurs when the camera is set to follow a sprite and lerp is not (1,1). roundPixels true or false doesn’t fix it.

This issue wasn't present in Phaser 2.

Tested on two computers, on mobile, chrome and firefox.

halilcakar commented 4 years ago

Hi @Kosmoon, I checked the example and played the lerp value a bit,

i can say even if lerp is 1, there is a slightly seeable effect on small teleport like you said.


var config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    physics: {
        default: 'arcade',
        arcade: {
            debug: true,
        }
    },
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};

...

function create ()
{
    .... //same with example
    this.cameras.main.startFollow(player, true, 1, 1);
}

With debug mode on you can see even clear :)

So I wondered if we make the same example without physics how would it be:

var config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    scale: {
        mode: Phaser.Scale.FIT
    },
    physics: {
        default: false,
    },
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};

var player;
var cursors;

var game = new Phaser.Game(config);

function preload ()
{
    this.load.image('bg', 'assets/pics/the-end-by-iloe-and-made.jpg');
    this.load.image('block', 'assets/sprites/block.png');
}

function create ()
{
    //  Set the camera and physics bounds to be the size of 4x4 bg images
    this.cameras.main.setBounds(0, 0, 1920 * 2, 1080 * 2);

    //  Mash 4 images together to create our background
    this.add.image(0, 0, 'bg').setOrigin(0);
    this.add.image(1920, 0, 'bg').setOrigin(0).setFlipX(true);
    this.add.image(0, 1080, 'bg').setOrigin(0).setFlipY(true);
    this.add.image(1920, 1080, 'bg').setOrigin(0).setFlipX(true).setFlipY(true);

    cursors = this.input.keyboard.createCursorKeys();

    player = this.add.image(400, 300, 'block');

    this.cameras.main.startFollow(player, true, 0.05, 0.05);
}

function update ()
{

    if (cursors.left.isDown)
    {
        player.x -= 5;
    }
    else if (cursors.right.isDown)
    {
        player.x += 5;
    }

    if (cursors.up.isDown)
    {
        player.y -= 5;
    }
    else if (cursors.down.isDown)
    {
        player.y += 5;
    }

}

With same example if you remove all of physics and simulate same effect with only changing x, y values, its worked quite okey.

Kosmoon commented 4 years ago

Hi @halilcakar thank you for the reply.

Indeed, i noticed too that it's only occuring when the sprite has a velocity applied to it.

You will probably agree with me that removing physics is not an acceptable solution.

halilcakar commented 4 years ago

Yea I just wanted to check another scenario 😃

Wambosa commented 4 years ago

Hey y'all, I am dealing with this issue as well. For me, I perceived it as my background moving instead of the character moving. Its actually pretty tough to determine what is moving when the "skip" or "teleport happens". I've noticed it on static images that are not being tracked on the camera, but also the tracked sprite.

Here is the solution I have so far that improves both the background/static image jitter as well as the player sprite teleport problem. Still looking to perfect the config and update loop for the best quality. Any additional insight (especially a working code snippet) is greatly appreciated.

https://github.com/photonstorm/phaser/issues/4664#issuecomment-592060468

samme commented 4 years ago

I think this is not exactly a bug but a natural consequence of how fixed-step physics interacts with the camera lerp.

For me it happens when there are 2 physics step per frame instead of 1. That's rare but normal. So the body delta doubles, the camera interpolates to a position behind it, then closes the gap over a few frames. You see a stutter.

samme commented 4 years ago

4989

Kosmoon commented 4 years ago

Nice find @samme !! It actually works. I hope that does not break something else in my code but adding: https://github.com/photonstorm/phaser/pull/4989/commits/7e4c08c27087b4fa0025d07b5644c7e404466ec5 to my phaser.min.js did the trick. This guy is a genious :)

Kosmoon commented 4 years ago

s = e * 0.001; a = true; this._elapsed = 0;

minified

Feavy commented 4 years ago

I don't think #4989 is an acceptable solution because we may not want our game to have faster physics on higher framerate monitors, especially if the game is multiplayer.

Kosmoon commented 4 years ago

I don't think #4989 is an acceptable solution because we may not want our game to have faster physics on higher framerate monitors, especially if the game is multiplayer.

You are right, i ended up reverting the change and had to keep the glitchy bug because i want a consistant physic behavior in my game.

Wambosa commented 4 years ago

Out of curiosity @Kosmoon, Did you try this workaround?

https://github.com/photonstorm/phaser/issues/4664#issuecomment-592060468

I have not been working with phaser recently since i am engaged in another project; yet am still interested in this problem because it really bugs me.

The main things I noticed that will help, not solve the problem completely are:

  1. Don't directly track a physics object with the camera.

  2. Use whole numbers for lerping (this one sucks because often times we need more control).

While this is not a knock out solution, i noticed a significant decrease in stutters/skips.

Finally an additional concept not in that code snippet is to only update the camera lerp every 5 or 10 frames instead of very frame. This has a side effect of making the camera drag even more, which might be acceptable depending on your game. I think I settled on updating camera lerp once every 4 frames.

samme commented 4 years ago

Turning off fixedStep won’t make physics run faster (or slower). It will still simulate 1 physics second per game second, only the delta interval varies.

Feavy commented 4 years ago

I don't know about Arcade but with PlanckJS I can see speed variations between 60 Hz and 144 Hz monitors when using a variable time step. (Looks like PlanckJS doesn't like variable time step very much https://github.com/shakiba/planck.js/issues/74).

Kosmoon commented 4 years ago

Turning off fixedStep won’t make physics run faster (or slower). It will still simulate 1 physics second per game second, only the delta interval varies.

I have some level in my game to test consistancy. Like "auto levels" in Mario maker. They are broken when fixedStep is turned off. Also, testing fixedStep on and off in chrome inspector with device toolbar toggled and switching between "online" and "low-tier mobile" is an easy way to see the difference.

Kosmoon commented 4 years ago

Out of curiosity @Kosmoon, Did you try this workaround?

#4664 (comment)

I have not been working with phaser recently since i am engaged in another project; yet am still interested in this problem because it really bugs me.

The main things I noticed that will help, not solve the problem completely are:

  1. Don't directly track a physics object with the camera.
  2. Use whole numbers for lerping (this one sucks because often times we need more control).

While this is not a knock out solution, i noticed a significant decrease in stutters/skips.

Finally an additional concept not in that code snippet is to only update the camera lerp every 5 or 10 frames instead of very frame. This has a side effect of making the camera drag even more, which might be acceptable depending on your game. I think I settled on updating camera lerp once every 4 frames.

Interesting mate !

Using a dummy non-physic object followed by the camera doesn't fix it for me. What do you mean by "only update the camera lerp every 5 or 10 frames instead of very frame." and how can you achieve that?

Wambosa commented 4 years ago

What do you mean by "only update the camera lerp every 5 or 10 frames instead of very frame." and how can you achieve that?

Here is a quick pseudo code snippet:

  create() {
    this.frame = 0
    ...
  }

  update (time, delta) {
    this.frame++
    this.chara.update(...)

    if (this.frame % 5 === 0) { //only run every 5 frames
      this.cameraFocus.x = Math.round(this.chara.x)
      this.cameraFocus.y = Math.round(this.chara.y)
    }
  }
}

Also @Kosmoon, what version of phaser are you using? I was having issues on 3.21.0 then upgraded to 3.22.0. The upgrade did not fix it for me and my scene unfortunately, just curious what version you are on.

Kosmoon commented 4 years ago

What do you mean by "only update the camera lerp every 5 or 10 frames instead of very frame." and how can you achieve that?

Here is a quick pseudo code snippet:

  create() {
    this.frame = 0
    ...
  }

  update (time, delta) {
    this.frame++
    this.chara.update(...)

    if (this.frame % 5 === 0) { //only run every 5 frames
      this.cameraFocus.x = Math.round(this.chara.x)
      this.cameraFocus.y = Math.round(this.chara.y)
    }
  }
}

Also @Kosmoon, what version of phaser are you using? I was having issues on 3.21.0 then upgraded to 3.22.0. The upgrade did not fix it for me and my scene unfortunately, just curious what version you are on.

I'm using the last version: 3.24.1 updating the cameraFocus every 5 updates made it worst for me.

Wambosa commented 4 years ago

I'm using the last version: 3.24.1 updating the cameraFocus every 5 updates made it worst for me.

Oh bummer, welp. I suppose it only works for me since its a slower moving top-down view. If you need faster camera response times, then I don't think the skip-frame technique works very well.

Feavy commented 4 years ago

I think I have an idea to solve the problem:

  1. Create a dumb game object and make the camera follow it.
  2. Every physic step, save position of the real object you want to follow in an array.
  3. Every render update, get and remove (shift) the first position of this array and teleport the dumb game object to this position.

With a bit of luck frames with 2 physic steps and the ones with 0 will cancel each other and the camera will not fall behind. This is only possible if fps = amount of physic steps per second. If fps is higher, a bit of interpolation will be needed. If fps is lower, quick teleportations are inevitable.

onurusluca commented 1 year ago

Any solutions to this? Having the same problem. Tried a few solutions but no to avail.

photonstorm commented 7 months ago

I'm not seeing this happen at all any more. There was a quite large fix added in 3.60 that resolved camera following (without physics), and if you set the camera lerp to 1:1 in the 'follow user controlled sprite' example the following is perfect with no stuttering. With a lerp value, the camera is clearly lerping, but I'm not witnessing any teleporting. If someone can re-create and demonstrate the issue with v3.80 then I'll happily take another look, but for now I'm suspecting this has been resolved via all the numerous updates that took place in the 4 years since the issue was opened.

The Camera is positioned in the preRender step - so it doesn't matter how many physics updates happened prior to that (0, 1 or more), it's always going to use the followers final x/y positions now.

damian-pastorini commented 7 months ago

UPDATE: I've managed to fix my player jitter with the interpolations, though the issue persist with the background if I set the camera following the player without any interpolation :(


hi @photonstorm , I'm having this issue on 3.70, but in my case I'm not using any physics from Phaser, since I'm creating a multiplayer game the player movement is coming 100% from the server and the position is been set "manually" and not using any speed applied on Phaser.

What I found is:

As said, since I'm setting the position arriving from the server into the sprites, so I need to interpolate the position updates "on my own" in order to make the player movement smooth (but again, this is just changing the sprite X and Y). At this point, is where I'm trying to apply the fix on my code in order to keep the background working, but apparently even the slow camara would still make the player sprite jitter no matter what I do. In result: as soon the camera movement goes of the player "focus" the jitter is visible on the player sprite, as soon the camare get the player as focus, the background jitter.

Unfortunately I don't have a extracted version to show the issue, but if I can't fix it on my own I will try to extract a an example to show.

ncoop720 commented 4 months ago

What I found is:

  • If I set the camera to exact follow the player movement 1:1, the player animation looks ok, but the background jitter (visible here: https://demo.reldens.com/).
  • If I set the camera with a lerp (0.2, 0.2) to make the camera movement slower, the background works fine but the player animation jitter.

@damian-pastorini I am having the same issue as you. Did you ever figure out a solution?

damian-pastorini commented 4 months ago

Hi @ncoop720 , not yet, I'm going to tackle as soon I get some time, in between I've posted a few videos on the phaser discord server showing the issues but didn't got any replies. Ref: https://discord.com/channels/244245946873937922/416623653741133837/1230053213453942855 And the issue is still visible in the example: https://labs.phaser.io/view.html?src=src\camera\follow%20user%20controlled%20sprite.js

You can even see how the screen shakes when the movement stops.