Lesson 51 - Preparing the Project for Final Build

Tutorial Series: Introduction to Unity with C# Series

Previous Article  |  Next Article


Alright. What we’re going to do now is tie up loose ends in our game, tidy up a few things and make some tweaks, and add a bit more functionality so that the game is better prepared for when we create the final build in the next lesson. After that lesson, we’ll move on to a bit of a crash-course on making a 2D platformer, which will show you how to tap into the Unity physics engine for moving objects and collision detection, and so on. For this lesson, the first thing we want to do is we’ll tweak, or actually we’ll fix a bug that we created in the PowerUpController in the last lesson. Perhaps you spotted it and fixed it yourself. It’s a real simple fix, but basically the bug prevented our PowerUps from getting destroyed, because now that we’re using floats, it’s highly unlikely that we’ll end up with a Counter that is exactly equivalent to 140.

All we have to do here is just make it greater than that and that will fix that bug. Alright, the next thing I want to do is, let’s just put our Cube and Sphere objects at opposite sides when the game starts. -4.35, and 0 for the Y there, for the Cube. For the Sphere, we’ll make it 4.2. I found that the music was a little quiet. We have the music loops here. MusicLoop1 I think would be best set at 0.7, and MusicLoop2, a little bit louder at 0.8. I’ll save this scene, and want to change the loudness for the start section here as well. This one, let’s make that 0.6. Then save that. I also found that CrowdCheer was a little bit on the loud side. That’s here, this one. If we have a lot of Spheres on our screen, it’s going to just multiply and overlap, and going to get a little bit too loud, so let’s make that 0.45. Here, we have these Sprites, and did a hacky thing here where we put on the Enemy layer, really no point for that, just put on the Player1 layer, and put it on order 1, because I think the Cube is on 0.

That will ensure that this will, the Game Over Sprite will be above everything, which is basically what we want. Over in the SphereController, I realized that the game gets a little bit too difficult once we have a lot of Spheres or eyes on the screen. I thought a good idea would be to spawn the enemies around the face-off circle in the middle so that you can strategize and go to that middle area before a spawn occurs so as to maybe escape the next wave. We’ll just make sure that the enemies spawn around that area by employing this bit of code, which by now we’re familiar with. Right? To return either -1 or 1. Now it’s going to be 1f to 6 point… well, just outside the bounds or at the edge of the arena, 6.4f. This would be 1f and 3.5f and the same thing. There’s this PlayDelayed()issue that we’ve been having hanging around for a few lessons. That’s because the Play() method here that takes in a ulong has been deprecated.

Still able to do its job but because it’s been deprecated, who knows if it’s going to do the job as well as we’re hoping, or if it will be even functional in later iterations of Unity. We may as well employ PlayDelayed() now which is what the error was or the warning was telling us to use now instead.. PlayDelayed() Now it takes in floats. What we’ll do is we’ll just pass them at random value that we get from Random.Range() method anywhere between 0.01 and 0.3 seconds. Another thing I wanted to add was pause functionality, we’ll do it in the WorldManager. We’ll head over there. This will allow us to pause our game. It’s not going to be perfect, but it’s a nice little bit of functionality to have.. Let’s start off with a field here, a bool called PauseSwitch. I’ll just call it here even before we made it, just so I don’t forget, as I’m prone to do.. We’ll make that method right now., This method will just check for, we’ll say if (Input.GetKeydown(KeyCode.P)). so the P key will cause the game to pause. We’ll switch the bool to the opposite value.

Here, we’ll have a conditional., If (PauseSwitch == true && !GameOverManager.IsGameOver) we shouldn’t be able to pause a game when it’s game over. What we’ll do is, this is how we’re going to pause the game. We’re going to access the timeScale through Time, the Time class, Time.timeScale = 0. It behaves exactly as you expect. The timeScale handles the progression of time in the game so if you set it to 0, there’s no progression. That’s how that works. We also want to disable the CubeController because we don’t want us to allow us to use any inputs.,, GeComponent<>() enabled equals false., Else if (!PauseSwitch && !GameOverManager.IsGameOver) we’ll switch the pause, and we’ll go back to regular timeScale. In other words, unpause. Time.timeScale = 1. Now, we’ll just re-enable that CubeController script so we can go back to playing the game and read our inputs.

I’m just going to quickly test that out. Yeah, great. The CubeAnimate, I think this looks funny having these old iterations here, these methods that use different types of iterations, but I want to let it hang around so that you can reference it if you need to later. I’ll just comment this out. Now, we’ll want to disable the PowerUpManager script on game over. Go to the SphereController, and when it’s game over.. I’ll just put that above there and say, let’s say, GameObject.Find(“PowerUpSpawn”) Then we’ll use GetComponent<PowerUpManager>() to return the PowerUpManagerscript. We’ll sign that to a local reference, powerUpManager. We’ll call it powerUpManager with a lower-case ‘p’. We’ll say powerUpManager.IsPowered… We’ll also want to disengage PowerUp, so we’ll say it’s false. On game over, there’s no reason why it should be in PowerUp, and have the camera zoomed in and all that. This will also zoom out the camera.

We’ll say powerUpManager.enabled = false Next, we should have the horn stop playing when it’s game over,. so let’s go over to the WorldManager, and in the PreSpawnAirHorn() We’ll just have a little conditional check here. If (!GameOverManager.IsGameOver) then we’ll play the audio, only if it’s not game over .Alright. The next thing we want to do is clean up the font for our HeadsUpDisplay. It’s not been looking very good. We might as well put in some sort of finished state before we have the final build. Let’s go over to the HeadsUpDisplay. Now, what we’ll want to do is we’ll want the font to scale when the screen size is smaller. This will really only affect how our game looks in the editor. You may have been noticing the fonts stay the same size regardless of whether or not you’re playing the game and the editor is scaled down, in other words not maximized on play, and the fonts stay the same when you’re maximized on play. To make sure that fonts scale, I’ll just show you how to do that, here in the HeadsUpDisplay, we’ll just add all this code. We’ll say private int FontSize and public GUIStyle, kind of a funny sounding type, GUIStyle.

We’ll call this FontStyleResources to signify the resources in our game, not the scoreboard.. That will be another GUIStyle. We’ll say equals new GUIStyle() We’ll make one for the scoreboard. We want this to be slightly different for the scoreboard, just for the appearance of the game to look a little bit more interesting. Call that FontStyleScore. In the editor, we'll be setting some of these attributes in a moment. Now we’ll say also public int Offset. This will handle sort of a drop shadow effect. We’ll have another GameObject as a drop shadow. We’ll specify the offset in the editor as well so that it’s just a little bit offset and you get that drop shadow effect. We’re going to need a font. Go to Assets here, make a new folder. We’ll call this Fonts, although I’ll only use one right now.

I’m going to import this asset. This is a freely available Microsoft created asset called Trebuchet, a font asset. It’s the best one I could find for this task. I’m not totally happy with it because it’s not going to look as nice in our game when everything is all pixelated and retro-looking, whereas this font has nice curves and everything. I’m sure there’s ways to do this better using Bitmap images. You can take that on as a challenge to do it that way if you want. At this point, I just want to get the font looking halfway decent before we’re done with this project, and not get too complicated. We’re going to use this font as the basis for our GUIStyle font here. We’ll start, we’ll have a Start() method, say Font, we’ll assign this font to both FontStyleResources, and the font property, as well as the FontStyleScore. Remember this is a funny assignment notation we saw before.

Resources.Load<Font>() from our Font folder by passing in the string to reference it, “Fonts/trebucbd”. I think this is the bolded version. Yeah, I’m just doing this all in one line just to make it look a little cleaner. Now here, what we’ll do is instead of having a constant value for the Rect here that passed in the Label, we’ll make it relative to the width and height of the screen. Here, we’ll say Screen.width/85f + Offset. Here, for the height, I figured this value is best. You can play around with these values to see what they do. It’s hard to explain why I chose these numbers. We’ll no longer need this formatting. Actually, I don’t want that text there either. We’ll just remove this string.Format() that we wrapped around that. We’ll also want the FontStyle passed in, and we’ll also apply this to the other Labels. We actually can comment this out because we’ll determine the color in the editor. To handle the scaling, we’ll just have a method that we can call in the OnGUI() Method. Private void ScaleFontSize() and the field FontSize will be equivalent to Screen.Width/64. This will scale it depending on how wide the screen is. Also, I don’t want the score to appear unless it’s greater than 0.

We’ll just wrap this around a conditional. So, if (WorldManager.GrandTotalScore > 0) then and only then do we want to display this score. Now here in the editor, because we made the GUIStyles public, we have the GUIStyles here. We’re going to determine all sorts of properties here. We’re not going to really worry about too many here. You can even establish the font. We did that in code, but as well as the size as well, but right now what we’re going to do is we’re just going to change the color. Under Normal, let’s say make it, we had a magenta before, so just like that, and the Font Style, make it Bold. Rich Text is on. For this one, the Offset is 0. What we’ll do is we’ll copy this, and put it in its own child game object, and then make it offset. That will become the drop shadow. Control+C, copying this GameObject. We’ll call this HUDShadow. For this, what we’ll do is make this black, also be Bold, and the Offset will be 1. We want to do the same thing for the score. For this score, for the parent GameObject, for the GUIStyle, for the scoreboard, we’ll make it Bold.

For the Color, it’d be cyan I guess, something like that. This one looked good to me. To create this blooming effect with the scoreboard, what we’ll do is we’ll make an addition here to the WorldManager. We first have to determine whether or not we’re actually scoring. We’ll do that with this, a public static bool, we’ll call IsScoring. We’ll create a private method here. Actually, it’s going to return a bool. We’ll say private bool ScoringCheck(in) it will take in an int, which will look at the previousFramesScore and another int which is the currentFramesScore. What we’ll do is we’ll just check if (previousFramesScore < currentFramesScore) to determine whether or not we’re actually scoring. Else, return false. Now, in the GetCloseCallScore() here we’ll capture the previous frame score before it goes through all this calculation and gets the new score by assigning it to a local variable we’ll call previousGrandTotalScore. It will be the GrandTotalScore before the calculation. Then we’ll assign IsScoring, the bool to the result of the method, ScoringCheck() and we’ll pass in the previousGrandTotalScoreand the GrandTotalScore now, after the calculations have been made.

Now that we know whether or not we’re actually scoring, what we can do is we can Lerp the scoreboard to have this blooming effect when we’re scoring and look a little bit more interesting. For that, back in the HeadsUpDisplay, we’ll say create a private method, private void FontBloom() we’ll call it. We’ll take in GUIStyle. We’ll say sizeFactor equals, we’ll do a little ternary right here. If (WorldManager.IsScoring) then the size factor will be the FontSize for the script times 4. Else, it will be the FontSize * 2. We’ll Lerp the fontToScale. In this case, it will be the scoreboard font. We’ll Lerp the fontSize to whatever the sizeFactor, and we’ll do it at this rate. Because that returns a float and the fontSize is an int, we just cast all that to an int. It’s a whole number. Fonts are measured in whole numbers. You can play around with these values and see how it affects the positioning of the font, and run the game at maximum screen size as well as not maximized in order to see how this affects the positioning of the Labels. Alright. That’s it for all the little tweaks that we needed to do. In the next lesson, we’ll actually build the project. I’ll 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