Detect if player sees the enemy

I try to play a horror sound effect when the enemy is on the screen (known from Slender). So I have to check if the player can see it. How do I do this? I was looking for a similar script and found many examples to check if the enemy sees the player. I thought I can just switch the objects but it did not work for me.

This don’t work:

var enemy : GameObject;
var shockAudio : AudioClip;

private var playedAudio : boolean;

function Start(){
	playedAudio = false;
}

function Update(){
  CanSeeEnemy();
}

function CanSeeEnemy(){
	var here = transform.position;
	
	if (enemy.renderer.isVisible){
		var pos = enemy.transform.position;
		var hit : RaycastHit;
      
		if (Physics.Linecast(here, pos, hit) && hit.transform == enemy.transform){
			if(!playedAudio){
				audio.clip = shockAudio;
				audio.Play();
				playedAudio = true;
			}        
		}
		else{
			playedAudio = false;
		}
	}
}

The most easy visibility test is to use Linecast from the player to the enemy position, then check if the object hit is the enemy - if not, something is obscuring it. But the “enemy-sees-player” test is easier, because the enemy usually knows where the player is. The player, on the other hand, usually doesn’t keep track of where the bastards are, what makes checking their visibility more complicated. A traditional way to handle this is to get a list of all enemies with GameObject.FindGameObjectsWithTag in the player script, and iterate through this list each frame checking if the player can see one of them. Since Find functions are time consuming, they should ideally be called only once at Start - but this fails if new enemies are instantiated during the game. A good compromise is to update the enemy list at predefined intervals, what can be done like this:

// In the player script:

var interval: float = 2.5; // interval to update enemy list
private var enemyList: GameObject[]; // enemy list

function Start(): IEnumerator { // make Start a coroutine
  while (true){ // refresh enemy list at interval seconds:
    enemyList = GameObject.FindGameObjectsWithTag("Enemy");
    yield WaitForSeconds(interval);
  }
}

// check if some enemy is at sight: if so, returns its game object reference, else returns null

function CanSeeEnemy(): GameObject {
  var here = transform.position; 
  for (var enemy: GameObject in enemyList){
    // if enemy not destroyed yet, and is in front the camera...
    if (enemy && enemy.renderer.isVisible){
      // do a Linecast:
      var pos = enemy.transform.position;
      var hit: RaycastHit;
      // if nothing is obscuring the enemy...
      if (Physics.Linecast(here, pos, hit) && hit.transform == enemy.transform){
        // finish function and return its GameObject reference:
        return enemy;
      }
    }
  }
  return null; // if none at sight, return null
}

function Update(){
  var enemyAtSight = CanSeeEnemy(); // check if some enemy at sight
  if (enemyAtSight){
    // player can see the enemy, and its GameObject reference is in enemyAtSight
  }
  // other Update code, if any
}

NOTE: renderer.isVisible only indicates whether the object is inside the viewing area of any camera, not if it’s actually visible.