Okay, we're going to move right along and sort of pick up the pace as we start filling in more game play mechanics into our project. I think the next thing we should do to break up the monotony in our game is add a powerup. In other words, some sort of GameObject that you can run over and pick up and build up a meter of sorts. Then when you want to, spend that meter for some kind of powerup that we'll determine later. The first thing we'll do to achieve that is add another GameObject, let's call it power-up. Right clicking on our hierarchy here. We'll call it PowerUp. It's going to be another sprite-based game object, so might as well add a sprite renderer. I'm just going to give it a a default position in our scene here. Since we'll be essentially collecting the PowerUp we may as well put it on the same sorting layer as the cube, Player 1 in other words, and same order in layer. It doesn't really matter, but let's just do that. I made another sprite image for this. I'm going to import that. Called Tabors. That's going to be our power-up.
I bet most of you probably know who I'm referring to, Bob Tabor. I kind of consider him my mentor in C# and a lot of you I'm sure as well, but in case you're not familiar with him, this is Bob Tabor who runs DevU.com. Well, in my mind this is an homage to him. We'll be using Tabors as a PowerUp. We will set the attributes for that like before, make it points so it doesn't filter and make it all fuzzy. Apply that. We'll just drag it over there. Okay, so the first problem we'll need to solve is to once again as we did with the sphere controller is sort of roll our own kind of collision detection and based on that, destroy this PowerUp and then increment the power meter of sorts. Let's create a new script. Let's call it PowerUpController and attach that to our GameObject. Attach it to the PowerUp GameObject as a component. And open it up. What we should do first is I guess get a reference to the cube GameObject so that we can do this collision detection. For this we'll go back to the previous way of finding it using the GameObject.Find() method as opposed to the other way, which is dragging and dropping directly from the inspector.
This could kind of cause problems. I know this ahead of time. Let's just do it that way. We'll make a field, GameObject. Call it CubeReference as we did with the SphereController. In Start() we'll set that reference with the Find() method from the GameObject class. Now we can simply refer to how we calculate the distance between the objects in the SphereController and sort of copy that and paste it here. Let's go to SphereController and that was where? In the EnemyProximity() method with distance. Now I realized we never did change this to a local, lower-case variable following our camelCasing rules that we're trying to impose on ourselves to make things a little easier on ourselves while we look at our code and can see at a glance if it's a field or a local variable. I'm just going to quickly make those changes and save that. I'm going to just copy all this stuff right here and we'll plunk it right into our Update() method for our PowerUpController. I want to make this code a bit simpler actually now that we know about constructors a little bit better. Let's rework it with the following minor changes I suppose. Let's take that out. Actually we'll leave a comment here. We'll make a new one. We'll say, "Eat or Destroy PowerUp" That's basically what we're doing here.
In the constructor, we can just pass right in the details as before that subtracts the transform position of this object from the cube to kind of do this collision detection. We can just do it this way. I'm sort of splitting it up on two lines so that it's a little bit easier on the eyes. Don't get confused though, this is just an ordinary method and I'm passing in two arguments separated by the comma. transform.position,y, takeaway.... Same as above but for the Y-coordinate. Here we will simply, I'm sorry, I didn't fix that. We'll make it a little more lenient. You don't have to be right over the PowerUp. You're barely touching it so we'll just sort of make these all .5 rather than .1 distance. Here we will say well, this gameObject, so the property that references this gameObject is again GameObject. Comes from the Component class that we derived from being a MonoBehaviour, which is a Component. We'll just reference gameObject. You know, you can say this.gameObject as I often do. Anyways, this time I'm just going to do gameObject Let's put a comment that's more relevant to this. I'll say "Determines whether or not Cube is near the PowerUp "Some leniency within 0.5 units." Alright?
Alright, so now for the powerup meter what we can do is we can create a field that simply increments by some I don't know, preset value that we can determine the code whenever we pick up the PowerUp in this collision detection That should be pretty easy. Let's see what we can do about that. Let's make it a, well, we can see in the inspector at least right now, we'll say public int PowerUpMeter. We'll give it a preset value. Make a little comment. We'll give a bonus fifty to PowerUpMeter at start-up. Right below here we can increment when we... Yeah. PowerUpMeter will += so will add to whatever its current value is, 100. So that's how much we'll be giving to the PowerUpMeter whenever we collide with it. Alright, so let's run that and see how that works. Alright, so PowerUpMeter right now is zero. We want to watch that as we start out, alright, it's at fifty so we're kind of around the PowerUp and we grab the PowerUp but this is kind of a problem, isn't it? We lose the PowerUp as soon as we destroy the GameObject. The PowerUpMeter, it increments. Actually it doesn't increment because it happens, it would happen after the object is destroyed. Nevertheless, this is a problem because even it's allowed to increment that increment is only, or that PowerUpMeter is only available as long as the GameObject is available.
This is going to be a bit of a problem in our script because we know we want to destroy the PowerUp GameObject. That much is clear. Otherwise the GameObject will just linger around and become an endless PowerUp well which is not what we want. Maybe what we can do is create another class, another script that handles or manages PowerUps including building up the meter and other useful stuff and just leave this PowerUpController script to handle the basic logic of collision detection, destroying the object and whatever else is just relevant to each individual PowerUp. Let's do that, but before we actually move onto doing that, why not refactor what we have so far considering we don't want to clutter up the Update() method. Chances are we'll be adding more code to this class as we move along. Let's do this, all we want from this method is to determine a binary outcome. Either the PowerUp is eaten or it's not. True or false? Maybe it's easier to read if we simply create a method that returns a bool based on this outcome. Let's write something that will resemble that. Let's make a method called, well, let's make it private because we're not calling it outside of its class. We'll call it AtePowerUp() and just move all of this in here. We're returning ... Let's see. How are we going to do this?
Alright, so let's just take this out temporarily and say if this collision detection pans out, we'll just return true. Else return false. Right? Now we'll want to put this in a conditional that then destroys the GameObject and so on depending on whether or not this method returns true. I don't even want that in the Update() method because I'm kind of funny that way, so I'll just make another method to handle the outcome of this conditional check. We'll do private void DestroyPowerUp() What do we have before? We have to then first check whether or not AtePowerUp() return true or false, so do this, AtePowerUp(). Call it within that if conditional because the return a true or false so this is perfectly legal syntax. Then just plunk that in here. Now up in the Update() all we have to do is call that method which itself calls the AtePowerUp() method. Again, a little bit cleaner, a little bit easier to look at when you're first glancing at this after maybe a long break of looking at your code. You'll learn to appreciate this kind of refactoring later on when you come back to looking at your code.
Also come to think of it we should probably destroy the PowerUp after a period of time has elapsed regardless of whether or not it has been picked up. In this destroy method, we can use a counter to do this after a certain amount of time has elapsed, alright? Let's do something like this. How about right in the DestroyPowerUp() method, well, let's start by putting up a timer and set it to one to start off with. Down here we can just increment it every time DestroyPowerUp() is called because this is going to be called on every frame. We'll make another conditional that says if timer modular four forty is equivalent to zero, so after every four hundred and forty ticks we'll just automatically destroy this PowerUp whether or not we grabbed it or not. We'll say GameObject.Destroy(gameObject) gameObject, same as if we grabbed it. Well, let's make this public in the inspector and so when we run this we can just take a look at that and make sure that even if we don't grab it, it disappears. We'll worry about the other stuff later where we manage our PowerUps. Close to 440, and... Yep, there it goes. We lose our PowerUp.
Alright, so that is a pretty good start I think. We still have a little ways to go before we massage our PowerUp mechanic into a manageable state. I think in the next lesson what we'll do is we'll make that manage a class to do just that and store a powerup meter and all those other good things. Good job if you've been following along. Lots of good stuff coming up in the next lesson. Thanks.
Lesson 30 - Collecting Collectibles, Items and Powerups