It would seem that the simplest question on the topic of camera viewport ...
The coordinate values ββon both axes inside the camera's viewport take values ββfrom 0 to 1. If the point takes on other values, it lies outside the viewport of this camera.
Since the object should not go beyond the visibility of the camera, it means that its coordinates in the projection on the camera's viewport should be within [0, 1] .
This can be achieved in 2 ways:
object world pos -> object viewport pos -> clamp object viewport pos to [0..1] -> object world pos
In this case, the transfers from world pos to [0..1] viewportΚ»a lead to a huge loss of accuracy and the output is nonsense, verified by experiment, does not work.
- Get a rectangular section of the camera's visibility pyramid in world coordinates, and check the coordinates of other objects relative to this rectangle β it works fine. This method will go further.
In this case, it will be about limiting the position of a single object, but rewriting it for use on an array of objects is not difficult.
To begin with, we will find the very "cut" of the camera frustum - a regular rectangle. As I wrote above, viewport is always limited, specific coordinates depend on the engine. In the case of Unity, this is [0..1] . Well, here we find the coordinates of the angles of the viewport in world coordinates, using the coordinates of its edges in the coordinate systems of the viewport itself. Although the viewport is a rectangle, we pass there the 3 coordinate - the distance from the camera to the object of interest to us, everything seems logical here, so I wonβt chew.
Camera cam = Camera.main; Vector3 cameraToObject = transform.position - cam.transform.position; float distance = -Vector3.Project(cameraToObject, Camera.main.transform.forward).y; Vector3 leftTop = cam.ViewportToWorldPoint(new Vector3(0, 1, distance)); Vector3 leftBot = cam.ViewportToWorldPoint(new Vector3(0, 0, distance)); Vector3 rightBot = cam.ViewportToWorldPoint(new Vector3(1, 0, distance)); Vector3 rightTop = cam.ViewportToWorldPoint(new Vector3(1, 1, distance));
If we throw the whole thing in gizmos - we get the following picture:

In fact, a rectangle can be defined exactly 2 diagonally opposite points - this is quite enough to get the restrictions on both coordinates.
Summary Code:
Vector3 cameraToObject = transform.position - Camera.main.transform.position; // ΠΎΡΡΠΈΡΠ°Π½ΠΈΠ΅ ΠΏΠΎΡΠΎΠΌΡ ΡΡΠΎ ΠΈΠ³ΡΠΎΠ²ΡΠ΅ ΠΎΠ±ΡΠ΅ΠΊΡΡ Π² Π΄Π°Π½Π½ΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ Π½Π°Ρ
ΠΎΠ΄ΡΡΡΡ Π½ΠΈΠΆΠ΅ ΠΊΠ°ΠΌΠ΅ΡΡ ΠΏΠΎ ΠΎΡΠΈ y float distance = -Vector3.Project(cameraToObject, Camera.main.transform.forward).y; // Π²Π΅ΡΡΠΈΠ½Ρ "ΡΡΠ΅Π·Π°" ΠΏΠΈΡΠ°ΠΌΠΈΠ΄Ρ Π²ΠΈΠ΄ΠΈΠΌΠΎΡΡΠΈ ΠΊΠ°ΠΌΠ΅ΡΡ Π½Π° Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠΎΠΌ ΡΠ°ΡΡΡΠΎΡΠ½ΠΈΠΈ ΠΎΡ ΠΊΠ°ΠΌΠ΅ΡΡ Vector3 leftBot = Camera.main.ViewportToWorldPoint(new Vector3(0, 0, distance)); Vector3 rightTop = Camera.main.ViewportToWorldPoint(new Vector3(1, 1, distance)); // Π³ΡΠ°Π½ΠΈΡΡ Π² ΠΏΠ»ΠΎΡΠΊΠΎΡΡΠΈ XZ, Ρ.ΠΊ. ΠΊΠ°ΠΌΠ΅ΡΠ° ΡΡΠΎΠΈΡ Π²ΡΡΠ΅ ΠΎΡΡΠ°Π»ΡΠ½ΡΡ
ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ² float x_left = leftBot.x; float x_right = rightTop.x; float z_top = rightTop.z; float z_bot = leftBot.z; // ΠΎΠ³ΡΠ°Π½ΠΈΡΠΈΠ²Π°Π΅ΠΌ ΠΎΠ±ΡΠ΅ΠΊΡ Π² ΠΏΠ»ΠΎΡΠΊΠΎΡΡΠΈ XZ Vector3 clampedPos = transform.position; clampedPos.x = Mathf.Clamp(clampedPos.x, x_left, x_right); clampedPos.z = Mathf.Clamp(clampedPos.z, z_bot, z_top); transform.position = clampedPos;
Result of work:

PS
It is clear that this approach can be optimized and, for example, get viewport coordinates exactly 1 time, if the height of the camera and the depths of all elements in the game are constant and the same. Such optimizations depend on the specific case.