Hello everybody.
In my editor I instantiate a 3d object which in my case is a character.
Work in perspective projection. According to the camera settings, the position of the plane on which the character is instantiated, the size of the screen, it can appear larger or smaller.
I would need to resize it when instantiating it so that it always takes up a third of the screen.
I have managed to get some results using ScreenToWorldPoint or even ViewportToWorldPoint but without much success.
Since a Nav Mesh Agent is attached to the character I get its height from it.
Now I thought it might be a solution to get the height of the screen in units and divide this measurement by three and divide the result again by the height of the character. This should be the scale to be assigned to it.
But I can’t get the height of the screen.
Do you have any suggestions?
For perspective projection the height of an object compared to the screen depends on how far away from the camera it is. You could use trigonometry to determine this. Consider this diagram from Wikipedia:
Imagine that the point at capital A is your camera, and this triangle is the top half of your viewing frustum. Imagine the point at capital C is the point at the center of the object that you want to instantiate. The side labelled “Adjacent” represents the distance between these to points. The side labelled “Opposite” is… well the top two-thirds would be the empty space above your object, and the bottom third would be the top half of your object. We know the angle between the “adjacent” and the “hypotenuse” because that is your Camera’s fieldOfView. Let’s call this angle theta, because that is the convention.
We can easily get the tangent of theta using Unity’s Mathf.Tan() function. The value of the tangent is equal to the length of “opposite” divided by the length of “adjacent”, That means that if we know the length of either line, we can easily calculate the the other.
So… that means if you want the object to be one third the height of the screen there are two ways you could do that:
-
You could instantiate the object at a set distance from the camera and scale the object to be a third the size of the screen. In this case you’ve already decided the distance from the camera (“adjacent”) and we know theta, so we can get the length of “opposite” by taking the tangent of theta and multiplying it by adjacent. Once you know the length of “opposite” you just need to scale your object so that it is 2/3 the size of “opposite”. The amount you scale depends on how big the object already is.
-
The second way is to keep the object the same size and change the distance so that it takes up more or less of the field of view. Assuming you already know the height of the object, then the height of “opposite” is 3/2 the height of the object. We also know theta. We just need to calculate the “adjacent” side. That would be the height of “opposite” divided by the tangent of theta.
Obviously It’s also possible to adjust the scale and the distance, but that would be too complicated for me.
By the way, all this is off the top of my head, so there could be some errors in here.
One other thing: Mathf.Tan expects an angle in radians, where as Camera.fieldOfView is in degrees, so you’ll need to convert one to the other.
Hi kdgalla,
thank you very much for the time you lost and for the excellent explanation.
I’ve worked on it a bit and your suggestion is of course correct. I only found one fix to make: the FOV value should be half its real value.
I share my tests for you and anyone who might need them.
Code (CSharp):
- float getTan = Mathf.Tan(cam.fieldOfView * 0.5f * Mathf.Deg2Rad);
- float getH = getTan * cam.transform.position.z;
With a FOV of 60 and a camera distance of -8.80 I get a height of 5.12.
I have prepared two characters, one with the height of 1.73 and one of 1.23.
To get the height I use the Height field of Nav Mesh Agent after having adapted it to the character but you can also use a collider.
This is the result:
The characters are positioned at Vector3.zero.
In the case of the larger character we have 5.12 / 1.73 = 2.95, for the smallest 5.12 / 1.23 = 4.16 and as we can see from the image the calculations seem to return.
Thanks again.
I was stuck somewhere waiting, so I though “might as well.”