Lesson 64 - Finishing Touches

Tutorial Series: Introduction to Unity with C# Series

Previous Article  |  Next Article


Alright, this is pretty much our final video where we're doing coding anyways, and what we’ll be doing this video is will be sort of tidying up our project and getting it in a sort of final state for building. So a lot of what we’ll be doing is cleaning up the code that we already wrote and sort of poring over code from our previous project, which I’ll be basically copying and pasting most of that. I don't want to take too much time, I want to sort of get through this. So yeah, the first thing we'll do is we've got to handle our ghost collision behavior. We never really did do that, so for that we're going to do, getting right to it, is go to our Enemies script, and we're going to want to make the OnCollisionEnter2D() method which is tied into the event.

We're going to want to make it virtual, so we can override it for the ghost, so it can have its own implementation. We're not going to want it want to have the default type of implementation. We’ll have it slightly different for the ghost. So we'll say simply protected virtual void. So just make that change. Now we’ll set up an overrided version of this in the Ghost script, so. we'll just… We’ll flesh it out later, but right now it's a protected override void OnCollisionEnter2D(). It sees already that we need this, or that we have the option having a virtual method of this name in the base class. So remove the call to the base method, and simply put for now: if (coll.gameObject.tag == “Player”) And we’ll come back and flesh this out moment. Alright, so actually for the ghost, let’s do this. We're actually to keep the Collider in the RigidBody. So just comment that out or remove it.

We're going to want to set the RigidBody as isKinematic. So actually first the Collider.radius = 0.16f, and Body.isKinematic = true. And what the ghost is going to end up doing is, instead of killing our player, you know you can't really kill a ghost, and a ghost shouldn’t be able to kill you, so we're going to have it spook the player. So let's go back to the PlayerController and set some, change some code here. So we’ll make move speed static, public static. And we want to have another field here. For public static bool IsSpooked, and a SpookTimer we’ll set later in this class. So basically, it’ll spook us for some given amount of time. Move this down here make it a little more appealing. Now we’ll access these static fields back in the ghost’s OnCollision2D() event. So when it collides with our Player. It will be the PlayerController.IsSpooked = true.

And basically what this will amount to is it’ll make us a little slower. It’ll derement our speed by one. So minus equals one. And we’ll want to destroy the ghost. Because basically, there's no way of escaping our ghost enemy and once we get touched by it, it just leaves basically. So GameObject.Destroy(gameObject). And so to set up a method to handle what happens to a player when the ghost collides with it, let's go back to the PlayerController and let's create a method in this class. Private to this class, so private void SpookedCheck() and I'll just write this out. Should be pretty easy to understand what's going on here. So start with a, we’re going to need a Lerping behavior, going to make the CheeseHead a little green under the gills when it get spooked so we’ll start with a private, or a local variable here called lerpTo. And a float called lerpSpeed.

And we’ll simply say if (IsSpooked) we’ll set the SpookTimer first of all. SpookTimer += Time.DeltaTime * 60. So incrementing roughly one in sixty frames per second on each frame lerpTo = 0, and lerpSpeed will be 0.6f. Else we’ll make sure that we're not green under the gills. So lerpTo will equal 1, and lerpSpeed will do a little bit quicker if he's in fact been spooked, we’ll lerp quickly out of this looking spooked, looking green under the gills, so do a little quicker otherwise. And then we’ll need the SpriteRenderer to make it look, the CheeseHead look green. So we’ll say GetComponentsInChildren() because we're going to want to make the entire CheeseHead green, including his fist for example. So components in children, that’ll return an array, right? So type SpriteRendere, we want all the SpriteRenderers. And we’ll store that in a variable called renderers, local to this method.

So this is going to be a little bit expensive I guess you could say, once we call this on every frame in Update() it’s going to be using GetComponent(). Sort of a a bit of an expensive method itself, but whatever it’ll do the job. I'm not too worried about taking up CPU cycles with this little game. So SpriteRenderer. It's going to return, this method’s going to return an array of SpriteRenderers, so call it renderers. Renderers. And then we’ll iterate through those renders. So foreach (SpriteRenderer renderer in renderers) I can just do it on one line her, do the Lerp. So Color.Lerp(). Pass in the renderer.color for the starting point, to a new Color. And we’ll simply type in, for the the red property of the RGB, the color. lerpTo(), so lerpTo zero if if we get spooked, which will result in a greener hue to our CheeseHead. lerpTo, and 1, 1, 1 for G, B and A inputs. And then we’ll do it at lerpSpeed * Time.DeltaTime. And of course, we’ll assign that to the renderer.color.

And also, we need to reset the SpookTimer. So if (SpookTimer) we’ll have it going for roughly three seconds at sixty frames per second. So if(SpookTimer > 180), Sixty times three. We’ll reset the SpookTimer to 0. MoveSpeed will be reset to its ordinary value, 3. I guess you could make this constant but whatever, just going to set it here to 3, it's not going to change. IsSpooked = false as well. Alright, let's just try that out. Actually to try to out what I'll do here is in the EnemiesFactory, instead of picking a random instance of our enemy, we’ll pick the Ghost, which is case 5. So we’ll say 5 temporarily here for testing. Of course, we have to check on Update() SpookCheck(), so here to check SpookCheck(). Run this and see if we indeed get spooked and then after three seconds, roughly, unspooked. There's ghostly gale. Yep, looks like we’re spooked. We’re turning green under the gills and after about three seconds we go back to regular MoveSpeed. Awesome.

So change that back to… And what else? OK so, in the OnCollisionEnter2D() I never did check to make sure it's just colliding with the player, in other words, or otherwise it’ll run this when it collides with just about anything, including platforms for that matter. So say if (coll.gameObject.tag == “Player”) Basically wrap that in here. I'll fix any sort of, you might get null references otherwise, so I’ll also fix the out. We’ll also get an error I noticed if we’re holding down the direction key to it when we start the game. See our CheeseHead now is on the far left, much farther than it should be, and this throws off the whole camera script that we have. So let's just fix that by placing the CheeseHead squarely at the center of the screen when the game starts.

You see here we have this error. We assigned its value for copy, but it's never used. That's not necessary, the way we did that, so let’s quickly fix that too. That was for the tiling script, right? Yeah, I basically showed you this to show you how you can assign something to a variable as it's being passed into a method. Right, you can do it all in the same place. They actually don't need this field at all, so just delete this and it should also fix that error. Because it’s in principle possible that this will never be assigned, so that's why it's complaining, that error. So we just simply pass in the Enum, just like that. That’ll fix that. We also have in the out of bounds method here, yeah I noticed we had enemies that were flying way up, upwards and sometimes it didn’t stop moving because I guess AddForce() kept adding to a previous AddForce() and just kept going on forever seemingly.

So let's just add a condition or another case in which we destroy the out of bounds enemy. Say or if the transform.position.y > 20, then we also destroy it. Right, because presumably it's going to just keep going up and up and up, so that fixes that. You may have also notice in the previous video that our CheeseHead was sticking to the side of the platform. There's a lot of ways to fix that issue. One way is our CheeseHead is a square collider, right? A box collider. So you could change it to a circle collider. That doesn't catch then on any sort of right angles, that's basically what's happening here. You can add a slippery material. I showed you materials in the previous video I think. You can do that to solve the problem. Also, actually related to this, is the surface arc of the effector. That should be changed too, we should make it a little wider or smaller. Can’t remember which.

So let's go to the prefabs here, and for the surface arc for PlatformGrassy, change that to 160 and that will just make it so that we don't actually catch the edges when we go up. When we're sort of going upwards, and we’ll want to do the same here for the rubber platform. And yes, I think the way I want to solve this once and for all is to just give the CheeseHead a slippery material. So we have a materials folder here, let's create another material. Physics 2D material, call it slippery and what we'll do is we'll just basically have zero friction and zero bounciness, and just apply this to the CheeseHead collider. And this will just make it so that he doesn't catch on anything. Set that.

Alright, so next I want to sort of HUD the way we did in our previous project. So basically we’ll be copying and pasting a lot of the code we used in that project. No sense really to type it out all over again. If you want to go back to those old lessons and figure out how to do it, go right on ahead, but for now I'll just sort of copy and paste all this code. It's been modified a bit so that it works with this project. And actually, the first thing on do is create a HUD GameObject under the Main Camera. So create empty, and call it HUD. And in scripts dot world, let’s create a HeadsUpDisplay script. So HeadsUpDisplay, and put that in the HUD. So I'll just copy and paste this in, I already wrote this so out. Feel free to pause and just write that all out if you want to, as we did in the previous project we're going to want to change the font style. In the inspector here and make it white to make it stand out, and also bold it.

I also want a GameOverManager like we had before so we’ll pretty much be copy and pasting a lot of that as well. So let's start off with parenting a GameOver GameObject to the Main Camera. So call this GameOver. We’ll want a script for this too. As well, create the script now. It's called GameOverManager. We’ll just put that on the GameOver GameObject. We’ll want a SpriteRenderer. And I’ll just imported these images that we’ll need. I almost forgot here, I didn't add the font for our HUD, so let me just do that now. And import that as well, not absolutely necessary but it's the little touch. This font is referenced in the HUD HeadsUpDisplay script.

Right, back over to GameOver GameObject, we’ll want the. GameOver sprite here, and put it on the Player layer. Sorting on the first order in layer. Make sure it's 1 on the Z value, and the opacity should start off as all the way down to start off with. And we’ll want to parent to this, another GameObject, and so again you can go back to the previous lesson that handled this to see why exactly I'm doing it this way. So Restart for that GameObject, going to want a PressEnter sprite. And put it set 2.5, actually -2.5. It’ll also be on the Player layer. And we’ll also want a script for this is well. So call this GameStartManager, and make sure to put it here. Here. We're going to want to start button for the GameStartManager as well as pausing, we’ll have that again as well.

So in the InputManager, I'll just add an axis here, just need 5, call this one Start and make it the return key, or enter key however you prefer, but you need to write in return. We don't have an alt button for that. And the GameStartManager, as before, will just sort of reload the level. So GameStartManager, just once again I'll copy and paste this new and you can pause the video to write this on your end. Pretty simple code though. And we also have the GameOverManager, same thing. I’ll copy and paste that, it’s pretty much as before with some minor modifications. This will have to recognize when it's game over, so for that we're going to need to add IsGameOver to the OnCollisionEnter2D() in the Enemy class. Or access to it anyways, we’ll access the static bool by going to the Enemy class.

And after we've done all this, we’ll simply say it's game over. So GameOverManager.IsGameOver = true. And to make sure that this works right with loading the scene, just want to remember to add the scene to the project in the build settings. So for this just add current, that should be good enough for that. And for pause functionality, we’re going to want to copy and paste this as well. Go to the WorldManager script. And we’ll want a GamePause() method. As well as a private bool PauseSwitch. And in Update(), we’ll call GamePause(). And before I forget this, I’ll set this to vSync = 1. Make sure it’s vSync to avoid screen tearing and jitteriness and such when we actually build a project. And right, the camera keeps moving when it's game over unless we basically… Well, the camera follows the CheeseHead, so if we zero our the position for the CheeseHead, that’ll prevent that from happening.

Ao in the Enemy class. In the OnCollisionEnter2D(), what we’ll do is we’ll change some of this code up. So we’ll say RigidBody2D CheeseBody = coll.gameObject.GetComponent<RigidBody2D>. We’ll need to reference it again, so store it to CheeseBody. And then we’ll say CheeseBody.velocity = new Vector2(0). And same thing here, we want to do this again. And we’ll still want to do the AddForce to push him up in the air. So say CheeseBody.AddForce(). The Stomper I noticed, it wasn't exactly catching some of our enemies, so I want to make it a little bit larger, so let's go to a CheeseHead Stomper and make the collider a little larger. So make this -0.35 and .2 Y. There you go, looks a little bit bigger.

And there's that pop sound effect that I want to play, not just when we stomp on an enemy but also when we destroy enemies in general. So, maybe a bit of a goofy way of doing this, but it works in general. In the DoDamage() method, here we check if basically the hit points are to zero and we destroy the GameObject, we can then access that pop sound that we already have. That popping sound. And play it here, so it's a little goofy because we'll have to access it from the Stomper GameObject, I would have probably made the popping sound somewhere else more globally accessible I guess you could say, if I knew I was going to be reusing it. But whatever, this will work. So, GameObject.Find(“Stomper”).GetComponent<AudioSource>. And then just play that popping sound. And then destroy the GameObject.

I also took the liberties to make a little bit of a musical theme for this game. So we’ll want to add that to this project. Just add a little bit of flair at the very end. So we’ll want on the Main Camera, an audio source. All import this same loop. Platformer_Theme_Loop that I made. Looping in Play On Awake, and turn the volume down a little bit on it, .75. And while I’m at it with audio, I also want to change the jumping sound a little bit. So it's pretty much a pitch that's consistent with the music. I guess I'm maybe taking this little bit too seriously, but anyways just make that little change. So 1.35 for the jumping sound effect. Alright, and that looks like that's about it. This project is pretty much done. If you want to build the project. Maybe go back to the video for the previous project that had to do with the final build settings, and just apply that to this project and you'll be good to go. So that’s the last video lesson. In the next video, we’ll be talk a bit about where to go from here.

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