Over the past few days, I have decided that I need an easy way to tell whether an object is in the camera’s line of sight, or, more specifically whether is can be seen or not.
My ideas was to use an elaborate array of raycasts that cast to a length of 200 or something, and creating an array of objects caught by it, but that doesn’t technically provide any way of checking if an object is actually being looked at, it just provides the devs with, basically, a list of objects.
What I am looking to do, is have an easy way to check whether an object is visible, but as this is not something Unity has any tutorials on despite being quite a basic feature in theory, I am guessing it is a little less simple in practice.
so, I am here to ask about methods others have used, or any recommendations/pointers anyone can give me, or even a working model which would provide me with a simple detection method to build off of.
This would only cover a VERY narrow area of the viewport, the purpose of the array would be to cast a ray every 0.1 squared (assuming the viewport was calculated on a 0,1 scale) to create a grid-style layout.
Having a system which returns multiple objects along just 1 path isn’t too useful for this purpose.
It’s not in any tutorials, because it really is a non trivial problem.
To check if an object is visible at all you have to check the object pixel by pixel to see if any were rendered. As far as I’m aware there isn’t a cheap way to do that.
Maybe some sort of fancy shader magic might work. Basicly something that can set a flag if it draws any pixels on the object of concern. Transparency will be an issue, and will require more magic.
Otherwise fake it with half a dozen raycasts and be done.
True fact. Not actually verifying the distances will probably lead to bugs. I’m too used to prototyping with RaycastAll.
Grid-style layout? I really think you’re overthinking this. You should know two parts of the equation.
What’s looking
What it’s looking for
You need the third part
Can it see what it’s looking for
A raycast from what’s looking to what it’s looking for tells you number 3.
Attempting to find the objects in the first place with raycasts is silly. You know the objects are there, you just need to test if they can be seen. For efficiency’s sake, you can have a trigger collider that maintains a list of objects around or in front of the player.
i would do 2 things, do the math to figure out what is in the current players camera frustum, than simply raycast to each object found, to make sure it is not being blocked by an other object.
to see if something is in the view frustrum you can use WorldToScreenPoint, and simply check if its location is between 0 and the screen width and height
Note that the editor camera will trigger OnBecameVisible as visible as well as the game camera. Also, for some reason it doesn’t show in my Console more than 2 times, even though it works.
Here’s how I used it to pause animations that weren’t visible in my scene:
void OnBecameInvisible()
{
IsVisible = false;
//save animation state to use when visible again
PreviousStaticFrameAnimationState = StaticFrameAnimationState;
StaticFrameAnimationState = 0;
Debug.Log("Invisible");
}
void OnBecameVisible()
{
IsVisible = true;
StaticFrameAnimationState = PreviousStaticFrameAnimationState;
Debug.Log("Visible");
}
Yet “Visible” and “Invisible” show only once in my console, no matter how many times I change the camera’s view points. However, the bool (IsVisible) changes, and the Profiler shows a performance boost when I narrow my camera’s FOV, so it does work.
Overall I think it is a very strangely made but incredibly useful function.
public static bool IsDirectLineBetweenTheseTwo(GameObject one, GameObject two)
{
var ray = new Ray(one.transform.position, two.transform.position - one.transform.position);
var hits = Physics.RaycastAll(ray,ray.direction.magnitude);
if (hits.Length > 0)
{
foreach (var hit in hits)
{
//Ignore the two input objs
if (hit.collider.gameObject == one || hit.collider.gameObject == two) continue;
//Debug.DrawLine(one.transform.position, hit.collider.transform.position,Color.red,1);
//Debug.DrawLine(one.transform.position, two.transform.position, Color.blue, 1);
//Debug.Log($"Interferred by {hit.collider.gameObject.name}");
return false;
}
}
//Debug.DrawLine(one.transform.position, two.transform.position, Color.green,1);
return true;
}
Would it be possible to define a viewport at runtime and raycast WITHIN it?
for example: define a maximum view height and width and as long as the raycast ‘looks into it’ then it registers a raycast?