I’m writing a Line-Of-Sight system for my strategy game, but I’ve run into an unexpected issue. At the beginning of each turn, each friendly unit performs a raycast with each enemy unit, to see if there exists Line-Of-Sight.

Here’s the trouble: say an enemy is in a doorway, and directly behind him is another enemy. He ‘takes the hit’ for the enemy behind him, hiding him completely. I need to be able to perform a raycast between objects that will ignore other objects of the same type. I don’t think this is a job for Layers, as all enemies would be on the same layer anyway.

Here’s my simple code for the current check:

public GameObject CheckLineOfSight(GameObject agent, GameObject target)
	{
		RaycastHit hit;
		
		if (Physics.Raycast(agent.transform.position, (target.transform.position - agent.transform.position), out hit))
		{
			if (hit.transform == target.transform)
			{
				return target;
			}
			else 
			{
				return null;
			}
		} else {
			return null;
		}
	}

If anybody could point me in the direction of some way to achieve this, I’d greatly appreciate it.

Cheers,
-Stew

RaycastAll stabs through stuff.

Instead of raycasting from the player, try raycasting from the enemies, that way you can ignore the other enemies using LayerMasks while raycasting.

I’m gonna controversially change my answer!

When you cast between two objects, A and B, to check for line of sight:

You must exclude A and B from the cast.

(Depending on your game you might have to temporarily change the layers of A and/or B during the cast.)

Also note: as whydo reminds us you need to cast in both directions to check line-of-sight, since mesh is senso unico.

One more critical note to the OP, you must absolutely limit a cast to the length between A and B or you are not checking line of sight between the two


Note that almost certainly, you could give consideration to using

#CapsuleCast
.

in dealing with the issue in your question. CapsuleCast is a “fat” ray. But note, the 6 sentences above the dotted line here, applies just the same to either capsule cast or a ray cast, no difference.

Hope it helps!!


You mentioned you wanted a general code example of using layers, there are many long and good discussions on here. Here’s some random example code, it happened to be open in the window behind!!

// so, as with all lasers, simply cast to see if we hit the hero ...

var ignoreMask:int;
var theHit : RaycastHit;
ignoreMask = 1<<13;  // 13 is surely the hero's layer (but not his lair)

if ( Physics.Raycast ( pp, dir, theHit, playAreaWidthMeters, ignoreMask ) )
	{
	candc.heroBashedAtPoint( theHit.point, BASHTYPE.laser );
	return;
	}

yield WaitForSeconds(0.1);
// our lucky players lived with luck smeared in time!

if ( Physics.Raycast ( pp, dir), theHit, playAreaWidthMeters, ignoreMask ) )
	{
	candc.heroBashedAtPoint( theHit.point, BASHTYPE.laser );
	}

hope it helps. there are many questions about the arcane details of physics layers on this site.

unityGEMS.com has outstanding articles on the topic

etc etc

I would think the best way is to first check range with Distance, then line of sight with Angle or Dot product and finally checks the actual view with Linecast.

See below for one player /many NPC, this is attached to the NPC

void AIFunction(){
    Vector3 direction = player.position - _transform.position;
    if (direction.sqrMagnitude < attackRange){
        if (chasing){
            _delegate = this.Attack; 
        } else{
            if (Vector3.Dot(direction.normalized, _transform.forward) > 0 &&
                !Physics.Linecast(_transform.position, player.position, layerMask)){
                    _delegate = this.Attack;
                    chasing = true;
            }
        }
    }else{
        if(chasing){
            _delegate = this.Walk;
            chasing = false;
        }
    }
}

Your update looks like this:

void Update(){       
    _delegate();
}

You now have two other methods for Attack and Walk and you need to declare all the variables for chasing _delegate and other range…You also need a reference to your player.

Now for the explanation, first we check if the guy is within range, then is our NPC already chasing him, then we attack. This is because if you manage to get quickly out of sight (by getting behind him fast enough), he does not see you anymore.

Now what if the player is in range but we were not chasing him, meaning the NPC is not aware of the layer being around (the player may come from behind), then we check if the player is within a line of sight using dot product. Here we have a 180 degrees line of sight.

But we also check if a linecast between the NPC position and the player position is possible. The layerMask shoudl include both of them so that their own colliders are not interfering. This is because is the player is within range and in front of the NPC but behind a wall, the NPC is not supposed to see him.

The whole thing is explained here in more details unitygems.com