Lesson 31 - Spawning and Managing Prefab Powerups

Tutorial Series: Introduction to Unity with C# Series

Previous Article  |  Next Article


Alright, as we turn our attention now to creating a manager class to manage instances of our PowerUps, we immediately have a few glaring problems to solve. First we have exactly how do we go about creating new instances off our basic PowerUp GameObject and controller script. Remember, Unity discourages us from using pure C# and building the GameObject purely in code and then using, say, the new keyword to create a new instance. That is possible. We will look at how we could do that at some point down the line, but right now, all we want is just to create simple, multiples instances of our PowerUps over time. How do we go about doing that, is the first problem to solve. Next, we have problem of wanting each instance to be somewhat different. They would have to spawn at random locations maybe and maybe have other differences between each other as well. Again, we don't have a basic class blueprint for creating new instances. Where can we build our instances off of a basic starting point without having even a constructor or fields set up that allow us to make an instance different from another instance? Finally, we have the manager will want to communicate with the controller in some way. That lets us manage the meter and also allows us to draw from that meter in order to go into a powered up state.

Implicit in that, is what does it mean to be in a powered up state? We will figure out something down the line, but first, let's just get the basic functionality of spitting out instances and collecting and destroying them, and building up meter out of the way. What we want to do first, is create a GameObject that becomes our invisible factory in our scene here that pumps out PowerUp instances in our game world. Create a GameObject and let's call it, PowerUpSpawn, to differentiate it from PowerUp. We'll want to make a new script, as I said. Let's call it PowerUpManager. There we go, attach that. Before we write something into the PowerUpManager, what's apparent to me right away, is all our Power Ups should be spawned at some sort of random location, bound only by the confines of the arena. We could make the PowerUpController determine the object's transform position randomly and set it in the Start() method, for example. We could do something like this. Say Vector3 and have a randomSpawn variable and simply say new Vector3().

In the constructor, which again, I think is best put on separate lines, I can use the Random.Range() method as we did before, and pass in for the X coordinate, the boundaries of our arena, so it spawns within the horizontal boundary of 6.2 and -6.2. As for the first constructor argument for X value and for the Y, we will make that -3.4 and 3.4 and of course we need the third argument. We should put it in there for the Z value and then stuff that right into the transform position for this GameObject. We could do that, but I'd rather tie this into the spawn GameObject, because this has to do with its position and space as it's being spawned rather than any sort of inherent field or property that describes the Power Up GameObject. Instead, let's just move it to the manager class instead. We will just put it in the start here and this is just for testing it out right now. We are not going to want to keep it in the start method forever. We are just going to want to test it out right now. We are going to want to assign this transform to the GameObject that we want to spawn. How are we going to do this? Well, what we know to do so far is GameObject.Instantiate(). I'm going to ignore the first argument. Second argument is randomSpawn, the position. The rotation, we will just give it the spawner's rotation value. It doesn't matter.

The question now becomes, what do we instantiate? What GameObject? Well, probably the most obvious way to do this is to have a public GameObject field in the spawn manager and then just click and drag our Power Up GameObject into that reference. Or else, use the GameObject.Find() method as we did in the PowerUpController. Use that as a basis for instantiating new instances of our Power Ups. We can try that, let's see how that works out. We will make a public field GameObject PowerUpHere, we will simply, whatever we put in the editor, whatever we click and drag to the inspector, that will be our object that we use to instantiate from. Save that and let's run the game and see what happens. First give it a Power Up reference that we're looking for. Alright, so it works. As you saw, it works, but it's kind of funny because we are creating a Power Up instance and as soon as the game is running, there's your Power Up. That instance serves as the basis for all other instances that the spawn manager is creating new instances off of. In C#, we make a clear distinction between classes as blueprints, versus instances that we create off of blueprints. The class exists independent of instances. You can have no instances at one moment in time and then you can have as many instances as you want. The class always exists. Here with Unity's way of doing things, we're creating instances off of instances. It's kind of funny, we want to keep those separate.

Well, Unity has a way of dealing with this, has it's own way of handling it, with something called Prefabs, that is actually a lot easier than you would think. It's basically just a GameObject bundled up with all the components that are attached to it, including its scripts, Sprite Render, transform and so on. It's all bundled up into a single unit that you can then keep outside of your game world, outside of your hierarchy, so it's not immediately in your game. Then only reference it in a way similar to how you've come to understand the class blueprints in C#. To create a prefab, you simply click and drag your GameObject into a folder in your Assets directory and it becomes a blueprint that you can create instances off of. Let's actually create a new folder called Prefabs. And I will show you how its done. Click and drag this object to Prefabs and there you go. It even gets bundled up into a new file that has the extension '.prefab.' Now that we have this Power Up, we can safely get rid of our GameObject that it's based off of in our game world. This GameObject we no longer need when our game starts. We can now click and drag this prefab game object into the same slot as before in the manager class. You can use that to create new instances off of. Pretty handy.

Let's just go back to code here. Just so we know what we're dealing with, it doesn't matter, but I will rename it to Prefab, just like that. We know we want to create instances over time, so why not next, create a timer and a conditional to spawn these instances. Let's start off with a Timer field right there. You no longer need this here in start, that was just for testing purposes. Let's just say, Timer = 1 in start. Set the timer, and in Update() we'll say if (Timer % 300 == 0) we will do the same thing as before. Put a little comment here for us, "Every 300 ticks", and... We will just increment the Timer in Update() and let's run it and see how the spawning occurs over time. At every 300, we will expect a Power Up to spawn. We are not going to try to collect them, we will just try to see how the spawning occurs in our game. This is our second one. We have 2 spawns of our Power Ups on the screen at once. This is probably not optimal. We will need to re-work this, in our destroy timer. We only have a single Power Up in the screen at a time, so it only gives us a short period of time to say, "Pick it up before it goes away."

For this, we will want to go back to the Power Up controller and change the modulo calculation to... Let's just do 140, so every 140 ticks. This may seem a little weird, but this will actually destroy at 140 ticks after the object is spawned. That's almost half way between the time between this object's spawn and the next object's spawn. We are calling that the object spawns in the spawn manager at 300, 600, 900, 1200 and so on, it will effectively destroy at 440, 740, 1040, 1340, and so on. I will stop there. I don't think I will be able to remember exactly how those increment. If this doesn't make sense, just remember that after the spawn manager creates new instances, right then and there is when the instance's Start() method is run and it's also it's Update() starts to run. In that instance's Timer starts right then and there as well. That's why at 440, it will disappear. Let's quickly just run that and see that this is indeed working as we are thinking it will. We should never see 2 Power Ups on the screen at once. It should only give us a short period of time to collect it. That's a pretty short period of time. It automatically collected on right there. Let that one disappear. Yep, looks like it's working right.

We still have this problem of needing to create a PowerUpMeter that persists even when the objects are collected or otherwise destroyed. Why don't we just make this meter a static property, or a field I should say, of the spawn manager and then have the Power Ups individually through the PowerUpController, reference it only to add to it when it's collected. We can easily do that like this. Go to our PowerUpController and take all of our references to PowerUpMeter, let me find them all by highlighting. Actually, we will leave that one there, we will just take this out and this and just move it to the PowerUpManager. Alright, simple. Make that a public static just so it's easy to find and reference PowerUpMeter. As we did before, just give it default bonus and here in the PowerUpController, we'll statically reference the PowerUpMeter through the PowerUpManager class. Just to use IntelliSense to do the job to see how we can get that static reference. There it is, Power Meter and plus equals 100. We will set that value back to the power manager class, since it's a static field now.

I guess the easiest way that we can output this right now to see the meter, is, this is temporary, but we will just output it to the debug log, just so we can make sure it's working right. Alright, we have to wait around for the first Power Up to show up. Hopefully we will be able to grab it in time. There it is and there we go, 150. Let's see if we can increment the way, teleport to get that one, what a daft move. 250, there we go. It seems to be working as we're hoping. 350, because now it's persisting even when we destroy our Power Ups, it's persisting in the spawn manager, PowerUpManager class. Alright, I think that's pretty good so far. I think we still have little ways to go. In specific, we need to set up this logic that defines what it means to be in powered up mode. So I think we will get right into that in the next video. Alright, see you there.

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


Please login or register to add a comment