Lesson 30 - Collecting Collectibles, Items and Powerups

Tutorial Series: Introduction to Unity with C# Series

Previous Article  |  Next Article


Transcript

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.


Related Articles in this Tutorial:

Lesson 1 - Who This Course is For

Lesson 2 - What to Expect from this Course

Lesson 3 - Installation and Getting Started

Lesson 4 - Starting the First Project

Lesson 5 - Prototype Workflow

Lesson 6 - Basic Code Review

Lesson 7 - Game Loop Primer

Lesson 8 - Prototyping Continued

Lesson 9 - C# Fundamentals and Hello World

Lesson 10 - Variables and Operations

Lesson 11 - Variables and Operations Continued

Lesson 12 - Floats, Bools and Casting

Lesson 13 - If Statement Conditionals

Lesson 14 - If Statements Continued

Lesson 15 - Complex Evaluations and States

Lesson 16 - Code Syntax vs. Style

Lesson 17 - Variable Scope

Lesson 18 - Object-Oriented Programming Intro

Lesson 19 - OOP, Access Modifiers, Instantiation

Lesson 20 - Object Containment and Method Returns

Lesson 21 - "Has-A" Object Containment

Lesson 22 - "Is-A" Inheritance Containment

Lesson 23 - Static Fields and Methods

Lesson 24 - Method Inputs and Returns

Lesson 25 - Reference vs. Value Types

Lesson 26 - Introduction to Polymorphism

Lesson 27 - Navigating the Unity API

Lesson 28 - Applying What You've Learned and Refactoring

Lesson 29 - Constructors, Local Variables in the Update Method

Lesson 30 - Collecting Collectibles, Items and Powerups

Lesson 31 - Spawning and Managing Prefab Powerups

Lesson 32 - Implementing Powerup State Logic

Lesson 33 - Displaying Text, OnGUI, Method Overloading

Lesson 34 - Referencing Instantiated GameObjects, Parenting

Lesson 35 - Understanding the Lerp Method

Lesson 36 - Creating Pseudo Animations in Code

Lesson 37 - Understanding Generic Classes and Methods

Lesson 38 - Animations Using SpriteSheets and Animator

Lesson 39 - Working with Arrays and Loops

Lesson 40 - Debugging Unity Projects with Visual Studio

Lesson 41 - Camera Movement and LateUpdate

Lesson 42 - Playing Audio Clips

Lesson 43 - Routing Audio, Mixers and Effects

Lesson 44 - Adding Scoring Mechanics and Enhancements

Lesson 45 - Scene Loading and Game Over Manager

Lesson 46 - Understanding Properties

Lesson 47 - Controller Mapping and Input Manager

Lesson 48 - Understanding Enums

Lesson 49 - Dealing with Null References

Lesson 50 - Handling Variable Framerates with time.DeltaTime

Lesson 51 - Preparing the Project for Final Build

Lesson 52 - Final Build and Project Settings

Lesson 53 - Introduction to the Unity Physics Engine

Lesson 54 - Understanding FixedUpdate vs. Update

Lesson 55 - Movement Using Physics

Lesson 56 - Attack Script and Collision Events with OnCollisionEnter2D

Lesson 57 - Projectiles and Stomping Attack

Lesson 58 - Parallax Background and Scrolling Camera

Lesson 59 - Infinitely Tiling Background Sprites

Lesson 60 - OOP Enemy Classes

Lesson 61 - OOP Enemy Classes Continued

Lesson 62 - Trigger Colliders and Causing Damage

Lesson 63 - Multi-Dimensional Arrays and Procedural Platforms

Lesson 64 - Finishing Touches

Lesson 65 - Series Wrap


Comments

Please login or register to add a comment