I have a barely-modified version of the Space Shooter tutorial project, upgraded to run on Unity 2017.3. What I’ve done thus far is rotate everything 90 degrees (so that it’s horizontal instead of vertical) and add one new enemy ship type that doesn’t even have the ability to fire or dodge yet; hardly anything else. Every now and then, one of the original enemy ship types will fire a shot, and the OnTriggerEnter function in DestroyByContact will report that the gameController variable is null. This is particularly bizarre to me because I retained the null check code from the tutorial when intializing the DestroyByContact script, and the Degug.Log that should fire when gameController is null never triggers. How can gameController be successfully set when the object is instantiated but still end up being null when it gets to OnTriggerEnter? You can find a copy of the script here:
And it errors on line 43 every time. It’s always an enemy bolt that triggers this “Object reference not set to an instance of an object” error too. Note that this is not every enemy bolt. The game needs to be running for a while before it happens, and it will happen nearly immediately after the bolt spawns.
oops … posted a hasty response that was wrong. re-reading lol
Well, you never actually check if the component is found. You check if the game object is found.
I’m not entirely sure what is wrong just looking at that code, but I have a suggestion…
pass the gameController reference / variable from the ship firing the bolt, instead of finding it (which is good practice, anyways).
However, if you’re keen, you could try to track down what’s wrong when this happens.
Maybe by placing the game controller object at the class level, and making it public, and if you get a null on the component, look up the variable stored in the inspector and see if it’s pointing to something it shouldn’t be?
So I added that public variable to pass the gameController by reference to any spawned hazards, but now I noticed a new problem, and I’m not sure if it was there before. The Boundary kills itself at some point. No error reported or anything; it just disappears from the scene at some point. That Boundary is responsible for telling all of my other objects to delete themselves, so now they all just pile up. Is there any way to find out why this Boundary disappears? It’s in my scene by default when I press Play to test in the editor.
So in that script, I have a list that I pre-populate with some static values, and it also accepts list entries in addition to those on the outside. From the best that I can tell, DestroyByContact’s OnTriggerEnter is being called before Start() can finish executing. If I’m correct and this is a race condition, how can I avoid it with dynamically instantiated objects?
Yes, it’s being destroyed in the enemy bolt’s DestroyByContact script. The script has a public List of tags to ignore in OnTriggerEnter. In the prefab for Bolt Enemy, that List has one entry, and it’s PlayerShot, because I want the enemy shot to ignore the player shot so that they don’t destroy each other. In DestroyByContact, in the Start function, I call Add() on the list twice to add “Boundary” and “Enemy” to the list of tags that it should ignore. This means that even if the prefab has a List of size 0 defined on it publicly, the Start function should result in a List of size 2 by the time it’s done. The Boundary doesn’t destroy itself on the first enemy shot fired, either, which makes this problem stranger. It can sometimes take minutes for an enemy shot to destroy the Boundary, but when it happens, I logged the full size and contents of the List of the console, and the only tag in the List is the “PlayerShot” tag that exists on the publicly defined List in the prefab. This leads me to believe that OnTriggerEnter is firing before Start() can finish, since if it had finished, then the List would also have “Boundary” and “Enemy” in it.
Start will always be before the physics loop, including OnTriggerEnter. Start won’t be interrupted by that, either.
You can even assign to your list in Awake; if the problem persists, you can try to track it down/make a note here.
I hope you can figure it out. I admit it sounds strange.
So I added some more debug statements, and it’s definitely calling OnTriggerEnter() before it even runs a single line of Start() in those rare cases where it misbehaves. How can I force the equivalent of a constructor to run before any other event takes place? I can think of a hack that would take care of it, but it doesn’t seem like the “right” way to do it. I’ve commented in this new file:
what ends up happening on the good cases (most of them) versus the troublesome hazard spawns. Lines 44 and 65.
That shouldn’t be happening, but if it is in fact happening you can just do something like this:
private bool startRan = false;
void Start()
{
if (startRan == false)
{
//Your start stuff here
startRan = true;
}
}
void OnTriggerEnter(Collider other)
{
if (startRan == false)
{
Start();
}
// The rest of your trigger code here
}
I’ve run into a similar problem where occasionally with NetworkBehaviour scripts I can have Update or other method calls occur before Start as well, and had to do something like this.
According to the docs, OnTriggerEnter can be called while a MonoBehaviour is disabled. Start would only be called the first frame the MonoBehaviour is enabled. Is this object or component disabled for a frame before being enabled? If that is the case it is possible to call OnTriggerEnter prior to calling Start.
Maybe try outputting the value of this.enabled at the start of your OnTriggerEnter to see if this is the case or not.
I haven’t been disabling it, so I’m not sure why it would be disabled. If Awake always runs before Start, is there a reason I shouldn’t do all of my initialization logic in Awake instead of Start?
That’s not true about Awake() running every time a component is activated; OnEnable does that, but Awake and Start run only once for the lifetime of the object.
I even tried to spawn a cube inside a cube and tested OnTriggerEnter on both to see if it ever occured before Start() but it did not…I didn’t think it would.
For the record, it’s good to do your self initialization in Awake(), and initialization that relies on other scripts inside Start()… in general. I honestly don’t think that’s your issue here, but since you asked about it sort of.
Just a side note: I thought you changed the code to pass in the reference to the game controller, but it still looks like you’re getting it in the script?
Can you post the project in a unity package in the thread?
Thanks. Also, you were right. I was passing in a common gameController, but I was still getting it in the Start() function separately. I’ve now commented out the part in Start and it’s still behaving the same. The offender always seems to be the Bolt Enemy prefab, and sometimes it can take a few minutes for it to misbehave.
Sorry, I was unable to reproduce or figure it out lol.
Maybe I missed something, but if it’s as you say and takes minutes to figure out…who knows if I’ll ever get it.
I noticed that your version was a beta and a bit older. If there’s a newer version you can get, I would suggest that first as you’re testing. Just to rule out a potentially fixed bug, for example.
Just to be clear, I mean a higher stable version (not a beta).
I’m not sure for linux. There is at least 2017.4.0f1 in the linux thread, I saw.
I can’t be sure that has anything to do with your issue, but like I said… just in case.
If you ever find a way to replicate the problem more reliably and can’t work it out, please feel free to reply back in this thread. Otherwise, I’m sorry I couldn’t assist you more, but I do hope you get it solved somehow.
Since you’re able to get the “messed up results” sometimes, you could try moving your initialization code (adding the words to the list) in Awake(), even though I’m sure that shouldn’t matter… you can at least try