Search code examples
c#unity-game-enginegameobject

Multiple instances of gameObject not performing identically


So I have three different 'bystanders' in my game world, each with an attached 'Bystander' script, and have a 'BystanderDialogue' element of my UI. The idea is that when the player comes in range of ANY of the bystanders, randomly selected text from a database is displayed, but for my script it only ever works for ONE of the bystanders.

I feel like I'm cross-referencing scripts too much or something, but I don't know. Here are the two sections of code:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class Bystander : MonoBehaviour {

GameObject player;
public bool speak;
public bool isPlayerNear;
public int dialogueNumber;
GameObject bystanderDialogueObject;
BystanderDialogue bystanderDialogue;


// Use this for initialization
void Awake () 
{
    player = GameObject.FindGameObjectWithTag ("Player");
    bystanderDialogueObject = GameObject.Find ("BystanderDialogue");
    bystanderDialogue = bystanderDialogueObject.GetComponent<BystanderDialogue> ();
}

// Update is called once per frame
void Update () 
{

}

void OnTriggerEnter(Collider other)
{
    if (other.gameObject == player) 
    {
        dialogueNumber = Random.Range (0, bystanderDialogue.bystanderSpeech.Length);
        speak = true;
        isPlayerNear = true;
    }
}

void OnTriggerExit(Collider other)
{
    if (other.gameObject == player) 
    {
        speak = false;
        isPlayerNear = false;
    }
}
}

And the second one, attached to the UI object 'BystanderDialogue':

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class BystanderDialogue : MonoBehaviour {

Text text;
GameObject[] bystanderObjects;
Bystander bystander;
public string[] bystanderSpeech;
// Use this for initialization
void Awake () 
{
    text = GetComponent<Text> ();
    bystanderObjects = GameObject.FindGameObjectsWithTag ("NPC");
    foreach (GameObject bystanderObject in bystanderObjects) 
    {
        bystander = bystanderObject.GetComponent<Bystander> ();
    }
}

// Update is called once per frame
void Update () 
{
        BystanderSpeak (bystander);

        if (bystander.isPlayerNear) 
        {
            Debug.Log ("New bystander!");
        }

}

void BystanderSpeak(Bystander bystander)
{
    if (bystander.speak && bystander.isPlayerNear) 
    {
        text.text = bystanderSpeech[bystander.dialogueNumber];
    }
    else if (!bystander.speak && !bystander.isPlayerNear)
    {
        text.text = "";
    }
}
}

I'm very sure that I'm making some elementary mistakes, so apologies. Any help is much appreciated!


Solution

  • This code does not make sense:

    void Awake () 
    {
        text = GetComponent<Text> ();
        bystanderObjects = GameObject.FindGameObjectsWithTag ("NPC");
        foreach (GameObject bystanderObject in bystanderObjects) 
        {
            bystander = bystanderObject.GetComponent<Bystander> ();
        }
    }
    

    If there are more than one bystanderObject, then you only kept the last object as reference into the bystander member field. You should use a collection (list or array) instead to keep all of them:

    Bystander[] bystanders;
    
    
    void Awake()
    {
        bystanderObjects = GameObject.FindGameObjectsWithTag ("NPC");
        bystanders = new Bystander[bystanderObjects.Length];
        for (var i = 0; i < bystanderObjects.Length; ++i)
        {
            bystander[i] = bystanderObjects[i].GetComponent<Bystander>();
        }
    }
    

    EDIT: it seams that you have a misconception of how everything works in Unity between two frames. I suggest you read Execution Order of Event Functions.

    Your Update() method will be completed before anything is drawn on the screen. So if you overwrite the value of Text, only the last written value will be displayed. In fact, you need one Text instance for each bystander.