Variable from another script isn´t updating
So, my question is actually pretty simple.
I have a script that destroys the obstacles in the game (DestroyerScript) that is as follows:
public class DestroyerScript : MonoBehaviour {
public int destroyed;
void Start()
{
destroyed = 0;
}
void OnTriggerEnter2D(Collider2D other)
{
if(other.tag == "Player")
{
Debug.Break();
return;
}
if (other.gameObject.transform.parent == true) //is child of another object
{
destroyed++;
Destroy(other.gameObject.transform.parent.gameObject); //destroys the parent
Debug.Log("Destroyed " + destroyed.ToString() + " obstacles");
}
else //is not child of another object -> essentially the condition that always plays in this case
{
if(destroyed == 5)
{
destroyed = 0;
}
else
{
destroyed++;
}
Debug.Log("Destroyed " + destroyed.ToString() + " obstacles");
Destroy(other.gameObject); //destroys obstacle that collided
}
}
}
I have the variable destroyed that is public (I know the correct thing to do is to manipulate it through a function, but this is only for a matter of testing) and I increment it every time a obstacle is destroyed (when five are destroyed, I reset the value of said variable to 0).
Now I have another script that I use to instantiate the obstacles (InstaForeground).
What I want to do in this second script is use the destroyed variable from the first script as the decider to whether I should or not instantiate the obstacles, which I´m trying to do as follows:
public class InstaForeground : MonoBehaviour {
.
.
.
.
void Start()
{
Spawn(); //here I´m just instantiating five obstacles as a starter
}
void Update()
{
if(destroyerScript.GetComponent<DestroyerScript>().destroyed == 5)
{
Debug.Log("FIVE WERE DESTROYED");
Spawn();
}
}
void Spawn()
{
//Irrelevant for now
}
.
.
.
.
}
But apparently, like this, destroyed isn´t being changed as it runs here (as it should according to the other script which updates it [since the Debug.Log isn´t being printed, it shows that it doesn´t enter the if condition which I established].
Why isn´t the variable using it´s values according to the DestroyerScript ?
Remember that destroyed is declared as public there.
OBS.: The console in pointing the following error:
NullReferenceException: Object reference not set to an instance of an object InstaForeground.Update () (at Assets/Scripts/InstaForeground.cs:60)
l-> It is referring to the if statement line:
if(destroyerScript.GetComponent().destroyed == 5)
Any help will be hugely appreciated.
I moved the question to the Help Room as we had covered NullReferenceException problems several times by now.
$$anonymous$$y main goal with what I´m trying to do is to make it in a way that I can only spawn a new batch of obstacles every time 5 past obstacles have been destroyed.
Nothing too wild, just a matter o controlling the pace in which the obstacles are instantiated in the scene, ins$$anonymous$$d of it being all at once (specially because they are supposed spawn infinitely until the player dies).
Answer by Bunny83 · May 03, 2017 at 08:42 AM
If you get a null reference exception on that line that means that either:
the destroyerScript variable is null, doesn't reference anything.
the object referenced in the destroyerScript variable doesn't have a DestroyerScript attached.
Those are the only two things that could cause a null reference exception. What exactly is "destroyerScript"? How does it get it's value in the first place? Do you assign something in the inspector or do you assign it manually inside a script?
edit
In the code you posted in the comment below you wrote:
//[ ... ]
private DestroyerScript destroyerScript;
void Start()
{
DestroyerScript destroyerScript = GetComponent<DestroyerScript>();
// [ ... ]
Here you first declare a member variable inside of your "InstaForeground" class. However inside Start you declare another local variable also named "destroyerScript". Setting this local variable won't change / set the member variable of your script.
Instead you don't want to declare a new local variable but simply assign something to the member variable
//[ ... ]
private DestroyerScript destroyerScript;
void Start()
{
destroyerScript = GetComponent<DestroyerScript>();
// [ ... ]
Note the difference between the two code snippets.
However keep in mind that calling GetComponent<DestroyerScript>()
the way you did is equal to gameObject.GetComponent<DestroyerScript>()
. So this only works when both your "InstaForeground" script as well as your "DestroyerScript" are attached to the very same GameObject. GetComponent will return null if there isn't a component of the specified type attached to this gameobject.
Additional note:
An exception in a program is an unexpected situation in your program that will forcefully terminate your method at the point the exception occurs. You're actually lucky that Unity actually catches the exception at a higher level. In a "normal" C# application an unhandled exception would result in the termination / crash of your application.
If you see in the console that any kind of exception is thrown you have to find the reason for that. Any other debugging actions are pointless at this moment. An exception breaks the normal program flow, so it's possible that important initialization code had not a chance to run which might cause tons of other errors. When you have a problem (runtime exception or compiler error) you always start debugging with the first error you encounter.
Regarding the first point: how can I be sure to be referring the object properly?
Is it something like this?
DestroyerScript destroyerScript = GetComponent<DestroyerScript>();
If so, where should I write it?
destroyerScript is a script attached to game object that destroys the obstacles in the game upon collision. The destroyed variable I assign through the script as it shows in the destroyerScript I posted on the original question.
@luigiwagner Please see my comment to your original question above. It describes how you should approach this problem.
Answer by OfficialCoatsee · May 03, 2017 at 08:53 AM
I think, and I may be wrong... But?
if(destroyed == 5) {
destroyed = 0; //maybe this is getting reset to early?
} else {
destroyed++;
}
Because you are doing a check, right? From the other script?
if(destroyerScript.GetComponent<DestroyerScript>().destroyed == 5) {
Debug.Log("FIVE WERE DESTROYED");
Spawn();
}
So why don't you do the reset, in the check?
if(destroyerScript.GetComponent<DestroyerScript>().destroyed == 5) {
Debug.Log("FIVE WERE DESTROYED");
destroyerScript.GetComponent<DestroyerScript>().destroyed = 0;
Spawn();
}
Also, I am assuming you are calling the 'destroyerScript' properly? Meaning that the InstaForeground script knows what the DestroyerScript is attached to (impossible to tell, considering I don't know if you have shown the full code for the InstaForeground script).
I tried doing the reseting in the check, but it didn´t work :/
If you think it may help, here follows the InstaForeground Script:
public class InstaForeground : $$anonymous$$onoBehaviour {
public int maxPlataforms = 5;
public GameObject[] plataform;
public float horizontal$$anonymous$$in = 7.5f;
public float horizontal$$anonymous$$ax = 14f;
public float vertical$$anonymous$$in = -6f;
public float vertical$$anonymous$$ax = 6f;
private Vector2 originPosition;
public int prefab_num;
private DestroyerScript destroyerScript;
void Start()
{
DestroyerScript destroyerScript = GetComponent<DestroyerScript>();
Spawn(); //this spawns the first batch of 5 obstacles
}
void Update()
{
if(destroyerScript.GetComponent<DestroyerScript>().destroyed == 5)
{
Debug.Log("FIVE WERE DESTROYED");
destroyerScript.GetComponent<DestroyerScript>().destroyed = 0;
Spawn();
}
if (destroyerScript.GetComponent<DestroyerScript>().destroyed < 5)
{
destroyerScript.GetComponent<DestroyerScript>().destroyed = destroyerScript.GetComponent<DestroyerScript>().destroyed + 1;
}
}
void Spawn()
{
int fr = 0;
Debug.Log("Entered Spawn()");
for (int i = 0; i < maxPlataforms; i++)
{
prefab_num = Random.Range(0, 1);
Vector2 randomPosition = originPosition + new Vector2(Random.Range(horizontal$$anonymous$$in, horizontal$$anonymous$$ax), Random.Range(vertical$$anonymous$$in, vertical$$anonymous$$ax));
if (fr == 0)
{
Instantiate(plataform[1], randomPosition, Quaternion.identity);
fr = 1;
}
else
{
Instantiate(plataform[0], randomPosition, Quaternion.identity);
fr = 0;
}
originPosition = randomPosition;
}
}
}
Answer by EdwinChua · May 03, 2017 at 08:33 AM
@luigiwagner The NullReferenceException means that the script was unable to find the correct component. Please ensure both scripts are attached to the same object, or assign the gameobject to a public variable.
i.e. destroyerScript
in your example could be used like this:
public class InstaForeground : MonoBehaviour {
public GameObject destroyerScript; // click and drag from inspector to assign. Ensure the object assigned has the DestroyerScript component added
void Update()
{
if(destroyerScript.GetComponent<DestroyerScript>().destroyed == 5)
{
Debug.Log("FIVE WERE DESTROYED");
Spawn();
}
}
}
Incredible, man. It worked like a charm.
Thank you so much for the help, it actually enlighted me in some important things regarding Unity!
Your answer
Follow this Question
Related Questions
Main Menu Help! 3 Answers
Object reference not set to an instance of an object 0 Answers
Broken Jump Physics 0 Answers
My character won't stop crouching, they dont stop crouchin 2 Answers
Display Variable with UI Text 2 Answers