ScreenToWorld offset

Hi there,

In my project I have some problems with the screenToWorld function.
To show you I have made a new project to show you the problem I am facing.

In short; I have a image following the mouse representing a crosshair. When you click, a bullet will tween from an initial position to 10 units behind the crosshair in world space.

However, there seems to be an offset which I can’t figure out why that is happening. My guess is the problem lies within different screen sizes as on some monitors it just works and on some it doesn’t.

Any ideas why this is happening and how I can fix this?

Short video to show the offset:

Here’s my code:

Shoot.prototype.onMouseUp = function () {
    // clone bullet
    var bullet = this.app.root.findByName('Bullet').clone();

    // start bullet in lower left of screen
    bullet.script.bullet.initPos = this.camera.camera.screenToWorld(0, this.screenRes.y, 1);

    // ---------- Why is the bullet not landing exactly on crosshair?  -------------
    // end the bullet 10 units behind the crosshair.
    bullet.script.bullet.endPos = this.camera.camera.screenToWorld(this.entity.getLocalPosition().x, -this.entity.getLocalPosition().y, 10);

    // add the new bullet to the world
    this.app.root.addChild(bullet);

    // set variables in the bullet script
    bullet.setPosition(bullet.script.bullet.initPos);
    bullet.lookAt(bullet.script.bullet.endPos);

    // rotateLocal(updown, 0, rightleft)
    bullet.rotateLocal(90, 0, 0);
    bullet.enabled = true;
};

To see the full code, here is the project:
https://playcanvas.com/project/1021903/overview/screentoworld

Thanks

In a nutshell, the screenToWorld is expecting browser window mouse coordinates (the xy values that you get from mouse move event).

So you either have to store them privately in the script from the mouse move event, use the xy properties from the mouse up event, or convert the local position of the pc UI element back to window mouse coordinates

ie

    // end the bullet 10 units behind the crosshair.
    bullet.script.bullet.endPos = this.camera.camera.screenToWorld(event.x, event.y, 10);

1 Like

Hey @yaustar ,

Thanks for the quick reply.
In the original project I am not using a mouse to control the crosshair.
So the best solution would be to calculate the end position by converting the local position of the UI element (crosshair) to window mouse coordinates.

Just how would I convert it to window mouse coordinates?

In which case, you want to do the opposite of this: World to UI Screen space | Learn PlayCanvas

The UI Entity position (entity.getPosition()) is normalised to -1 and 1 on both axis, no matter the resolution. With that, you can work out the screen position based on resolution of the window.

1 Like

I did not know the positions of UI entities are normalised to -1 and 1. With this piece of info I was able to fix it in the original project.

Not sure this is the way you meant but it works:


    // calculate screen position of UI element
    screenPos.x = this.mapRange(this.entity.getPosition().x, -1, 1, 0, window.innerWidth);
    screenPos.y = this.mapRange(this.entity.getPosition().y, 1, -1, 0, window.innerHeight);

    // end the bullet 10 units behind the crosshair.
    bullet.script.bullet.endPos = this.camera.camera.screenToWorld(screenPos.x, screenPos.y, 10);

Thanks again!