• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Jagodas · May 14, 2017 at 09:54 PM · 2dzoomorthographicfloating

Faking orthographic zoom by scaling positions

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!

Comment
Add comment · Show 7
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image AlwaysSunny · May 14, 2017 at 09:54 PM 0
Share

You properly formatted your code! I could hug you. Trying to push these Q's through moderation. If nobody comes along to help, tag me in a comment and I'll give this a closer look. However, this Q is significantly more complicated than what we normally get at UA. It's probably better suited to the scripting forum, or perhaps a relevant StackExchange forum. Best,

avatar image Jagodas AlwaysSunny · May 21, 2017 at 03:44 PM 0
Share

I'll try where you recommend, since there's been no response so far. Thanks though!

avatar image Glurth · May 21, 2017 at 10:30 PM 0
Share

" I use a floating origin and do all my maths as doubles to avoid floating point errors."

Don't let this throw you off: Any non-interger numbering system with a limited number of digits will always result in precision errors. While the precision error for double is much, much smaller than for floats, precision errors ARE introduced.

"In order to zoom, I adjust the zoom level, which changes the game position of the planet," Looking at your code, it looks like you only change the position of the Camera.

"I expect that whatever is under the mouse to remain under the mouse" Hmm, this sounds tricky... I'm not quite sure how you would deter$$anonymous$$e the vector to move the camera such that a particular world point stays at the same (off-center) screen coordinate, it might even be a curve. I'll think about it, but expect it will need to use the camera's field of view angle somehow.

Edit: ok, much easier than expected with an Orthographic Camera. Not sure why you don't want to use orthographicSize; made it pretty easy (if this DOES work for you, let me know and I'll convert it to an answer):

 public float zoomSpeed = 0.1f;
 private void ZoomOrthoCamera(Vector3 zoomToward, bool isZoo$$anonymous$$gIn)
 {
     float negSpeed = zoomSpeed * (isZoo$$anonymous$$gIn ? 1 : -1);
     Camera cam = this.GetComponent<Camera>();
     cam.orthographicSize -= cam.orthographicSize * negSpeed;
     Vector3 camToTarget = zoomToward - transform.position;
     transform.position += (camToTarget* negSpeed);
 }

avatar image Glurth Glurth · May 21, 2017 at 11:39 PM 0
Share

Here is a version that can do fake scaling, leaving the orthagraphicSize untouched. It uses a parent object on which to apply the scale (rather than applying the scaling to the transform of each object in view.) The fake zoom will only work right for objects that are children of this parent object. This camera must ALSO be made a child of this object for it to work right.

 public float zoomSpeed = 0.1f;
     public bool fakeZoomWithScale=false;
     public Transform fakeZoomParentObject;// all objects that need to be scaled, and this camera object, should be children of this transform.
     private void ZoomOrthoCamera(Vector3 zoomToward, bool isZoo$$anonymous$$gIn)
     {
         float negSpeed = zoomSpeed * (isZoo$$anonymous$$gIn ? 1 : -1);
         Vector3 camToTarget = zoomToward - transform.position;
         if (!fakeZoomParentObject)
         {
             Camera cam = GetComponent<Camera>();
             cam.orthographicSize -= cam.orthographicSize * negSpeed;
         }
         else
         {
             if (fakeZoomParentObject != null)
                 fakeZoomParentObject.localScale += fakeZoomParentObject.localScale * negSpeed;
         }
         transform.position += (camToTarget * negSpeed);
     }

Show more comments

1 Reply

· Add your reply
  • Sort: 
avatar image
1
Best Answer

Answer by Glurth · May 23, 2017 at 09:58 PM

Here is a version that can do fake scaling, leaving the orthagraphicSize untouched. It uses a parent object on which to apply the scale (rather than applying the scaling to the transform of each object in view.) The fake zoom will only work right for objects that are children of this parent object. This camera must ALSO be made a child of this object for it to work right.

  public float zoomSpeed = 0.1f;
      public bool fakeZoomWithScale=false;
      public Transform fakeZoomParentObject;// all objects that need to be scaled, and this camera object, should be children of this transform.
      private void ZoomOrthoCamera(Vector3 zoomToward, bool isZoomingIn)
      {
          float negSpeed = zoomSpeed * (isZoomingIn ? 1 : -1);
          Vector3 camToTarget = zoomToward - transform.position;
          if (!fakeZoomParentObject)
          {
              Camera cam = GetComponent<Camera>();
              cam.orthographicSize -= cam.orthographicSize * negSpeed;
          }
          else
          {
              if (fakeZoomParentObject != null)
                  fakeZoomParentObject.localScale += fakeZoomParentObject.localScale * negSpeed;
          }
          transform.position += (camToTarget * negSpeed);
      }

I would expect problems designating a full Astronomical Unit Distance, a value of 1.0 "units". If you made an AU say... 10,000 "units" instead, you would be able to use much more reasonable scales in the camera when looking at a planet or star.

In other words (and different numbers): If a moon, is the smallest object you can see - it's radius of 1,000miles should probably be "less than, but near 1.0" in your "units"... maybe a radius of 0.001f (a small number, but not unreasonably so), a planet can have a radius of about 0.004f (4,000 mi), and a star can have a radius of about .40f (400,000 mi). Planetary distances would be 93e3f for one AU, but still gets pretty high at 2.8e6f (for Neptune's orbital radius of 30AU)
This RANGE does indeed make single-precision floats insufficient. E.g. Adding a moon's radius to the position of Neptune, using a float, will have NO effect on the position, it's too small (relatively) to register (float's can work, -with a possible loss of accuracy- with ratios of at most, about 1 in 8.3e6- or [2^23]). The double precision SHOULD be sufficient (working ratio: aprox 1 in 4.5e15!! [2^52]). This also seems like a good reason to do the fake zoom.

Regarding the parent scaling object: yes, all children objects will be scaled by this object's transform. So, if you have say.. sprites, scaled to your planet, by making it a child of the planet: When you scale the root object, both the sprite AND the object will be scaled.

Keep in mind the "final" or Global scale of the object in a transform hierarchy is the computed "lossyScale" (readonly), not the localScale (these are the same only when the localScale object has no parents, or all the parents have an even scaling of 1.0f).

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Jagodas · May 24, 2017 at 08:03 AM 0
Share

That's really helpful, thanks so much!

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

5 People are following this question.

avatar image avatar image avatar image avatar image avatar image

Related Questions

Zoom out in orthographic view? 2 Answers

Dynamic Orthagraphic Camera Zoom 1 Answer

2D camera zoom in comparison to the height of target 2 Answers

Resizing orthographic camera to fit 2d sprite on screen 1 Answer

Ambient Light not working with some objects 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges