Lesson 45 - Scene Loading and Game Over Manager

Tutorial Series: Introduction to Unity with C# Series

Previous Article  |  Next Article


Transcript

The bulk of what's left to do, coding-wise anyhow, is have a game start screen, which will just be another scene from where we can load the main game scene that we've been working on this whole time. We'll also set up a RestartManager script so that we can quickly restart the game once it's game over. The first thing we'll do is create a new scene. We'll call it ProtoStart. We'll go to File, New, and grab a New Scene. We'll save scene as, here in our scenes folder. We'll call it ProtoStart. Click save. Before we actually establish the scene, what we're going to want to do is first copy the Background GameObject, because it’ll be pretty much the same with this scene. Control+C on the Background and paste that in. As you can see here, we're going to need to readjust our camera a bit so it's just like it was with our other scene, so just follow along with these changes. I can't remember what the color was. It doesn't really matter because you'll never see this, so I'm setting it to that anyways, and the size is 3.6.

I'll also want to create a splash screen with some game info and have a blinking ‘Press Enter’, so we'll have those GameObjects. Move the Main Camera up there. Create a GameObject and let's call it Splash, and we'll also have an Enter GameObject. For these, we're going to need to import some more assets. Let's actually make a new folder. Call it GameStart and we'll import these assets. GameOver, Instructions, which will be part of our splash screen, and PressEnter. For the splash screen, we'll create a SpriteRenderer and drag that there, set that to the Background layer, and make sure it's on top, so put Order in Layer to 1. Then try not to forget to individually set the attributes for each image. For right now I just want to set these up, these GameObjects. This is going to need a SpriteRenderer as well, and to make sure it's above the splash screen let's put on the second Order in Layer. It's got to be at -3.05, I guess is best. To get the PressEnter to blink, let's attach a script, so let's create a GameStartManager.

Again, we'll create a folder called GameStart and create a new script. Let's call it GameStartManager. I didn't remember to change the asset attributes, so for images let's go here, Point, and on the Enter as well. Where were we? We were in the GameStart, creating a GameStartManager. Alright, so to get this Enter image to blink, it's actually going to be really simple. Let's create an integer field called LoopTimer and in Start() initialize that to 1, and then Update(), We'll just have a conditional, if (LoopTimer % 30 == 0) we will use GetComponent<>() here to get the SpriteRenderer for the image. We'll return that to a local SpriteRenderer reference. We'll call this pressEnterRenderer. We'll say PressEnterRenderer.enabled equals, we'll flip this, !pressEnterRenderer.enabled, which would be the opposite value of what you previously held. So LoopTimer ++ to increment it, and we'll want to then assign that to the Enter GameObject, right? Perhaps the most important thing you'll learn in this lesson is how to load another scene. Loading the Main scene once we hit the enter key, we can implement that with this, in the Update(), if (Input.GetKeyDown(KeyCode.Return)) return key.

We will call this method called LoadLevel() from the Application class. It's a static method that does exactly what it says. We'll load the scene, which is ProtoScene, right? Another thing we'll want to do is just attach a music loop for our intro screen, for our start screen, so just put on the .. We'll actually import a new asset, MusicLoop0. We'll put that, sure why not, on the Main Camera, and we’ll have it loop and Play on Awake. It's a bit loud. I made it a little bit too loud, so I'm going to put it to .4 for volume. We're actually not going to need this GameObject because we'll never see it, the start screen, so we'll just delete that. In order for this method to work properly, we need to set up our scenes in our build settings, so go to File, Build Settings, and we'll add this current scene. We'll also want to add this scene. So going back to ProtoStart, and now I'll run this.

Alright, now in the main scene we'll want to create a game over GameObject and parent it to the Main Camera. We'll call it GameOver, and we'll want to attach the GameOver sprite to this, so we'll need a SpriteRenderer. Let's put that on the Enemy Layer and let's just make it order, I don't know, a large number just to make sure it's going to be above all the enemies. Since we normally don't want to see the sprite, we'll just default it to a opacity of 0. Since we're on the Main Camera, I have to put the Z-property for the position to 1, or else it will end up behind everything because the Main Camera is on -10 for the Z-property, so put that to 1. So we're going to want to Lerp this when it's game over, so normally we want the opacity set to 0. Now we'll want to create GameOverManager script that will be attached to the GameOver GameObject to handle this Lerping and so on. Add a new script, GameOverManager. We'll have a SpriteRenderer field called GameOverRenderer. You could do this locally, but we'll just local to the method, the Update() method, but I'll do it as a field here. We'll say public static bool IsGameOver to handle that game over state, whether it's game over or not. We'll initialize the renderer, the GameOverRenderer, here.

We'll just get the SpriteRenderer for this GameObject. Here we'll say if (IsGameOver) which we'll determine elsewhere, maybe you can take a guess as to where we determine that. We'll do that shortly. Say GameOverRenderer.color. We'll do a color Lerp, Color.Lerp() from the GameOverRenderer's current color to this color, 255. We're just interested in opacity changing, so 255, 255, 255, and we'll Lerp it not quite all the way. It will have some transparency to it still, so 80% of the way to 255. That's what .8 will represent there. Then we'll do it with this kind of multiple, that rate. Now let's parent a Restart GameObject to the GameOver object to handle the restart logic, so call it Restart, and here we will want the PressEnter. Again we'll put on the Enemy Layer. Let's put on that Order in Layer, and we'll want it near the bottom of the screen just like with the start section. Normally it will be turned off because, again, we don't want to see that under normal conditions. Put 999. What we can do is we can just reuse the GameStartManager here for this logic.

What we'll do is we'll enable the script only when it's actually game over, so when the IsGameOver bool is true, then it will launch this script and it will restart the game. It's kind of handy functionality there. So we can enable this back in the GameOverManager by referencing the script. We'll say GetComponentInChildren<>() because we're going to access that restart GameObject as a child to where the GameOverManager script is attached, to the GameOver GameObject in other words, so GetComponentInChildren<>() and I'll want the GameStartManager. What we want to do is enable that script, so first we're giving this reference, and it's going to be GameStartManager. We'll call it reStart, and we'll just say reStart.enabled = true. Simple. Now I'll just run this logic where when we hit return, it will reload the scene.

The last thing we'll need to do is set the IsGameOver bool to true, so where do you think is a good place to do this? Where we're being eaten, right? Of course. So let's go to SphereController, and right here we just reference that static field from the GameOverManager. So just say GameOverManager.IsGameOver = true. We'll set that all into motion or we just code it up. Alright, so let's see what happens when we run this. Hmm, so this is a bit strange. Why is this happening? Actually it's something really subtle that nonetheless has an impact on how we look at the rest of our scripts. What's happening here is when we restart the game, the GameStartManager loads the level, so it just executes each script's Start() method first and then goes through calling all the updates on each frame, and moves along just as it normally would. But perhaps you recall that some of our fields were assigned values at the class level, and not in the Start() method. They were initialized at the class level. That means these values are set when the scripts and their instances are first loaded into memory, but this is going to be before Start() is even called, right? So when we reload the game, it doesn't actually kill or destroy those script instances in memory. It just holds onto them because they're still being used. The result is, those values are retained between loading scenes, specifically the ones that are initialized at the class level instead of in the Start() method.

So in this case, the culprit is the IsGameOver = false bool that we set here. Now you may or may not want this, depending on what reloading the level or loading the next scene means in your game. In our game, we just want everything to reset from a convenient starting point. So we have to make sure all of our values that might change during our game are initialized in the Start() rather than at the class level, and therefore reinitialized when we load the level or the scene again. Let's change all the relevant class level variables in our project that could cause problems. I'm just going to go through them one by one. We got Speed, that will definitely be changing. It's not constant. This is a constant, so we don't have to worry about that, so let's put Speed initialized here. This may or may not cause a problem, so we'll put that here. SphereController, same thing with Speed. SpawnInterval is not a constant, but it doesn't change, we know that, so I don't have to worry about that. Let's say Speed equals that, initialize it there. Might as well put the Timer here and here as well. Yeah, that should be pretty good, so now we should no longer have that problem.

Alright, so probably the most important thing you learned in this lesson was how to load a scene from another scene. Doesn't matter if that's from a start screen or a level, one level to another, or a cut scene. It doesn't matter. The process is pretty much just like this. That's it for this lesson. I'll see you in the next one.


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