Referencing variable from another script on another object
Firstly I'm aware this question has been asked to death, which is why I'm pulling my hair out here with my code - which, according to all the answers I've seen on here, is exactly right to the letter. I've got a public float value on one script that I'm trying to access on another script on another game object but for some reason Unity keeps throwing "Null Reference Exception: Object reference not set to an instance of an object" at me >.<
I have one script on one object that gives me a public float value in the Inspector.
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class CurrentHPSender : MonoBehaviour
{
public float currentHealth;
void Update()
{
Image image = GetComponent<Image>();
currentHealth = image.fillAmount;
}
}
Then I have script on another game object which attempts to access that public float:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class CurrentHPReceiver : MonoBehaviour
{
public float HP;
public GameObject Kirito_Damaged;
public float Turn_Damaged_At;
private bool KiritoDamagedSpawned;
void Update()
{
GameObject hpbar = GameObject.FindGameObjectWithTag("HPBar");
CurrentHPSender currenthpsender = hpbar.GetComponent<CurrentHPSender>();
HP = currenthpsender.currentHealth;
if ((HP <= Turn_Damaged_At) && KiritoDamagedSpawned == false)
{
Destroy(gameObject);
Instantiate(Kirito_Damaged, transform.position, transform.rotation);
KiritoDamagedSpawned = true;
}
}
}
And it's having an issue with the line
HP = currenthpsender.currentHealth;
I've tried using different ways of finding the object - GameObject.Find("") for example and nothing changes! Is there something wrong here that I can't see? Please help!
Answer by giorashc · Apr 13, 2016 at 10:18 AM
Do not use methods like GetComponent or FindGameObjectWithTag in the Update method as it will impact performance as it is high frequently called.
Do these in Awake()/Start() methods and keep the result in a private member of your script so you could easily access it wherever you need (in that script) without making these costly calls.
After you fix that, keep a reference of the game object with the sender script on the receiver script (via the unity editor) as a public member:
public class CurrentHPReceiver : MonoBehaviour
{
public float HP;
public GameObject Kirito_Damaged;
public float Turn_Damaged_At;
public GameObject hpBarGameObj;
private CurrentHPSender senderScript;
private bool KiritoDamagedSpawned;
void Awake() {
senderScript = hpBarGameObj.GetComponent<CurrentHPSender>();
}
void Update()
{
HP = senderScript.currentHealth;
if ((HP <= Turn_Damaged_At) && KiritoDamagedSpawned == false)
{
Destroy(gameObject);
Instantiate(Kirito_Damaged, transform.position, transform.rotation);
KiritoDamagedSpawned = true;
}
}
}
Use the script order execution (Project Settings) to make sure that the HP bar object is created before objects that use it (such as the receiver obj)
$$anonymous$$an you're a lifesaver! Accessing the hpbar as a public GameObject makes so much more sense. And yeah I'll definitely keep 'get component' and 'getobjectwithtag' commands out of Update - thanks for the advice (I'm fairly new to program$$anonymous$$g :D) Also I had no idea about the Script Order Execution settings so thank you for that!
Answer by M-Hanssen · Apr 13, 2016 at 10:38 AM
First of all it is very bad practice to execute GetComponent or FindGameObjectWithTag inside the Update method. Please do this on Awake or Start.
I've taken the liberty to modify your script so hopefully it will be clear to you how to do this the proper way. Still FindObjectOfType() should be avoided because it is very inefficient!
using UnityEngine;
using UnityEngine.UI;
public class CurrentHPSender : MonoBehaviour
{
public float CurrentHealth;
protected Image Image;
protected void Awake()
{
Image = GetComponent<Image>();
}
protected void Update()
{
CurrentHealth = Image.fillAmount;
}
}
public class CurrentHPReceiver : MonoBehaviour
{
public float Hp;
public GameObject KiritoDamaged;
public float TurnDamagedAt;
protected bool KiritoDamagedSpawned;
protected CurrentHPSender CurrentHpSender;
protected void Awake()
{
CurrentHpSender = FindObjectOfType<CurrentHPSender>();
Debug.Log("Currenthpsender object is:" + CurrentHpSender);
}
protected void Update()
{
Hp = CurrentHpSender.CurrentHealth;
if ((Hp <= TurnDamagedAt) && KiritoDamagedSpawned == false)
{
Destroy(gameObject);
Instantiate(KiritoDamaged, transform.position, transform.rotation);
KiritoDamagedSpawned = true;
}
}
}
Thanks so much! THIS SEE$$anonymous$$S SO $$anonymous$$UCH $$anonymous$$ORE EFFICIENT THAN WHAT I WAS DOING haha I'll definitely keep your advice in $$anonymous$$d for the future and try to be as efficient as possible (I'm quite new to program$$anonymous$$g) - all part of the learning experience so thank you :)
Answer by xavierwielemans · Apr 13, 2016 at 10:39 AM
Hi guys, I agree with @giorashc, using Find* or GetComponent methods (slow by definition) in Update() is not a good practice. That being said, good news for you @brendans95: no bugs in your scripts! :) I copy-pasted them in a unity project, attached them to 2 new GameObjects (sender and receiver) and tagged the sender with a new HPBar tag. Then I ran my scene and got no error. The exception at line 16 in your script does not mean that Unity can't find the currentHealth public variable in your sender, but rather that your currentHPSender variable is null. And since currentHPSender is where your store the result of your GetComponent() call, it probably means that your hpBar GameObject has no CurrentHPSender script componnent.
i.e. it seems you indeed have at least one gameobject in your scene that has the HPBar tag (otherwise FindGameObjectWithTag would have returned null and you would have had a null ref exception at the previous line, line 15). But that game object has no CurrentHPSender script added to it as a component.
It may also be that you tagged several GO with the HPBar tag and FindGameObjectWithTag returns you another one than the one you expected (and one that has no CurrentHPSender script).
A good old way to check and debug all that is to test the intermediate return values of your code:
GameObject hpbar = GameObject.FindGameObjectWithTag("HPBar");
if (hpbar == null) {
print("ERROR - no hpbar object found!");
} else {
print("hpbar object found. Name: " + hpbar.name);
CurrentHPSender currenthpsender = hpbar.GetComponent<CurrentHPSender>();
if (currenthpsender == null) {
print ("ERROR - no currenthpsender script found on hpbar object!");
} else {
HP = currenthpsender.currentHealth;
}
}
Hope this helps!
Cheers,
Xavier
Thank you so much! This will definitely come in handy to make sure I'm not doubling up on those tags - and yeah no Find* or GetComponent commands in Update for me from now on! Thanks!
Your answer
Follow this Question
Related Questions
What to use instead of GameObject.Find and GetComponent 2 Answers
Unity GetComponent Script? 1 Answer
Why i can't get Rect Transform 2 Answers
Is it possible to get a component from a parent of a found GameObject? 0 Answers
How to fix "no overload for method get component takes 1 arguments" 1 Answer