Skip to content

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

Closed
@SBCGames

Description

@SBCGames

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!

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions