Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Property value in last frame of tween is set directly to end value instead of value returned from ease function #6939

Closed
SBCGames opened this issue Nov 6, 2024 · 2 comments
Assignees

Comments

@SBCGames
Copy link

SBCGames commented Nov 6, 2024

Version

  • Phaser Version: 3.86.0
  • Operating system: all
  • Browser: all

Description

This issue is somewhere between bug and feature request... When tween is complete, the property value is set directly to end value instead of value returned by ease function with end value as parameter. If the ease function returns f(0) = 0 and f(1) = 1, then everything works well. But, if it is not true and, for example, f(1) = 0, you will get noticable glitch.

What it is good for to return other value than 1 for f(1)? For example if you want an object to bounce in size for some reason - then the ease function can be f(v) = sin(v * PI). This function starts and ends with value equal to zero and the tween end value in fact works as amplitude. With current code it works except for the last frame - the property is not set to f(1) * endValue, but to endValue.

Another example is object(for example button) pulsing endlessly with sinewave. It works except for very last frame in each repeat - there is ugly glitch.

Found workaround and suggested solution is in part Additional Information

Example Test Code

example 1 - bounce sprite - sprite starts with scale 1, in the half of the duration its scale is 1.2, last frame before end it is close to 1 and on last frame it jumps to 1.2 (instead of 1)

        const spr = this.add.sprite(0, 0, "anyAtlas", "anySprite");

        this.tweens.add({
            targets: spr,
            duration: 1000,

            scaleX: 1.2,
            scaleY: 1.2,

            ease: (k: number) => {
                return Math.sin(k * Math.PI);
            },
        });

example 2 - pulse sprite - the same as above. Each last frame of each repeat, the scale jumps to 1.2 (for single frame) instead of 1

        const spr = this.add.sprite(0, 0, "anyAtlas", "anySprite");

        this.tweens.add({
            targets: spr,
            duration: 2000,

            scaleX: 1.2,
            scaleY: 1.2,

            repeat: 5,

            ease: (k: number) => {
                return Math.sin(k * Math.PI * 2);
            },
        });

Additional Information

workaround
As a workaround it is possible to overwrite object values in onRepeat/onComplete callbacks. Like this for pulsing sprite:

        this.tweens.add({
               :
               :
            onRepeat: () => { spr.scaleX = spr.scaleY = 1; },
            onComplete: () => { spr.scaleX = spr.scaleY = 1; }
        });

You have to add onRepeat if tween is repeating and onComplete if not repeating or repating is not endless.

solution
Suggested solution is an update to code in TweenData.js class in update function. Current code is this part of TweenData.js

It is possible to refactor it to this:

            if (!forward)
            {
                progress = 1 - progress;
            }

            var v = this.ease(progress);

            if (this.interpolation)
            {
                this.current = this.interpolation(this.interpolationData, v);
            }
            else
            {
                this.current = this.start + ((this.end - this.start) * v);
            }

            target[key] = this.current;


            if (complete)
            {
                if (forward)
                {
                    if (this.hold > 0)
                    {
                        this.elapsed = this.hold;

                        this.setHoldState();
                    }
                    else
                    {
                        this.setStateFromEnd(diff);
                    }
                }
                else
                {
                    this.setStateFromStart(diff);
                }
            }

It now passes the last value through ease function. Value of progress is already clamped to 0-1 range, so it should not break anything. And it also saves a few lines!

@photonstorm
Copy link
Collaborator

Thanks, have merged into main and will be part of 3.87 release.

@photonstorm
Copy link
Collaborator

Hmm, in hindsight, this actually breaks the expected result for a NumberTween. Time to unpick this I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants