Alright, before we go on to the topic for this lesson, which is dealing with null references, there's a bug that we produced a few lessons back when we moved values that were assigned at the class level, we moved them to Start() method. The bug that popped up is kind of interesting and I want to quickly fix it and cover the reason for why it popped up. First I'll show you the bug. I’ll try to time this accurately so we’ll be in PowerUp right before the next spawn. Alright, so a good place to start to figure out why this is happening, is to look at the Speed field here for the SphereController. After all, that's what seems to be at fault. The Spheres are going too fast when thy shouldn't be. Right. Why is this happening exactly? Well, here's why.
The Speed field, here, it's static so it's shared across all instances of Spheres. But now that we moved the initialization of the static field to the Start() method, each time a new instance is created it runs that instance’s Start() method. That Start() method will reset the value back to its original value which we expect to be half the value when we're in powered up mode. Right. That's the simple explanation for why this bug popped up. It's a good example of how you can fix one problem, we had the problem of static fields retaining their values when restarting the game. That's the reason why we tried to fix that problem. It caused another bug to pop up, like coding whack-a-mole, as often happens.. I think the easiest way to solve this is to have the Speed value .. Move it back to being assigned and instantiation and then when the game resets we just make sure to reset these values, as well, in the GameStartManager. Let's re-move this here, reassign it back here. We'll do the same for the CubeController just for consistency sake.
The manager, the GameStartManager, we'll just make sure to reset those values. Alright, onto the main topic of this lesson, null references. Null reference means exactly what it implies. An object in code, a reference type, remember, that gets destroyed and yet other blocks of code continue to refer to the object as if it exists. These code blocks that refer to it, they get null returned because that memory pointer or reference is no longer pointing to a particular spot in memory since it's been destroyed. Right. We've had a null reference hanging around our code ever since the beginning of this course, which I'm betting you've noticed whenever our Cube gets eaten and the Debug.Log() complains. Sorry, Let's clean up our Debug.Log() here. Let's get rid of that. We don't need that anymore. Let's do this again. Keep an eye on the console for that null reference error to pop up. As you've probably seen a bunch of times, it’s actually hidden behind here, ‘MissingReferenceException: the object of type game object has been destroyed but you're still trying to access it.
Your script should either check if it's null or it should not destroy the object.’ That's a pretty good hint. Actually, it implies exactly what we're going to do to fix this, in this lesson. Now with our project almost done, I feel this is the right time to deal with this issue. We absolutely have to deal with it, null references are not something you want lingering in your code. The correct way to deal with null references or at least the most basic way of dealing with them, is to take all of your code blocks that refer to the object that might become null, and wrap these code blocks in a conditional check that first checks whether or not the object is null, and only executes the relevant code if it's not null. In other words, we would have to do something like this. Our Cube is being destroyed and becoming null, so wherever we have a reference to the Cube GameObject, one way we can do this is to just search our entire document for the Cube.
Well that's going to take a little too long, so I'll just show you an example of one way of fixing this without committing to it. We know that there's a reference to the Cube GameObject in the SphereController, right? For example, where we're tracking the movement of the Cube right here. We can just wrap this entire code block in a check, to check if it's null. If (CubeReference !=null) so as long as it's not equal to null, go ahead and execute this code block; Otherwise if it returns null, then don't. That avoids that null reference error but you'd have to do this with every case in which you have reference to this game object. We could do that, but we have to ask, “Do we even have to destroy the Cube GameObject to begin with?" In this example .. Here, I'll undo all this since we're not going to commit to it. In this example, this is the culprit for the null reference error. Now, if this were an object that could get out of control, in memory in some way, I'll say it was a fired bullet. In which case, each fired bullet, if it were not destroyed in memory, you'd imagine it would just build and build until you have this cache of unwanted bullets that you still don't want to get rid of for some unknown reason.
Kind of a twisted programmer’s version of a hoarder. Just clogging memory space for no particularly good reason. In this case, we're not in that kind of danger. It's just a single GameObject. We only need it to be gone at the end of the game, at which point we have very few options but to restart the game or quit. What we can do instead is just make the object invisible, and handle this quite differently and maybe even come up with a more interesting game over scenario. I'm going to comment this out and place a comment here for future reference. “Removed to fix null reference in final build.” Now, we'll deal with the GameObject this way, when it gets eaten. I'm going to put a comment here and say “Disable CubeController Script” so we can't move, for example. To do that, tap into the CubeReference, use GetComponent<CubeController>() to grab the CubeController, the actual script, and just set it's enabled property to false. Alright, simple.
That takes care of that part. Comment, say, “Cube gets swallowed up.” We'll animate a swallowed up animation, by the Sphere. For that just say, take that CubeReference, and take its transform.localScale and have that as a result of a Vector3.Lerp() from it's current transform.localScale. Get this out of the way. All the way to the end state of a new Vector3. We'll want to scale it all the way down until it's pretty much invisible. And at this rate, why not make it a little bit more interesting, the animation? Let's make it spin. “Cube spins as it's getting swallowed.” Again, reference the transform, this time rotation. Set that to a new Quaternion() which is just a fancy term for rotation.. It's very similar to a Vector3, except it has four parameters x, y, z and w. For that, we'll set it to 0, 0... The only part we're actually interested in on a 2D plane is transform.rotation.z. That's the... That'll be that parameter that would affect the rotation that we'd see visible on 2D plane.
We'll multiply it, we'll make it spin half the Speed, we'll multiply it by .5. Half the speed of whatever the rotation speed of the Sphere is rotating at. We need a ‘w’ argument so just pass in the current transform.rotation.w. Alright, now we're going to need to keep the SphereController script alive just long enough so that it creates this game over animation. As soon as the Cube's almost invisible, so by the time it Lerps its scale down to about .01 or so, we can check for when it's just about down to that low and make the Cube invisible. And also, more importantly, disable the SphereController entirely, so that points no longer get awarded and more spheres get spawned. For that, we just add a conditional, say if CubeReference.transform.localScale… and we can use x or y. I'm going to use the y property. If it's less than .011 so just about, almost all the way down to the end of the Lerp.
I'll say GetComponent<SphereController>() for this particular GameObject that this script is attached to. We'll have multiple scripts attached to multiple GameObjects, just remember that. That's why this is going to work. Set enabled = false. We'll also want to disable the SpriteRenderer for the Cube. I'll say GetComponent<SpriteRenderer>().enabled = false. That's the way we're going to solve this null reference issue. It's far from perfect. I mean, one obvious issue that we have left over is that the score keeps on increasing for a moment as we're getting swallowed up, but that's okay. I'm not going to worry about solving that issue. You can take that up as a personal challenge if you want. There's a lot of ways you can go about solving that. The way that we fixed the main issue is all I was really interested in, and it's good enough for what we need. Alright, that's it for this issue. I'll see you the next lesson.
Lesson 49 - Dealing with Null References