0
\$\begingroup\$

I'm creating line-of-sight code in Unity. It allows a radar to check whether it can see an object, or whether the object is behind an obstacle (a collider).

Similarly to Unity's suggestion, I check to see whether a Linecast going from radar to target strikes a collider that belongs to the target's game object.

In my implementation, a Target is a type that buildings and vehicles inherit from. Radar is a script attached to any game object, tracking from the transform of that game object towards any Target within the radar's scope.

The method giving me trouble is below. It is called every time a radar checks whether or not a collider stands between it and the Target (targets behind a wall or mountain can't be tracked).

/// <summary>
/// Check whether two objects can see each other. This does not verify any maximum distance.
/// </summary>
/// <param name="seeker">The object seeing</param>
/// <param name="target">The object being seen</param>
/// <returns>True if within line of sight, false if another collider is in the way</returns>
protected bool InLOS(Transform seeker, Transform target)
{
   RaycastHit hit;
   Physics.Linecast(seeker.position, target.position, out hit);
   if(hit.collider.gameObject == target.gameObject) // Null error? Make sure the sensor is not within a collider/rigidbody, or that trigger is on
      return true;
   else
      return false;
}

This method works to a degree. The image below shows its result. Green lines represent unobstructed radar tracking. (There are two radars in the scene: one on the airplane, and one on the dish).

enter image description here

Note that the airplane cannot see the radar dish, nor can the dish see the airplane (although they are both targets). Accordingly, there are two null errors at every frame: NullReferenceException: Object reference not set to an instance of an object.

The error points me to the following line: if(hit.collider.gameObject == target.gameObject). That's because, unlike the other, simpler targets, the airplane does not have its collider on the parent, but has many different colliders in its children.

I've tried finding the parent game object from the collider, but that didn't work either:

// Raycast
// ...

try
{
   if(hit.collider.gameObject == target.gameObject) // Null error? Make sure the sensor is not within a collider/rigidbody, or that trigger is on
      return true;
   else
      return false;
}
catch
{
   if(hit.collider.GetComponentInParent<Target>().transform.gameObject == target.gameObject)
      return true;
   else
      return false;
}

Same null error on hit.collider.GetComponentInParent<Target>().transform.gameObject == target.gameObject.

Well then, let's check for nulls:

// Raycast
// …

GameObject raycastHit = hit.collider.gameObject;
GameObject desiredHit = target.gameObject;

if(raycastHit == null)
{
   // Collider must be child raycast target hit, then
   raycastHit = hit.collider.GetComponentInParent<Target>().transform.gameObject;
}

if(desiredHit == null)
{
   // Collider must be child...
   desiredHit = target.transform.GetComponentInParent<Target>().transform.gameObject;
}

// Check hit equivalency
if(raycastHit == desiredHit)
   return true;
else
   return false;

Nope, same old null reference. Man are those GetComponent<> methods always a pain.

So let's go simple:

if(Physics.Linecast(seeker.position, target.position))
   return true;
else
   return false;

In this case, obstacles are not detected anymore. Hmm.

Question: how can I write GetComponentInParent<> correctly to find the Target game object's transform, knowing that sometimes Target will be a component of the same game object as its collider, while at others, Target will be the parent, and various colliders will be children?

\$\endgroup\$
1
\$\begingroup\$

This method tests positions against each other. If a game object does not have a collider at its pivot point, no collider will be found, causing a null exception.

To fix the null errors, it was only necessary to add a collider to cover the radar building's pivot point, which was exposed in my setup.

The code needed for line-of-sight detection is this:

protected bool InLOS(Transform seeker, Transform target)
{
   RaycastHit hit;
   Physics.Linecast(seeker.position, target.position, out hit);

   // Test whether the raycast hit the same game object as the wanted target (no obstacle)
   if(hit.collider.gameObject == target.gameObject)
      return true;
   else
      return false;
}

Because of Unity compound colliders, all child colliders can be referred to by using hit.collider, making GetComponentInParent<> calls unnecessary.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.