I’m working on a 2D scene that displays a randomised solar system to scale. For display purposes, I divide the position (measured in metres, don’t worry, I’m storing it as a double) of a planet by a zoom level, so that on starting a planet 1AU from it’s star is 1 unit away in the game. I use a floating origin and do all my maths as doubles to avoid floating point errors.
In order to zoom, I adjust the zoom level, which changes the game position of the planet, giving a pseudo-orthographic zoom effect. I’m not actually using the orthographic zoom because the orthographic size becomes tiny at the planet scale.
However, I’m finding that my camera position doesn’t change enough as I zoom, it always is bit less than it’s supposed to be. Zooming towards is also implemented here, but the problem is there with or without. I expect that whatever is under the mouse to remain under the mouse, but whatever is under the mouse always ends up moving further away from the solar system origin than the camera. I’ve been scratching my head for ages now trying to figure out what I’m doing wrong. I don’t doubt that there are many things. The relevant code attached to my Main Camera is below:
private void Update()
{
//zoom according to mouse input
if (Input.GetAxis("Mouse ScrollWheel") > 0)
{
ZoomOrthoCamera(cam.ScreenToWorldPoint(Input.mousePosition), true);
}
if (Input.GetAxis("Mouse ScrollWheel") < 0)
{
ZoomOrthoCamera(cam.ScreenToWorldPoint(Input.mousePosition), false);
}
//pan according to input
float panHorizontal = Input.GetAxis("Horizontal");
float panVertical = Input.GetAxis("Vertical");
transform.position += new Vector3(panHorizontal * panSpeed, panVertical * panSpeed, 0f);
}
The place where the magic happens (I’m using a double version of Vector3 called Vector3d and then converting back to Vector3 after all the maths, for accuracy):
private void ZoomOrthoCamera(Vector3 zoomToward, bool isZoomingIn)
{
//change solar system zoom level by a fraction rather than a fixed amount since we want to change orders of magnitude
SolarSystemView.instance.zoomLevel -= (isZoomingIn ? 1 : -1) * SolarSystemView.instance.zoomLevel * ActualZoomFactor(isZoomingIn);
//since the solar system might not be at the game origin due to floating origin, we find our camera's position from the solar system's centre
Vector3d vectorToCentre = new Vector3d(transform.position.x, transform.position.y, 0) - SolarSystemView.instance.positiond;
//scale our solary system camera coordinates according to the new zoom level
Vector3d newRelativePosition = new Vector3d(
vectorToCentre.x + (isZoomingIn ? 1 : -1) * vectorToCentre.x * ActualZoomFactor(isZoomingIn),
vectorToCentre.y + (isZoomingIn ? 1 : -1) * vectorToCentre.y * ActualZoomFactor(isZoomingIn),
MagicNumbers.mainCameraPos.z
);
//convert solar system camera coordinates back into game coordinates
Vector3d newVectorToCentre = new Vector3d(newRelativePosition.x, newRelativePosition.y, 0) + SolarSystemView.instance.positiond;
//zoom towards mouse location
Vector3d zoomTowardPrecise = new Vector3d(zoomToward.x, zoomToward.y);
//since the solar system might not be at the game origin due to floating origin, we find our zoom towards position from the solar system's centre
Vector3d zoomVectorToCentre = new Vector3d(zoomTowardPrecise.x, zoomTowardPrecise.y, 0) - SolarSystemView.instance.positiond;
//scale our solar system zoom towards coordinates according to the new zoom level
Vector3d zoomNewRelativePosition = new Vector3d(
zoomVectorToCentre.x + (isZoomingIn ? 1 : -1) * zoomVectorToCentre.x * ActualZoomFactor(isZoomingIn),
zoomVectorToCentre.y + (isZoomingIn ? 1 : -1) * zoomVectorToCentre.y * ActualZoomFactor(isZoomingIn),
MagicNumbers.mainCameraPos.z
);
//convert solar system zoom towards coordinates back into game coordinates
Vector3d zoomNewVectorToCentre = new Vector3d(zoomNewRelativePosition.x, zoomNewRelativePosition.y, 0) + SolarSystemView.instance.positiond;
//find the vector we need to move the camera along to zoom towards the mouse position
Vector3d zoomTranslation = (zoomNewVectorToCentre - newVectorToCentre) * ActualZoomTowardsFactor(isZoomingIn);
//finally get new camera position in game coordinates
transform.position = new Vector3(
(float)(newVectorToCentre.x + (isZoomingIn ? 1 : -1) * zoomTranslation.x),
(float)(newVectorToCentre.y + (isZoomingIn ? 1 : -1) * zoomTranslation.y),
MagicNumbers.mainCameraPos.z
);
}
The extra functions referenced in the above (different in case they needed to be treated differently):
private double ActualZoomFactor(bool isZoomingIn)
{
//we want to be able to get back to inital zoom level when we zoom out so do some maths that allows this
return isZoomingIn ? zoomFraction : zoomFraction / (1d - zoomFraction);
}
private double ActualZoomTowardsFactor(bool isZoomingIn)
{
//we want to be able to get back to inital zoom level when we zoom out so do some maths that allows this
return isZoomingIn ? zoomFraction : zoomFraction / (1d - zoomFraction);
}
Much obliged for any help or advice given!