[PROBABLY EASY] Script only effecting one prefab

Alright, bare with me while I try to explain this.

I made a script that allows for me to interact with a ‘NPC’ prefab that I made. I also have a script on my First Person Controller’s Main Camera that will display a crosshair in the middle of the screen, and the crosshair is public so it can be defined in the inspector.

In my NPC script, when I’m within a Sphere collider trigger and my mouse is on the prefab, it will display a altered crosshair and a label below it saying “Talk to Character Name”, and when interacted with it will play a specified sound. I got this working fine and dandy with no flaws, but when I added another of the exact prefab onto the scene the script kinda broke. It will display the ‘Talk to’ label and play the sounds but the crosshair doesnt change. Any Advice? I will post my scripts below and a pic of how it should look.

P.S. I applied the prefab settings, so both prefabs in the scene are the exact same, except for the character name.

CrosshairScript

using UnityEngine;
using System.Collections;

public class CrosshairScript : MonoBehaviour {

	public Texture2D crosshairToDisplay;
	public int crosshairWidth = 38,
			   crosshairHeight = 38;
	
	void OnGUI () {		
		// Locks the cursor in the middle of the screen
		Screen.lockCursor = true;
		// Displays the specified crosshair image in the middle of the screen.
		GUI.Label (new Rect((Screen.width / 2) - (crosshairWidth / 2), (Screen.height / 2) - crosshairHeight, crosshairWidth, crosshairHeight), crosshairToDisplay);
	}
}

NPCInteractScript

using UnityEngine;
using System;
using System.Collections;

public class NPCInteractScript : MonoBehaviour {
	
	static int greeting = 0;
	
	GameObject playerObject = null;
	PlayerScript playScript = null;
	CrosshairScript crossScript = null;
	bool isHovering, inRange = false;
	float xPosition = 0;
	
	public string characterName;
	public float boxWidth = 350,
				 boxHeight = 100;
	public Texture2D crosshair,
					 interactCrosshair;
	public AudioClip[] soundClip;
	public GUIStyle labelStyle;
	
	void Start() {
		try {
			// Grabs the Player Object.
			playerObject = GameObject.FindGameObjectWithTag ("Player");
			
			// Validates that it didnt grab a null object
			if (playerObject != null) {		
				try {
					// Grabs the Crosshair Script
					crossScript = playerObject.GetComponentInChildren<CrosshairScript>();
				} catch (Exception e) {
					// Prints an error if the Crosshair Script could not be found
					Debug.Log ("ERROR: Crosshair Script could not be found.");
					Debug.Log (e);
				}
			}
			
		} catch (Exception e) {
			// Prints an error if the Player Object could not be found.
			Debug.Log ("ERROR: Player Object could not be found.");
			Debug.Log (e);
		}
		
		// Sets xPosition to have the box width for the Talk To label
		xPosition = boxWidth / 2;
	}
	
	void Update() {
		// Checks if in range and mouse is on NPC
		if (isHovering == true  inRange == true) {
			// Changes the Crosshair to the interact crosshair
			crossScript.crosshairToDisplay = interactCrosshair;
			if (Input.GetButtonDown ("Interact")) { // Checks if the player is intercting
				// Plays the sound
				audio.PlayOneShot (soundClip[greeting]);
				// Increments the sound array for a different sound
				greeting++;
				if (greeting >= soundClip.Length) {
					// Resets the greeting to the begining if there are no more sounds to play
					greeting = 0;
				}
			}
		} else {
			// Resets the crosshair to the specified crossahir
			crossScript.crosshairToDisplay = crosshair;	
		}
	}
	
	void OnGUI() {
		// Checks if in range and mouse is on NPC
		if (isHovering == true  inRange == true) {
			// Displays the Talk To Label
			GUI.Label (new Rect((Screen.width / 2) - xPosition, (Screen.height / 2), boxWidth, boxHeight), "Talk to " + characterName, labelStyle);
		}
	}
	
	void OnTriggerEnter(Collider col) {
		// Checks if the player is entering the trigger
		if (col.tag == "Player") {
			// Sets inRange to true
			inRange = true;	
		}
	}
	
	void OnTriggerExit(Collider col) {
		// Checks if the player is leaving the trigger
		if (col.tag == "Player") {
			// Sets inRange to false
			inRange = false;	
		}
	}
	
	void OnMouseEnter() {
		// If mouse is hovering over the NPC, then sets isHovering to true
		isHovering = true;	
	}
	
	void OnMouseExit() {
		// If mouse is not hovering over the NPC, then sets isHovering to False
		isHovering = false;
	}
}

The left picture is how it should look, it is also how it looks the on the original prefab and the left picture is how it looks on the duplicate prefab.

I’ve tried rewriting this script like four times lol and I just cannot figure it out on my own. All ideas and suggestions would be very much appreciated. Thank you for your time!

In Update, for each NPC, you’re doing the following, every frame:

  • If the crosshair is on me, change it to the “talk” icon.
  • Otherwise, change it to the normal icon.

So when you have two of them, they will both try to set the crosshair every frame, and whichever gets processed second will “win”, resulting in the behavior you’ve seen.

One solution would be to add a flag and make the logic work like this:

If crosshair on me and !flag
  Show talk crosshair
  Set flag
If crosshair not on me and flag
  Show normal crosshair
  Clear flag

You put that script on your prefab?

Conceptually, your prefabs really shouldn’t care what the cursor looks like. The thing controlling the cursor should be in charge of updating it - meaning, the player script should be handling cursor changes. You could use OnMouseEnter to tell the player what object / type it’s interacting with, and then let the player handle alllll the cursor changes in one place.

What’s happening right now is this:

Prefab1 (P1) and P2 are both running their scripts.
You stare deep into the eyes of P1.
P1 says “YES YOU LOVE ME” (isHovering isInRange), and sets the cursor to speechbibble.
P2 looks over, says “Y U NO LOV” (!isHovering !isInRange), and then IT sets the cursor back to crosshair.

There are ways to avoid this overriding behaviour and still have the crosshair controlled from the prefabs, but I highly recommend just transferring cursor control to the player - something like this:

{player}

static GameObject lastHoverTarget;

public static void NewHoverTarget( GameObject ob, Texture2D crosshair ) {
  crossScript.crosshair = crosshair;
  lastHoverTarget = ob;
}

public static void ClearHoverTarget( GameObject ob ) {
  if ( ob == lastHoverTarget ) crossScript.crosshair = default;
}

{prefab}

public void OnMouseEnter() { Player.NewHoverTarget(gameObject, myCursor); }
public void OnMouseExit() { Player.ClearHoverTarget(gameObject); }

Ahhh, so simple. Ha thank you both for your help!