Smoothing Rotation on Drag

Hi,

I posted recently and got some great suggestions and I’m hoping I could get some further input

I’m trying to drag a sprite and have it rotate to line up with the direction of the mouse cursor, I have my drag event handlers setup as shown in the code snippet below:

        // Center the object on the cursor on drag start 'pick up'
        this.input.on('dragstart', function (pointer, gameObject) {


            gameObject.x = pointer.x
            gameObject.y = pointer.y

        });

        this.input.on('drag', function (pointer, gameObject, dragX, dragY) {

            var points = pointer.getInterpolatedPosition(30);

            // Smoothly draw a point for each interpolated position, stops scattering effect
            points.forEach(function (p) {

                // Points should be drawn on the 'tip', so need to offset
                rt.draw(point, p.x, p.y - 28);
                rt.draw(point, p.x + 16, p.y - 28);
                rt.draw(point, p.x - 16, p.y - 28);

            });


            // Rotate the object on drag
            p1x = gameObject.x
            p1y = gameObject.y
            p2x = pointer.x
            p2y = pointer.y
            dx = p2x - p1x
            dy = p2y - p1y

            let targetRad = Math.atan2(dy, dx)
                targetRad += (Math.PI/2) // Phaser uses rh clockwise rotation (0 = right) so offset 
            
            gameObject.rotation = targetRad

            // Move the object on drag
            gameObject.x = pointer.x
            gameObject.y = pointer.y

        });

This works however it seems quite erratic especially at lower speeds and I’d like to smooth it out if possible, .gif below should give some ideas.

As the distance between object/cursor increases I get a smoother rotation which makes sense and i’ve tried limiting rotation to only apply once that distance is some arbitrary value but this hinders things when you want to slowly drag the object as rotation isn’t applied

Would anyone have any ideas/suggestions or is there some function already part of Phaser that handles this?

Worst case I could lock the rotation to fixed directions like compass points depending on where the angle falls but I’d really like something more natural looking

Thanks again, any thoughts are appreciated

What if you tried calculating the rotation of the rake/pitch fork :sweat_smile: from the bottom of the handle instead of the center of the sprite?

I think it appears smoother at faster speeds because your mouse is more often ahead of the point being used to calculate the rotation.

And it is less the case when moving slowly.

1 Like
gameObject.rotation = Phaser.Math.Angle.RotateTo(
  gameObject.rotation,
  targetRad,
  0.05 * Math.PI // delta
);
2 Likes

RotateTo seems to help a little, but I think the issue is the cursor being too close to the center and the calculated angle coming out too large, if you have only a 1 pixel distance then you’re only going to get angles relating to the 8 possible points surrounding the center

I like the idea of pushing the point of rotation away from the cursor, will give that a shot and report back

Just an idea, if you filter the distance?

1 Like

I did try only moving once the magnitude between center/cursor is greater than an arbitrary value - helped with the rotation but movement became choppy as it’d only redraw once that distance was reached, even pushing the position updates into the interpolated points loop didnt seem to help

Might be worth another try though, is there an equivalent of RotateTo but for movement that could more smoothly move the object to a the cursor point?

I would use a distance threshold for changing the angle, but not the position.

1 Like

Tried it and unfortunately I end up with no rotation at very low drag speeds (threshold never hit) or choppy rotation at ‘medium’ drag speeds where the cursor occasionally hits the threshold

If I get an hour this evening I’ll give the offset rotation point a test, not had chance today
Another idea I might try is reducing the applied rotation by some factor depending on the distance, to ‘dampen’ it a at short distances, I could just adjust the delta in rotateTo to something really small

You might try using pointer.angle or pointer.velocity instead. pointer.angle is noisy at low speeds, but it will act the same no matter where the cursor is placed relative to the sprite.

2 Likes

There is also pointer.motionFactor. You can reduce that from 0.2 to 0.1, for example, to smooth pointer.angle more.

2 Likes

Can give it a shot but how do you access pointer.angle?

It doesn’t seem to be an available property in the callback pointer, looks like there is a prevPosition though so I guess you could calculate it yourself

Not sure if the documentation is off or I’m misunderstanding, it talks about using InputPlugin.activePointer to access the reference but this seems instead to be input.manager.activePointer

There’s no Pointer#angle before Phaser v3.16.

Since v3.16 it is on pointer in the callbacks or this.input.activePointer in the scene.

1 Like

Ah I see, the example I’d built on was 3.15, updated to 3.23 now

And wow, definitely an improvement - thank you, saves me having to manually do basically the same thing with an offset rotation point

3 Likes