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

GAMEOBJECT_DRAG and GAMEOBJECT_DRAG_END events return different x/y values #7028

Open
Stever1388 opened this issue Feb 3, 2025 · 4 comments
Assignees

Comments

@Stever1388
Copy link

Stever1388 commented Feb 3, 2025

Version

  • Phaser Version: Phaser 3.87.0
  • Operating system: Windows
  • Browser: Chrome

Description

The documentation for GAMEOBJECT_DRAG and GAMEOBJECT_DRAG_END both say that the x and y values are in "world space", however this doesn't seem to be correct because if you are dragging an object that is inside of a container, you can still use the values directly to set the position of the object to where the mouse is.

However, there is another contradiction which is the GAMEOBJECT_DRAG_END x and y values seem to be in the local space of the object being dragged, so they differ from the GAMEOBJECT_DRAG values.

Example Test Code

export default class DragEventIssue extends Phaser.Scene {
  create() {
    this.add.rectangle(this.scale.width * .75, this.scale.height / 2, 100,100, 0xff0000).setInteractive({ draggable: true })
      .on(Phaser.Input.Events.GAMEOBJECT_DRAG, (pointer, x, y) => console.log(x,y))
      .on(Phaser.Input.Events.GAMEOBJECT_DRAG_END, (pointer, x, y) => console.log(x,y)); // should return the same values but doesn't

    const r = this.add.rectangle(this.scale.width / 4, this.scale.height / 2, 100,100, 0xff0000).setInteractive({ draggable: true })
      .on(Phaser.Input.Events.GAMEOBJECT_DRAG, (pointer, x, y) => {
        console.log(x,y);
        r.setPosition(x,y);
      })
      .on(Phaser.Input.Events.GAMEOBJECT_DRAG_END, (pointer, x, y) => console.log(x,y)); // should return the same values but doesn't
  }
}

Additional Information

The documentation should probably be updated to clarify that the x/y values are in the "parent space" - which might be the scene and thus "world space" but might be in a container's space. The GAMEOBJECT_DRAG_END values should be fixed to return the same values as the GAMEOBJECT_DRAG event, or at least the documentation should be updated to indicate what space they are in to avoid confusion. This may affect other DRAG events such as DRAG but I haven't checked.

@zekeatchan zekeatchan self-assigned this Feb 5, 2025
zekeatchan added a commit that referenced this issue Feb 5, 2025
@zekeatchan zekeatchan reopened this Feb 5, 2025
@zekeatchan
Copy link
Collaborator

Hi @Stever1388. Here is an example of implementing the drag and dragend events.

const r1 = this.add.rectangle(this.scale.width * .75, this.scale.height / 2, 100, 100, 0xff0000).setInteractive({ draggable: true });
const r2 = this.add.rectangle(this.scale.width / 4, this.scale.height / 2, 100, 100, 0xff0000).setInteractive({ draggable: true });

this.input.on('drag', (pointer, gameObject, dragX, dragY) => {
    if (gameObject === r2)
    {
        r2.setPosition(dragX, dragY);
    }
    console.log('drag', dragX, dragY);
});

this.input.on('dragend', (pointer, gameObject, dropped) => {
    console.log('dragend', pointer.worldX, pointer.worldY);
});

@Stever1388
Copy link
Author

Hi @Stever1388. Here is an example of implementing the drag and dragend events.

const r1 = this.add.rectangle(this.scale.width * .75, this.scale.height / 2, 100, 100, 0xff0000).setInteractive({ draggable: true });
const r2 = this.add.rectangle(this.scale.width / 4, this.scale.height / 2, 100, 100, 0xff0000).setInteractive({ draggable: true });

this.input.on('drag', (pointer, gameObject, dragX, dragY) => {
if (gameObject === r2)
{
r2.setPosition(dragX, dragY);
}
console.log('drag', dragX, dragY);
});

this.input.on('dragend', (pointer, gameObject, dropped) => {
console.log('dragend', pointer.worldX, pointer.worldY);
});

That's for the global drag events (via listening on the InputPlugin). My issue is with the drag events on a specific game object. Perhaps the issue/solution is to change the gameobject GAMEOBJECT_DRAG_END callback to mirror the global one - instead of it returning pointer, x, y, dropped, it can just return pointer, gameObject, dropped.

Also note that your solution doesn't work if the gameobject is inside a container that isn't at (0,0) world space. The thing about the gameobject drag event callback is that the x/y values are adjusted for the gameobjects parent hierarchy, so you can use them directly.

If you put one of the rects inside a container at position 500,500 you will see what I mean, using pointer.worldX/worldY will result in the rect not being placed in the correct/expected position.

@zekeatchan
Copy link
Collaborator

zekeatchan commented Feb 6, 2025

Here is another implementation for local drag events:

const r2 = this.add.rectangle(this.scale.width / 4, this.scale.height / 2, 100, 100, 0xff0000)
    .setInteractive({ draggable: true });

r2.on(Phaser.Input.Events.GAMEOBJECT_DRAG, function (pointer, x, y)
{
    console.log('GAMEOBJECT_DRAG', x, y);
    this.setPosition(x, y);
}, r2);

r2.on(Phaser.Input.Events.GAMEOBJECT_DRAG_END, function (pointer, x, y) 
{
    const localX = this.x + x;
    const localY = this.y + y;
    console.log('GAMEOBJECT_DRAG_END', localX, localY);
}, r2);

instead of chaining the event listeners, it's separated so you can add a context to each listener to access the gameObject's properties via this:
gameObject.on(event, function, context);

Let me know if this works for you or if you have further questions.

@Stever1388
Copy link
Author

Here is another implementation for local drag events:

const r2 = this.add.rectangle(this.scale.width / 4, this.scale.height / 2, 100, 100, 0xff0000)
.setInteractive({ draggable: true });

r2.on(Phaser.Input.Events.GAMEOBJECT_DRAG, function (pointer, x, y)
{
console.log('GAMEOBJECT_DRAG', x, y);
this.setPosition(x, y);
}, r2);

r2.on(Phaser.Input.Events.GAMEOBJECT_DRAG_END, function (pointer, x, y)
{
const localX = this.x + x;
const localY = this.y + y;
console.log('GAMEOBJECT_DRAG_END', localX, localY);
}, r2);
instead of chaining the event listeners, it's separated so you can add a context to each listener to access the gameObject's properties via this: gameObject.on(event, function, context);

Let me know if this works for you or if you have further questions.

This still doesn't work correctly - the localX/Y values are still no where near what they need to be in order to actually set the position of the object to where it should be. It also doesn't fix the issue that the documentation for both GAMEOBJECT_DRAG and GAMEOBJECT_DRAG_END events says the x/y values are in "world space" when they are definitely not.

I also think another issue is that there's just inconsistency in the two events - why are you able to do obj.setPosition(x,y) in GAMEOBJECT_DRAG but not in GAMEOBJECT_DRAG_END, why does GAMEOBJECT_DRAG_END return different values? Is there a reason for that - and if there is, the documentation should be updated to reflect it. Also the fact that DRAG_END and GAMEOBJECT_DRAG_END have different callback parameters (other than the gameobject param). DRAG_END doesn't even include x/y values (DRAG does though). Perhaps GAMEOBJECT_DRAG_END doesn't need to return x/y values? And that would solve some of this confusion?

Here's a more complete code example, which includes the container example and also your updated code.

export default class DragEventIssue extends Phaser.Scene {
  create() {
    const r0 = this.add.rectangle(this.scale.width * .75, this.scale.height * 0.75, 100,100, 0xff0000).setInteractive({ draggable: true })
      .on(Phaser.Input.Events.GAMEOBJECT_DRAG, (pointer, x, y) => {
        r0.setPosition(x,y);
        console.log(x,y);
      })
      .on(Phaser.Input.Events.GAMEOBJECT_DRAG_END, (pointer, x, y) => {
        r0.setPosition(x,y); // this does not work as expected
        console.log(x,y);

        const localX = r0.x + x;
        const localY = r0.y + y;
        console.log(localX, localY);
        r0.setPosition(localX,localY); // this does not work either
      });

    const r1 = this.add.rectangle(this.scale.width * .75 + 150, this.scale.height * 0.75, 100,100, 0xff0000).setInteractive({ draggable: true })
      .on(Phaser.Input.Events.GAMEOBJECT_DRAG, (pointer, x, y) => {
        r1.setPosition(x,y);
        console.log(x,y);
      })
      .on(Phaser.Input.Events.GAMEOBJECT_DRAG_END, (pointer, x, y) => {
        r1.setPosition(x,y); // this does not work as expected
        console.log(x,y);

        const localX = r1.x + x;
        const localY = r1.y + y;
        console.log(localX, localY);
        r1.setPosition(localX,localY); // this does not work either
      });

    const c = this.add.container(250,250);
    const r2 = this.add.rectangle(0,0, 100,100, 0xff0000).setInteractive({ draggable: true })
      .on(Phaser.Input.Events.GAMEOBJECT_DRAG, (pointer, x, y) => {
        r2.setPosition(x,y);
        console.log(x,y);
      })
      .on(Phaser.Input.Events.GAMEOBJECT_DRAG_END, (pointer, x, y) => {
        r2.setPosition(x,y); // this does not work as expected
        console.log(x,y);

        const localX = r2.x + x;
        const localY = r2.y + y;
        console.log(localX, localY);
        r2.setPosition(localX,localY); // this does not work either
      });

    const r3 = this.add.rectangle(150,0, 100,100, 0xff0000).setInteractive({ draggable: true })
      .on(Phaser.Input.Events.GAMEOBJECT_DRAG, (pointer, x, y) => {
        r3.setPosition(x,y);
        console.log(x,y);
      })
      .on(Phaser.Input.Events.GAMEOBJECT_DRAG_END, (pointer, x, y) => {
        r3.setPosition(x,y); // this does not work as expected
        console.log(x,y);

        const localX = r3.x + x;
        const localY = r3.y + y;
        console.log(localX, localY);
        r3.setPosition(localX,localY); // this does not work either
      });

    c.add([r2,r3]);
  }

Note that this isn't a context or scope issue.

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

No branches or pull requests

3 participants