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
37.03k stars 7.09k forks source link

When providing frames with duration to anims.create(), 41.6ms is added to every frame (1 frame time @24 FPS) #6712

Closed Nerodon closed 8 months ago

Nerodon commented 9 months ago

Version

Description

When providing durations in animations using an array of frames rather than total duration or FPS, the timing is generally slower than it should, sometimes by over several hundreds of ms or more. This is especially noticeable for quick and snappy animations making it impossible to define animation durations per frame this way.

This scales with the number of frames given to anims.create() EXAMPLE:

    this.anims.create({
            key: 'walk',
            frames: [
                { key: 'brawler', frame: 0, duration: 200 },
                { key: 'brawler', frame: 1, duration: 100 },
                { key: 'brawler', frame: 2, duration: 200 },
                { key: 'brawler', frame: 3, duration: 100 },
                ],
            repeat: 0
        });  

For each frame, the time is extended by 40ms. So a 10 frame animation will have a total runtime of about 400ms longer.

Example Test Code

EXAMPLE JsFiddle

Simply refresh, the time displayed on screen should match the total duration for all the frames. In the fiddle is should be 2400ms, but the animation completes in about 3000ms on Chrome or Firefox browser.

Nerodon commented 9 months ago

I think it's related to the default framerate of 24 FPS and it adds 41.6 ms to each frame. Doing an animation with 1000 frames, duration 1ms per frame I get: 42646ms total remove 1000ms of actual animation and we get 41646ms or pretty close to 41.6ms per frame

Nerodon commented 9 months ago

A quick look in the animationManager This seems to be fixed for Aseprite imports using createFromAseprite() We find there the following:

      // Fix duration to play nice with how the next tick is calculated.
      var msPerFrame = totalDuration / animFrames.length;

      animFrames.forEach(function (entry)
      {
          entry.duration -= msPerFrame;
      });

Applying this manually before calling create() solved the issue for me, but if durations are provided, I would expect the same behavior as createFromAseprite()

samme commented 9 months ago

I think that the description for Phaser.Types.Animations.AnimationFrame.duration is wrong and it should be like Phaser.Animations.AnimationFrame#duration.

Nerodon commented 9 months ago

That is probably a fair point, but It would definitely be nice to set the msPerFrame to be 0 and all durations coming from the supplied frames. Afterall, this is basically required when importing animations from aseprite as the user specifies all frametimes manually, it's odd to not have a built-in way in Phaser to do the same.

rgk commented 8 months ago

To keep it consistent, being that "fix" for Aesprite shouldn't be needed, msPerFrame is no longer added to duration.

Thank you for submitting this issue. We have fixed this and the fix has been pushed to the master branch. It will be part of the next release. If you get time to build and test it for yourself we would appreciate that.

samme commented 8 months ago

I think 2e4b43b is technically a breaking change, because AnimationFrame#duration did correctly describe Phaser's behavior.

sylvainpolletvillard commented 7 months ago

All the animations in my game are running faster after upgrading to 3.80. Could it be related to this ? What's the fix ?

rgk commented 7 months ago

Only when applying a duration to a frame, as it no longer applies an additional msPerFrame, so the duration is the actual length of the frame.

To compare speed of animations you can use this example and change between versions: https://labs.phaser.io/view.html?src=src\animation\show%20animation%20play%20through.js