- Home /
Access and modify Text within a prefab
I am making my first non-tutorial game in Unity, and it's going well in most ways. I am stuck however, trying to write to a Text within a prefab during an instance of that prefab. In my game, the player triggers a battle, which generates an instance of a battle environment. Within that environment a copy of the player and a monster are also instantiated from prefabs. Everything's great, the internal UI works, and even the UI childed to the characters is showing up. I can't modify it, though. I kept getting the "object not a reference to an instance" error, until I added an if statement to see if the variable I stored the Text object in was null (which is always is). Any idea where I'm going wrong? Thanks so much!
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour {
public int test;
//Starting player health
private static float MAX_HEALTH = 100f;
public float playerHealth = MAX_HEALTH;
private float healthRatio;
public GameObject healthBar;
//Placeholder starting monster health
public float monsterHealth = 50;
//To track choices in battle
public int numAttacks = 0;
public int numRun = 0;
//For setting up and breaking down battles
public GameObject battleParticles;
public GameObject battleGround;
public GameObject player;
public GameObject playerImage;
public Text playerText;
public GameObject mainCam;
public GameObject monster;
public Text monsterText;
//For setting up and breaking down battles
private GameObject clonePlayer=null;
[HideInInspector] public GameObject cloneBattle=null;
private GameObject cloneMonster=null;
private Vector3 playerOrigin;
//Allow other classes to manipulate health/score
public static GameManager instance = null;
//Sets up singular instance of GameManager for other classses to use
void Awake (){
if (instance == null)
instance = this;
else if (instance != this)
Destroy (gameObject);
}
//Freeze the player, set up battle, start battle
public void startBattle(){
player.GetComponent<PlayerController> ().enabled = false;
Instantiate (battleParticles, player.transform.position, Quaternion.identity);
Invoke("initializeBattle", 2f);
}
//Start battle
public void initializeBattle(){
if (cloneBattle == null) {
cloneBattle = Instantiate (battleGround, new Vector3 (0, 50, 0), Quaternion.identity) as GameObject;
clonePlayer = Instantiate (playerImage, new Vector3 (-4f, 51f, 0f), Quaternion.identity) as GameObject;
cloneMonster = Instantiate (monster, new Vector3 (4f, 51f, 0f), Quaternion.identity) as GameObject;
playerText = clonePlayer.transform.GetChild (0).gameObject.GetComponent<Canvas> ().GetComponent<Text> ();
monsterText = cloneMonster.transform.GetChild (0).gameObject.GetComponent<Canvas>().GetComponent<Text> ();
}
}
//End battle, unfreeze player
public void endBattle(){
Destroy (cloneMonster);
Destroy (clonePlayer);
Destroy (cloneBattle);
player.GetComponent<PlayerController> ().enabled = true;
}
//Functions called to reduce health if player/monster is injured
public void playerInjury(float lostHealth){
if (playerText) {
playerText.text = "-" + lostHealth.ToString () + "!";
}
playerHealth -= lostHealth;
healthRatio = (playerHealth / MAX_HEALTH);
healthBar.GetComponent<RectTransform> ().localScale = new Vector3(healthRatio, healthBar.GetComponent<RectTransform> ().localScale.y, healthBar.GetComponent<RectTransform> ().localScale.z);
//Placeholder
Debug.Log ("Player: " + playerHealth);
if (playerHealth <= 0f) {
SceneManager.LoadScene ("Lose_Screen");
}
}
public void monsterInjury(float lostHealth){
if (monsterText) {
monsterText.text = "-" + lostHealth.ToString () + "!";
}
monsterHealth -= lostHealth;
//Placeholder
Debug.Log ("Monster: " + monsterHealth);
if (monsterHealth <= 0f) {
endBattle ();
}
}
//Functions called to track player choices in battle
public void playerAttack(){
numAttacks++;
Debug.Log ("Attacked: " + numAttacks);
}
public void playerRun(){
numRun++;
Debug.Log ("Ran: " + numRun);
}
}
In the above code, I have assembled a Game Manager which works marvelously well in all ways except for one. In each of these places:
if (monsterText) {
monsterText.text = "-" + lostHealth.ToString () + "!";
}
if (playerText) {
playerText.text = "-" + lostHealth.ToString () + "!";
}
The if statement is always false. monsterText and playerText are always null (or simply throw the "object reference not set to an instance" error), meaning something fails in their assignment, which occurs here:
clonePlayer = Instantiate (playerImage, new Vector3 (-4f, 51f, 0f), Quaternion.identity) as GameObject;
cloneMonster = Instantiate (monster, new Vector3 (4f, 51f, 0f), Quaternion.identity) as GameObject;
playerText = clonePlayer.transform.GetChild (0).gameObject.GetComponent<Canvas> ().GetComponent<Text> ();
monsterText = cloneMonster.transform.GetChild (0).gameObject.GetComponent<Canvas>().GetComponent<Text> ();
I've tried breaking that statement up and using temporary variables, but it's never paid off. I would like to update the text during each battle instance to show the value of blows landing as they happen. I thought setting and calling the variables after creating the instance would be enough, but Unity is still telling me the object is not set to an instance.
What am I missing?
Answer by phil_me_up · Mar 04, 2016 at 01:14 AM
I'm guessing your problem is with the GetChild(0).game object.GetCompenent().GetComponent() line(s). Sorry, I can't copy and paste on mobile but hopefully you know what I'm referring too!
I've never liked using GetComponent because a) it's slow and b) you can easily get in a muddle. In your case your code is expecting to find the Text component in a very specific place - Within the first child object of your instantiated prefab.
Note that GetCompenent().GetComponent() means find a Text Component on the same object as the Canvas Component, not a Text components on a child of the Canvas component, which is what I think you were aiming for.
You could potentially fix this with GetCompenent().gameObject. GetComponent(), but again this isn't really ideal for a number of reasons and really relies on you having that exact hierarchy in your prefab.
Another potential fix is to use GetComponentsInChildren or GetComponentInChildren, perhaps tagging the object your interested in to help search.
Personally, I'd prefer to have some management script within the root of your prefab which holds a reference to the text object you want to access.
I really wanted to use a direct reference, but somehow the nesting of Text within Canvas within Sphere all as a prefab made that not work. GetComponentInChildren worked perfectly though! Especially since I know there will be no other Text objects childed to the character instances. Thanks so much!
Your answer
Follow this Question
Related Questions
Missing canvas elements on Build & Run. 2 Answers
Unity UI Text Blurriness 0 Answers