[SOLVED] Problem with ratio and ray cast

Hello, sorry if I ask an already responded question.

I’m working on a 2D project.
I’m trying to create a point and click feature on a map, so I’m using the Ray Cast and bounding box solution :

Player.prototype.doRayCast = function (screenPosition) {
    // Initialise the ray and work out the direction of the ray from the a screen position
    this.cameraEntity.camera.screenToWorld(screenPosition.x, screenPosition.y, this.cameraEntity.camera.nearClip, Player.ray.origin);
    this.cameraEntity.camera.screenToWorld(screenPosition.x, screenPosition.y, this.cameraEntity.camera.farClip, Player.ray.direction);

    Player.ray.direction.sub(Player.ray.origin).normalize();
    // Test the ray against the ground
    var result = this.groundShape.intersectsRay(Player.ray, Player.hitPosition);

    if (result) {
        this.movePlayer(Player.hitPosition.x, Player.hitPosition.y, Player.hitPosition.z);
    }

This was working fine. Now I want to fix the ratio of my screen view so the view is always 16:9.
So I set the Fill Mode to “Fixed Ratio” in setting.

But now my click detection is inaccurate:

If i change the size of the window, my click detection is altered, but still inaccurate :

I think I understand the reason, the camera added some “cinema box” around to keep the ratio, and it’s making my click detection point at the wrong place.
But I’m not sure on how to fix that.
Is someone have an idea ?

edit : I set my project in public https://playcanvas.com/project/1098766/overview/sorcely-demo-0723

Hi @Dezra,

This is most likely happening because the mouse click event is reporting the window coordinates. Those are starting from the top/left pixel of the window, not the rendered canvas.

You will need to accommodate for that and pass the canvas click coordinates to your raycast method.

1 Like

Hello, thank you, so this is the fix I came with :

Player.prototype.isHorizontalCinemaBox = function () {
    let unitY = window.innerWidth / 16;
    let unitX = window.innerHeight / 9;
    let result = 0;
    if (unitX > unitY) {
        //we have horizontal cinema box
        let canvasSizeHeight = unitY * 9;
        result = (window.innerHeight - canvasSizeHeight) / 2;
    }
    return result;
};

Player.prototype.isLateralCinemaBox = function () {
    let unitY = window.innerWidth / 16;
    let unitX = window.innerHeight / 9;
    let result = 0;
    if (unitX < unitY) {
        //we have lateral cinema box
        let canvasSizeWidth = unitX * 16;
        result = (window.innerWidth - canvasSizeWidth) / 2;
    }
    return result;
};

Player.prototype.doRayCast = function (screenPosition) {
    // Initialise the ray and work out the direction of the ray from the a screen position
    let offsetX = this.isLateralCinemaBox();
    let offsetY = this.isHorizontalCinemaBox();

    this.cameraEntity.camera.screenToWorld(screenPosition.x - offsetX, screenPosition.y - offsetY, this.cameraEntity.camera.nearClip, Player.ray.origin);
    this.cameraEntity.camera.screenToWorld(screenPosition.x - offsetX, screenPosition.y - offsetY, this.cameraEntity.camera.farClip, Player.ray.direction);

    Player.ray.direction.sub(Player.ray.origin).normalize();
    // Test the ray against the ground
    var result = this.groundShape.intersectsRay(Player.ray, Player.hitPosition);
    if (result) {
        // this.app.fire('getPosition', this.player.getPosition(), Player.hitPosition);
        this.movePlayer(Player.hitPosition.x, Player.hitPosition.y, Player.hitPosition.z);
    }
};

I had an idea when writing it, maybe you can create a new variation of ScreenToWorld which do that natively ? Whatever, I let it here so people can use it as they want.
bye !

edit : I can’t find how to put the subject on “Solved” sorry

1 Like