TL;DR edit: some critical lessons I learned –
1. gameObject.enabled – what is this useful for?
Apparently while you can trust this boolean to be true for an object which is enabled, you can NOT trust it to accurately be false when the same object is disabled. Also, for clarity, I might be misconstruing this… What I know is that if I set a member variable to contain a reference to a script, i.e.:
PlayerController playerController; playerController = GameObject.FindObjectOfType<PlayerController>()
Then visual studio prompts me that I can use: playerController.enabled … it didn’t solve the problem, neither did playerController.IsAliveAndActive which, at the time, seemed like it would be a sure-fire winner.
2. null reference – the heart of the matter.
Well, as the error says, the issue is a NullReferenceException… so, what is very clear in hindsight is that the solution is to… well, check for a null reference! Thus the solution was to add this line of code:
if (playerController == null)
Of course there are a few more minor details, such as re-assigning the reference to the PlayerController to the playerController member variable, but this one line seems to have stopped the cascade of errors.
All the other related threads are in the 1.0 course; also, while they share the same error message it’s for a different reason. Here’s the scoop…
I have a PlayerManager.cs that has (for now) basically one job… Manage player resets. When the player damage-received exceeds the player shield capacity, the PlayerController.cs makes a call to playerManager.ResetPlayer(); Then the playerManager sets the player GameObject to inactive, waits a few seconds, and turns it back on at the “home” position.
So I’m only getting the “NullReferenceException: Object reference not set to an instance of an object” error after the player has died at least once. It comes from my EnemyController.cs, where I have this Update() method:
private void Update()
{
if (playerManager.PlayerIsAlive())
{
transform.LookAt(playerController.transform);
TryShoot();
}
}
The 3rd line: if (playerManager.PlayerIsAlive()) was intended to stop the errors. It seems that if the playerController is set inactive, and the enemy is told to LookAt() it, it’s a problem. Fair enough… But if I’m doing this check on playerManager, why do I still get the errors?
Well, I’ve tried a couple things, both of which I thought were all that was needed at the time, and apparently neither is. Allow me to walk you through my journey: First, in PlayerManager.cs, I’d set PlayerIsAlive to “return playerController.enabled”. That didn’t work. When I looked at the fine-print, I noticed that it says this boolean is updated for active objects, but not updated for inactive ones… Left wondering what utility there is in that, I moved-on and found what I tried next: “return playerController.IsActiveAndEnabled” Ahah! That’ll do it! NOT…
So then I got to thinking… Why not be more direct? So now, in my PlayerManager.cs (probably not the version on GitHub) I explicity set a local private boolean to false before I even deactivate the playerController, and I don’t set it back to true until after the player is respawned. Now playerManager.PlayerIsAlive() returns that boolean. FIXED, right?!?! NOPE.
At this point, I’m thinking I need to figure out the Unity event system… i.e. the moment the playerController decided it’s time to die/reset it needs to broadcast some type of event, and my enemy controller needs to be “tuned-in” to that… then each and every enemy will be notified the instant the player is inactive, and it can bypass it’s LookAt() command while it waits for the broadcast that the player is back in action…
Wish me luck!