World Position from click using pan and zoom

Hi, I am having a lot of trouble getting the correct world position while using pan and zoom for a project I am working on. The map size is quite large with bounds from -40000x to 40000x and -10000y to 10000y. Pan and zoom work great but I get big problems simply trying to get the world coordinate from where I clicked even though there is an option get this from the pointer. I know something exists for the tilemap but I cannot use that for this project unfortunately

The code is as follows:

export const createCamera = (game: Game) => {
  game.camera = game.cameras.main;
  game.camera.setBounds(-40000, -20000, 80000, 40000, false);

  game.dragLastX = 0;
  game.dragLastY = 0;
  game.lastDown = 0;

  game.downTime = 0;
  game.dragCheck = false;
  game.camSpeedX = 0;
  game.camSpeedY = 0;

  game.smoothTime = 0;
  game.smoothLastX = 0;
  game.smoothLastY = 0;

  game.input.on('pointerdown', (pointer: any) => {
    if (!game.lockMovement) {
      game.dragLastX = pointer.x;
      game.dragLastY = pointer.y;
      // Camera smooth scrolling listener
      game.camSpeedX = 0;
      game.camSpeedY = 0;
      game.dragCheck = false;

      const pos = game.camera.getWorldPoint(pointer.x, pointer.y);

      EventBus.emit('viewclicked', { pointer: pointer, x: pos.x, y: pos.y });
    }
  });

  game.input.on('pointermove', (pointer: any) => {
    if (!game.lockMovement) {
      let distX = (game.dragLastX - pointer.x) / game.camera.zoom;
      let distY = (game.dragLastY - pointer.y) / game.camera.zoom;
      if (pointer.isDown) {
        game.smoothTime = game.downTime;
        game.smoothLastX = game.dragLastX;
        game.smoothLastY = game.dragLastY;

        game.camera.scrollX += distX;
        game.camera.scrollY += distY;
        game.dragLastX = pointer.x;
        game.dragLastY = pointer.y;
      } // if (down)

      // Smooth scroll handling
      game.downTime = game.time.now;
      game.dragCheck = true;
    }
  });

  game.input.on('pointerup', (pointer: any) => {
    if (!game.lockMovement) {
      let distX = game.smoothLastX - pointer.x;
      let distY = game.smoothLastY - pointer.y;
      let elapsed = game.time.now - game.downTime || 30; // Default to 60fps
      let mulTime = 1000 / elapsed; // Multiply for dist per second

      // Direction, speed
      if (game.dragCheck) {
        game.camSpeedX = distX * mulTime;
        game.camSpeedY = distY * mulTime;
      } // if (dragged)
    }
  });

  game.input.on('wheel', (pointer: any, gameObjects: any, deltaX: number, deltaY: number, deltaZ: number) => {
    if (!game.lockMovement) {
      // Get the current world point under pointer.
      const worldPoint = game.camera.getWorldPoint(pointer.x, pointer.y);
      const newZoom = game.camera.zoom - game.camera.zoom * 0.0005 * deltaY;

      game.camera.zoom = Phaser.Math.Clamp(newZoom, game.clampZoomMin, game.clampZoomMax);

      // Update camera matrix, so `getWorldPoint` returns zoom-adjusted coordinates.
      // @ts-ignore
      game.camera.preRender();
      const newWorldPoint = game.camera.getWorldPoint(pointer.x, pointer.y);
      // Scroll the camera to keep the pointer under the same world point.
      game.camera.scrollX -= newWorldPoint.x - worldPoint.x;
      game.camera.scrollY -= newWorldPoint.y - worldPoint.y;

      // Scroll Level Handler
      let lastKey: number = 0;

      EventBus.emit('scrolling', game.camera.zoom);
    }
  });
};

export const updateCamera = (time: number, delta: number, game: Game) => {
  //const pos = game.camera.getWorldPoint()
  //const pos = game.camera.getWorldPoint(game.input.activePointer.x, game.input.activePointer.y);
  game.input.activePointer.updateWorldPoint(game.cameras.main);
  const worldPoint = game.input.activePointer.positionToCamera(game.cameras.main);
  game.cursor.setPosition(worldPoint.x, worldPoint.y);
  console.log(worldPoint);

  //game.cursor.setPosition()
  if (!game.lockMovement) {
    if (game.camSpeedX != 0 || game.camSpeedY != 0) {
      let camera = game.cameras.main;

      let speedX = game.camSpeedX;
      let speedY = game.camSpeedY;
      let reduceTime = 1000; // Calculate per second

      let scrollX = ((speedX * 2) / reduceTime) * delta;
      let scrollY = ((speedY * 2) / reduceTime) * delta;

      camera.scrollX += scrollX;
      camera.scrollY += scrollY;

      // Reduce speed of scroll
      game.camSpeedX -= ((game.camSpeedX * 0.8) / 100) * delta;
      game.camSpeedY -= ((game.camSpeedY * 0.8) / 100) * delta;

      // Absolute values to stop movement
      let absX = Math.abs(game.camSpeedX);
      let absY = Math.abs(game.camSpeedY);
      if (absX <= 8 && absY <= 8) {
        game.camSpeedX = 0;
        game.camSpeedY = 0;
      } // if (both slowed)
    } // if (camera scrolling)
  }
};

If anyone can help that would be great, been struggling with this longer than I would like. Thanks

:wave:

Where and how are the values incorrect?

Hi,

Apologies this took a while to verify, by the time I posted it I resolved the problem and I could not post to say this!

There were a few problems, sprites not appearing in correct coordinates and the camera not zooming in on the correct position. It turned out that there was a strange issue on the database where the output coordinates were not returned correctly.

For the camera, I just found the command to use which zoomed into the position correctly. I had problems using the recommended setScroll or the pan command but this worked:

  game.camera.zoomTo(zoomTo, duration, 'Linear', true, (camera, progress, x, y) => {
    game.camera.centerOn(location.x, location.y);
}

Thanks for taking a look! I will leave the code here in case anyone has need of it. Only remaining problem is that it does not work with mobile gestures for panning but I have not had time to look at it yet.

1 Like