0:00:00.000,0:00:05.940 Hi, my name is Mark. For years I've wanted to [br]make my very own video games, using software 0:00:05.940,0:00:13.320 like Unity. Unity is the powerful game engine [br]behind titles like Cuphead, Neon White, Tunic, 0:00:13.320,0:00:18.600 Outer Wilds, Hearthstone, Firewatch, [br]and even the Pokemon Diamond remake. 0:00:18.600,0:00:26.280 But I've always found that lengthy, multi-part, [br]meandering tutorials just send me to sleep. I 0:00:26.280,0:00:31.140 can't learn by watching someone else - I have to [br]get hands-on and figure things out for myself. 0:00:31.140,0:00:37.500 And so last year I developed a solution that [br]actually works. It's a three-step technique 0:00:37.500,0:00:44.820 where you: one just learn the absolute basics of [br]Unity. Then, two, cement those lessons with simple 0:00:44.820,0:00:51.180 exercises. And then, three, figure out the rest as [br]you go along. And it totally worked! In the space 0:00:51.180,0:00:57.000 of about a year, I went from ripping off iPhone [br]games to working on my very own puzzle platformer 0:00:57.000,0:01:03.480 about magnets. And I released an interactive [br]video essay that's had over 100,000 plays. 0:01:03.480,0:01:08.580 But wait, I hear you say! How do you do [br]step one? How do you learn the basics, 0:01:08.580,0:01:11.580 when the software is so complicated to figure out? 0:01:11.580,0:01:15.360 Well for me it was about writing down [br]a list of things I would need to know, 0:01:15.360,0:01:19.980 regardless of what game I was going to make. [br]Things like how to make a character appear and 0:01:19.980,0:01:24.060 move them around the screen. How to make [br]stuff spawn in and then delete it again 0:01:24.060,0:01:28.680 later. How to have collisions and game [br]over and animations and sound effects. 0:01:28.680,0:01:33.540 Then I learned all that by hunting through [br]lengthy tutorials, reading the Unity docs, 0:01:33.540,0:01:36.780 Googling esoteric words, and doing a lot of trial 0:01:36.780,0:01:41.820 and error. And so the whole point of [br]this video is to save you that hassle. 0:01:41.820,0:01:46.380 This video is the tutorial I wish [br]I had when I was learning Unity. 0:01:46.380,0:01:52.080 So in the next 40 minutes we're going to use the [br]engine to make Flappy Bird. Not because we want 0:01:52.080,0:01:57.060 to make Flappy Bird, but because in order to [br]remake this addictive iPhone game, we'll need 0:01:57.060,0:02:02.220 to learn basically everything I just listed, [br]from spawning objects to getting game overs. 0:02:02.220,0:02:08.460 This tutorial will cover every step of the way [br]from downloading Unity, to understanding the UI, 0:02:08.460,0:02:13.680 to writing your very first line of programming [br]code, to building a game that you can share with 0:02:13.680,0:02:19.080 your friends. And then, when the tutorial is over, [br]I'll share some concrete next steps that you can 0:02:19.080,0:02:25.920 take in order to continue learning the rest by [br]yourself. Sound good? Then let's get started. 0:02:26.880,0:02:34.200 Okay, let's start by getting Unity from the [br]website. Download and install the Unity Hub. 0:02:34.800,0:02:39.660 And then you'll need to make a free account [br]to actually use it. Once that's done, 0:02:39.660,0:02:45.840 you'll be asked to install the Unity Editor [br]- I'm using version 2021.3 for this tutorial, 0:02:45.840,0:02:49.020 if you're watching a million years [br]in the future and wondering why 0:02:49.020,0:02:52.380 things are different. Let's pretend [br]I have fast internet - Neeooowwwwmm. 0:02:52.380,0:03:00.300 We're not quite done yet. Under installs, hit the [br]cog icon on the Unity Editor and pick modules. 0:03:00.300,0:03:04.980 You'll see that Microsoft Visual Studio has [br]been ticked - this is the software we'll use 0:03:04.980,0:03:10.620 to write programming code. So hit continue. [br]And install Visual Studio. On this screen, 0:03:10.620,0:03:15.420 scroll down and tick game development [br]with Unity, and untick Unity Hub, 0:03:15.420,0:03:20.340 because we already have it. Neeooowwwwmm. [br]We don't need to make an account to use Visual Studio, 0:03:20.340,0:03:23.580 so skip that. And don't bother loading [br]it, we'll open it later. 0:03:23.580,0:03:31.140 Okay, that's all done now. So in Unity Hub, pick [br]new project. Choose all templates. And use 2D, 0:03:31.140,0:03:35.940 Core. This is an empty project, with a [br]few configurations to make it suitable 0:03:35.940,0:03:41.220 for 2D games. Give your project a name, [br]hit create, and let's get game makin'. 0:03:43.260,0:03:48.720 In step one, we're going to become familiar [br]with the default Unity user interface. And 0:03:48.720,0:03:52.500 as we explore the different panels, [br]we'll make the bird appear on screen. 0:03:52.500,0:03:58.800 Right. So this is the default screen layout [br]for Unity, and it's split into four panels. 0:03:59.880,0:04:05.340 First of all, down here, is the Project panel. [br]This will contain everything that is in our game 0:04:05.340,0:04:12.660 - like sprites, sound effects, scripts, tiles, [br]fonts, and so on. Some of this stuff will be made 0:04:12.660,0:04:17.940 in Unity as we go along. But we can also just drag [br]and drop files from elsewhere on our computer. 0:04:17.940,0:04:22.800 Like, I've made some sprites for the bird [br]and the pipe in Photoshop and I'm going to 0:04:22.800,0:04:27.540 import them into my project like so. I'd [br]recommend you make your own - that's always more 0:04:27.540,0:04:32.400 fun - but if you have zero artistic ability [br]then check the description for these assets. 0:04:32.400,0:04:38.940 The next panel is the hierarchy. This contains all [br]of the stuff that's in the current scene - which, 0:04:38.940,0:04:42.960 in most games, will be a level. We're [br]going to start by making the bird, 0:04:42.960,0:04:50.280 so right click and choose Create Empty. This [br]has made an empty GameObject... so what's that? 0:04:50.280,0:04:56.700 Well, a GameObject is essentially an invisible [br]container. It has a position in space, a rotation, 0:04:56.700,0:05:03.300 and a scale. Then, you can fill that container [br]with components - to add extra features. For 0:05:03.300,0:05:08.220 example, if we add a Sprite Renderer component, [br]we can slap the bird image onto the GameObject. 0:05:08.220,0:05:13.260 Absolutely everything in our level will be [br]a GameObject with components - the bird, 0:05:13.260,0:05:16.440 the pipes, even the user interface and the camera. 0:05:16.440,0:05:22.680 All of this magic happens in the third panel, the [br]Inspector - which is for messing with GameObjects. 0:05:22.680,0:05:28.440 So, once we've selected our new, empty GameObject [br]we can put a name in the top field - let's call it 0:05:28.440,0:05:34.860 Bird. And we can see and change the GameObject's [br]position, rotation, and scale, under Transform. 0:05:34.860,0:05:41.520 We can now press Add Component, pick Rendering, [br]and pick Sprite Renderer. To make this work, 0:05:41.520,0:05:45.540 we need to fill in the sprite field - [br]so just drag the bird image from the 0:05:45.540,0:05:49.620 project panel into the field [br]and viola, we have graphics! 0:05:49.620,0:05:53.280 That will, of course, show up [br]in the fourth and final panel, 0:05:53.280,0:05:57.240 the scene view. Here we can see [br]what's in our current scene, and, 0:05:57.240,0:06:01.560 if you want, you can use these tools to [br]move stuff around, scale it, and so on. 0:06:01.560,0:06:06.900 This section has an extra tab for game view, [br]which shows us what the game will look like from 0:06:06.900,0:06:13.440 the main camera when it's running. Also, from this [br]dropdown, we can set a resolution or aspect ratio 0:06:13.440,0:06:19.080 to get a better idea of what it will look like [br]when played - so I'm going to choose 1920 by 1080. 0:06:19.080,0:06:23.940 Oof, the bird takes up way too much space. [br]We could scale it down, but let's actually 0:06:23.940,0:06:29.700 just zoom out the camera. Like I said before, the [br]camera itself is a GameObject in the hierarchy. 0:06:29.700,0:06:35.880 And it has a camera component with stats we can [br]mess with. By changing the size, we can zoom out. 0:06:36.420,0:06:39.420 I'm also going to change the [br]background colour. Lovely. 0:06:39.420,0:06:44.340 We can now press the play button up [br]here to start... the world's most 0:06:44.340,0:06:47.760 boring game. Okay, let's make it a bit more exciting. 0:06:49.320,0:06:55.440 A quick recap. Unity has four panels by [br]default. Project holds all the stuff in our 0:06:55.440,0:07:00.900 game. Hierarchy lists all of the GameObjects [br]in the current level. Inspector lets us see 0:07:00.900,0:07:04.740 and change those GameObjects. And we [br]can see the level in the scene view. 0:07:04.740,0:07:10.800 And a GameObject is an invisible container that we [br]can fill with components, like a sprite renderer. 0:07:11.580,0:07:15.900 In step two we're going to use more [br]components to make the bird into a 0:07:15.900,0:07:19.320 physics object that is affected by [br]gravity. And then we're going to 0:07:19.320,0:07:23.400 write some programming code to make the [br]bird fly up when we press the space bar. 0:07:23.400,0:07:29.760 So let's add another component to our bird: [br]a Rigidbody 2D. This turns our bird into a 0:07:29.760,0:07:36.600 physics object, with gravity. So when we hit play, [br]the bird drops, and falls off the screen. Cool. 0:07:36.600,0:07:42.279 We'll also want this bird to be able to interact [br]with other objects, so let's add a collider. 0:07:42.279,0:07:43.920 A circle collider 2D. 0:07:43.920,0:07:49.920 Back in scene view we can see the collider as [br]a green outline. It's a bit off-center for me, 0:07:49.920,0:07:55.260 so I'll use the offset to move it. And, a [br]little game design trick - if we make the 0:07:55.260,0:08:00.720 collider a bit smaller than the image, it will [br]let the player get through pipes even if they 0:08:00.720,0:08:05.220 juuust touched the edge. It gives the game a [br]bit of leniency and makes it feel more fair. 0:08:05.220,0:08:10.920 The final thing to add right now: a script. [br]This essentially lets us make our own custom 0:08:10.920,0:08:16.680 component - but we'll have to write it ourself [br]using programming code. Choose New Script from 0:08:16.680,0:08:21.780 the components list. And call it BirdScript. [br]Once it's loaded, double click the script 0:08:21.780,0:08:27.000 field to open it up. This will open the file [br]in Visual Studio, which we installed earlier. 0:08:27.000,0:08:31.020 So, welcome to programming! It's not too scary, 0:08:31.020,0:08:35.460 promise. We'll take it slow. We're writing [br]in C sharp, that's the programming language. 0:08:35.460,0:08:41.340 And the only thing to worry about right now [br]is these two chunks here: start and update. 0:08:41.340,0:08:46.620 Start is for any code that will run as [br]soon as this script is enabled. And it 0:08:46.620,0:08:52.620 runs precisely once. Update runs constantly [br]while the script is enabled. And it will fire 0:08:52.620,0:08:57.360 off every line of code, every single [br]frame. Over and over and over again. 0:08:58.080,0:09:02.820 So the main thing we're going to be doing [br]with code right now is - well, if we go back 0:09:02.820,0:09:08.340 to Unity - see these numbers and text fields in [br]the components? And how we can change them in 0:09:08.340,0:09:13.320 the Unity editor? We're just going to write code [br]to change these stats while the game is running. 0:09:13.320,0:09:17.880 Just as a dumb example, and we'll [br]delete this in a second. In start, 0:09:17.880,0:09:25.920 we can type gameObject - that refers to this bit [br]up here. And then a dot. You'll see a list appear, 0:09:25.920,0:09:30.720 and many of the items refer to stuff back [br]in the Inspector, like isStatic, 0:09:30.720,0:09:38.940 tag, layer, and name. So let's pick name. [br]Then write an equals sign. And in quotes, 0:09:38.940,0:09:44.640 give our bird a name. Finally, we must always [br]use a semi-colon to mark the end of a command. 0:09:45.433,0:09:49.573 And we must always save the script[br]before we go back to Unity. 0:09:49.573,0:09:54.360 Now, when we run the game... the name of [br]the GameObject has been changed. Nice. 0:09:55.080,0:09:58.560 Okay, delete that code. That was just [br]for sillies - but it shows us how we 0:09:58.560,0:10:03.120 can use code to talk to the game. We can [br]write a command by choosing someone to 0:10:03.120,0:10:08.280 talk to - in this game, the GameObject [br]- and then a topic of conversation - its 0:10:08.280,0:10:14.160 name - and then a command - change it to [br]Bob Birdington. We'll be doing this a lot. 0:10:14.880,0:10:19.740 So what we actually want to do is... in [br]the Rigidbody 2D's component, under info, 0:10:19.740,0:10:23.400 we'll see a greyed-out field [br]for velocity. And we want to 0:10:23.400,0:10:27.120 write some code to add upward velocity [br]to the bird to make it fly into the air. 0:10:27.120,0:10:32.640 The problem is... initially, a script can [br]only talk to the GameObject's top bit and 0:10:32.640,0:10:37.200 the transform. Right now, this script is [br]completely unaware of the other components. 0:10:37.200,0:10:42.480 So we need to sort that out first. We need [br]to make a special slot on this script for 0:10:42.480,0:10:48.360 a Rigidbody2D - so we can then talk to it and [br]send it commands. This is called a reference. 0:10:48.360,0:10:52.380 We're going to create the reference [br]up here, between the class name and 0:10:52.380,0:10:57.840 the start function. We're going to [br]write public Rigidbody2D myRigidbody. 0:11:00.540,0:11:06.480 So we now have a slot to store a Rigidbody2D. [br]And we have a name that we can refer to - to make sure 0:11:06.480,0:11:12.060 we're talking about this specific Rigidbody2D. And [br]because we made it public, it means we can access 0:11:12.060,0:11:18.240 this slot from outside the script. So, if we save. [br]And go back to Unity, we'll see that the script 0:11:18.240,0:11:24.780 component now has a field for a Rigidbody2D. We [br]can drag the component into that slot, and viola. 0:11:24.780,0:11:28.740 We have established a line of communication [br]between the script and the Rigidbody. 0:11:29.340,0:11:36.300 Okay, back in Visual Studio. In update, [br]we can type myRigidbody. Then dot. And 0:11:36.300,0:11:40.440 now look at all the things we can talk [br]about. Angular drag, gravity scale, 0:11:40.440,0:11:45.540 mass - these are all properties on the [br]component. The one we want is velocity. 0:11:45.540,0:11:51.060 We want to set this to a new number, and so, just [br]like before with the name, we'll write an equals. 0:11:51.060,0:11:57.180 Now what we're actually writing here is a vector, [br]which is two numbers, to represent a position in 0:11:57.180,0:12:02.400 2D space. And in this case, it's used to represent [br]a direction for the bird to travel. We want the 0:12:02.400,0:12:08.940 bird to go straight up, so zero, comma one would [br]be a good one. I'm just going to use Vector2.up, 0:12:08.940,0:12:14.400 which a built-in shorthand for zero comma one. [br]And to give it a bit more power, I'm going 0:12:14.400,0:12:19.920 to multiply that vector by a number. Say, 10, [br]which should send the bird flying up in the sky. 0:12:19.920,0:12:24.720 Now, like I said before, any code in [br]update will run, over and over again, 0:12:24.720,0:12:31.560 every frame. So if we save the script and [br]hit play in Unity... off goes our bird. Bye!! 0:12:31.560,0:12:36.360 That's not what we want. We want this to only [br]happen when the player hits the space bar. So 0:12:36.360,0:12:41.040 it's time to use the most fundamental bit [br]of programming code: the if statement. 0:12:41.040,0:12:43.380 An if statement is like a gate. 0:12:43.380,0:12:47.640 You can surround some code with a fence, [br]and every frame that code will be completely 0:12:47.640,0:12:52.020 ignored. Unless, the game meets some [br]specific conditions that are written 0:12:52.020,0:12:56.700 on the gate - in which case the gate is [br]open, and the code is read and executed. 0:12:56.700,0:13:00.960 So we want to say "if the player hits [br]the space bar, then add upward velocity". 0:13:00.960,0:13:06.300 To do this... we can write if, and then in [br]brackets we can write the condition. This time 0:13:06.300,0:13:12.360 we're not talking to a component, we're talking to [br]Unity itself - specifically its input system. So 0:13:12.360,0:13:20.160 we'll write Input. Then we can pick GetKeyDown, [br]and in brackets, KeyCode.Space. This asks Unity 0:13:20.160,0:13:25.200 if the space bar has been pressed on this frame. [br]And then we'll finish with equals, equals true. 0:13:25.200,0:13:31.020 A quick note on equals signs - we use one [br]to make the thing on the left be the same 0:13:31.020,0:13:35.280 as the thing on the right. And we [br]use two if we're just checking if 0:13:35.280,0:13:38.520 the thing on the left is the same [br]as the thing on the right. Cool? 0:13:38.520,0:13:43.740 Anyway. So this code says... if the space bar [br]has just been pressed, then... and then we'll 0:13:43.740,0:13:48.960 use curly brackets - these are the fence in our [br]little analogy - and put the flap code in here. 0:13:50.100,0:13:54.240 So, now in update - every frame the game [br]will go to the gate and be asked "hey, 0:13:54.240,0:13:59.340 has the spacebar just been pressed?" If yes, the [br]code will fire and the bird will flap. If not, 0:13:59.340,0:14:02.760 it will skip the code in the curly [br]brackets and try again next frame. 0:14:02.760,0:14:08.160 So - save the script and go back to [br]Unity. We can now hit play and tada: 0:14:08.160,0:14:11.460 the bird goes up when we press space. We have now 0:14:11.460,0:14:17.340 created a character and made it react [br]to input. This is a video game. Hooray! 0:14:17.340,0:14:23.100 However, it feels like trash. The flap isn't [br]right, and it doesn't feel like the original 0:14:23.100,0:14:31.860 iPhone game. So we could change this number. [br]Save. Open Unity. Run the game. Not quite right. Stop. Change 0:14:31.860,0:14:36.540 the number. Save. But that's slow and [br]dumb. Let's do something smarter. 0:14:36.540,0:14:41.520 First, we're going to make a variable. Let's [br]go back to the top of the script and under 0:14:41.520,0:14:47.160 our reference to the Rigidbody, let's make [br]a public float called flapstrength. A float 0:14:47.160,0:14:51.960 is a floating point number - basically a [br]number that can have a decimal place. And 0:14:51.960,0:14:57.720 then back in our update code, we'll multiply [br]the vector2.up by flapstrength, instead of 10. 0:14:57.720,0:15:02.040 Now, back in Unity, you'll see that [br]the script component has a new field: 0:15:02.040,0:15:06.900 flapStrength. And we can change that [br]whenever we want to make the game feel 0:15:06.900,0:15:11.340 different. We can even change it during the [br]game, but note that anything you change while 0:15:11.340,0:15:15.360 the game is running won't save when you [br]press stop. This means you can play with 0:15:15.360,0:15:18.660 values to your heart's content without [br]worrying about screwing up your game. 0:15:18.660,0:15:22.680 So, if we mess with the flapStrength, [br]and also the gravity scale on 0:15:22.680,0:15:26.700 the Rigidbody, we'll hopefully get [br]to something that feels good. Ah, 0:15:26.700,0:15:29.640 changing numbers back and forth: [br]honey, that's game design! 0:15:30.300,0:15:31.440 Recap time. 0:15:31.440,0:15:36.000 We can use code to change the properties [br]of a component, while the game is running. 0:15:36.000,0:15:40.860 A script cannot talk to the other components [br]on the gameobject, by default. You have to 0:15:40.860,0:15:45.000 make a line of communication by storing [br]a reference to that specific component. 0:15:45.000,0:15:49.860 We create the reference in code, and then [br]fill it in Unity by dragging and dropping. 0:15:50.400,0:15:56.280 Code in start runs once, when the script comes [br]into existence. Code in update runs continuously, 0:15:56.280,0:16:01.920 every single frame. But, we can use if statements [br]to skip some code, unless a condition is met. 0:16:01.920,0:16:04.740 And we can use public variables to change certain 0:16:04.740,0:16:07.860 values in Unity's inspector - [br]even while the game is running. 0:16:09.480,0:16:13.680 Okay, so the secret to Flappy Bird [br]is that while it looks like a bird 0:16:13.680,0:16:18.180 is flapping along through a world of [br]pipes - it's actually not. The bird 0:16:18.180,0:16:23.640 stays completely still and the pipes move [br]across the screen. So in step three we're 0:16:23.640,0:16:28.620 going to make pipes spawn into the world, move [br]across the screen, and then delete themselves. 0:16:28.620,0:16:32.280 We'll start by making the object [br]we want to spawn. This will be 0:16:32.280,0:16:35.100 two pipes which move across the [br]screen, from the left to right. 0:16:35.100,0:16:40.500 Let's make another GameObject called [br]pipe. Put it exactly on the bird for now, 0:16:40.500,0:16:44.520 to get the sizing right. And then we'll [br]make another object within this one, 0:16:44.520,0:16:50.700 called top pipe. This is a child of the [br]first GameObject's parent. This way we 0:16:50.700,0:16:55.080 can nest multiple GameObjects, and move all [br]of them at once just by moving the parent. 0:16:55.980,0:17:00.000 So let's repeat what we did for the bird. [br]Add a sprite renderer for the pipe image. 0:17:00.900,0:17:06.840 And add a collider - a Box Collider 2D, this time. [br]We don't need a RigidBody because it's not going 0:17:06.840,0:17:13.440 to be affected by physics. We can then move it up [br]above the bird - but keep the X position as zero. 0:17:13.440,0:17:19.020 Finally, we can duplicate this whole top [br]pipe object. Call it bottom pipe. And flip 0:17:19.020,0:17:24.420 it upside down by changing the Y scale to [br]minus one. Then move it down below the bird. 0:17:24.420,0:17:31.020 As you can see, if we mess with the pipe [br]parent GameObject, both pipes move, scale, 0:17:31.020,0:17:35.400 and rotate along with it, with the [br]parent as the pivot point. So let's 0:17:35.400,0:17:38.940 add a script to this parent's object [br]to make it move across the screen. 0:17:40.680,0:17:46.080 We'll start by creating a variable for [br]moveSpeed. If we give it a number here, 0:17:46.080,0:17:50.700 it will fill this as the default value in [br]Unity. But we can always change it there, later. 0:17:50.700,0:17:56.100 Then we'll write code to move the object, in [br]update. Now it would be lovely if we could just 0:17:56.100,0:18:03.360 type transform.position.x, and change this number [br]directly - but, no, boo, you have to change the 0:18:03.360,0:18:08.580 entire Vector in one go. Oh, and this time we're [br]gonna have to use Vector3, instead of Vector2, 0:18:08.580,0:18:14.760 because the transform has three numbers. Even [br]though we're making our game in 2D, Unity is still 0:18:14.760,0:18:19.980 fundamentally a 3D engine and so it's keeping [br]track of the object's depth with the Z value. 0:18:20.580,0:18:26.160 So, here's what we'll do. We'll take the [br]current transform.position. And then equals. 0:18:26.160,0:18:31.200 We want to add to its current position, [br]so write transform.position again. And 0:18:31.200,0:18:37.320 then plus. And finally, in brackets, [br]we'll do Vector3.left * moveSpeed. 0:18:39.000,0:18:43.500 Back in Unity, press play and [br]vroooof. That's way too fast. Now, 0:18:43.500,0:18:47.520 you might think that you could just change [br]this moveSpeed variable down to a really 0:18:47.520,0:18:53.280 small number like 0.001. And that will work [br]- but that's not actually the problem here. 0:18:53.280,0:18:58.860 You see, code in update just runs as often as it [br]can. In fact, if we check the stats in Game view, 0:18:58.860,0:19:03.060 we'll see the game is running at [br]over 1,000 frames per second. Heh, 0:19:03.060,0:19:07.680 sorry PlayStation 5. 120 fps? Pfft, [br]that's got nothing on Flappy Bird. 0:19:07.680,0:19:11.880 And the real problem is that the game may run [br]at different speeds on different computers, 0:19:11.880,0:19:16.440 and we don't want the pipe to move [br]faster or slower depending on your rig. 0:19:16.440,0:19:22.680 Real games have actually made this mistake - in [br]Dark Souls 2, weapon durability was once tied 0:19:22.680,0:19:30.660 to frame rate, so your swords would break twice [br]as fast at 60 FPS, compared to 30 FPS. That was a whoopsie. 0:19:30.660,0:19:36.420 Luckily, it's a pretty easy fix. We [br]just multiply it by Time.deltaTime. 0:19:36.420,0:19:41.280 This ensures the multiplication happens the [br]same, no matter the frame rate. We didn't 0:19:41.280,0:19:44.820 need it for the velocity code because [br]physics runs on its own little clock, 0:19:44.820,0:19:49.320 but otherwise we will need it. if you want to [br]know more - about this, or anything really, 0:19:49.320,0:19:53.580 the Unity docs are a good place to [br]check. You'll find info and sample code. 0:19:53.580,0:19:59.400 Okay, now with that fix in place, our pipe [br]moves smoothly across the screen. Lovely. 0:19:59.400,0:20:04.560 Next, we want to create a system that will [br]continually spawn new pipes. To start, 0:20:04.560,0:20:10.740 take the parent GameObject from the hierarchy [br]and drag it into your project. This creates a 0:20:10.740,0:20:16.980 prefabricated GameObject. Or prefab. This is like [br]a blueprint for a GameObject and we can create new 0:20:16.980,0:20:22.500 versions of this entire GameObject- with all [br]its children, components, and properties. Oh, 0:20:22.500,0:20:26.460 and before we move on, we can delete the [br]original in our hierarchy now. Bye bye. 0:20:26.460,0:20:29.460 Let's make a new GameObject called Pipe Spawner. 0:20:30.720,0:20:36.240 We'll put it just to the right of the camera. And [br]we'll make a script for it. The purpose of this 0:20:36.240,0:20:41.820 script is to spawn new versions of the pipe prefab [br]every few seconds. And because the pipe already 0:20:41.820,0:20:46.800 has code to move left, the pipe will automatically [br]move across the screen as soon as it spawns in. 0:20:47.400,0:20:51.360 We're going to write some code to [br]spawn that prefab we just made. So 0:20:51.360,0:20:54.276 we'll start by making a reference [br]to the prefab. 0:20:54.276,0:20:58.680 Up here, we'll type Public GameObject pipe. 0:20:58.680,0:21:03.840 Then in Unity, we'll use the same drag and [br]drop method to fill the slot, but this time, 0:21:03.840,0:21:07.920 instead of a component, we'll drag [br]the prefab from the project panel. 0:21:07.920,0:21:11.580 Now,[br]Unity has a nice built-in method for spawning 0:21:11.580,0:21:17.880 new GameObjects. We'll type Instantiate, and then [br]open the brackets. In here, the command is asking 0:21:17.880,0:21:22.980 for some extra details. we can actually flip [br]through these to find different, I dunno, recipes? 0:21:22.980,0:21:28.500 I guess? Number 4 looks good - it will create [br]an object at a specified position and rotation. 0:21:28.500,0:21:32.760 So, for the GameObject, we can [br]type pipe. For position 0:21:32.760,0:21:37.560 we can just type transform.position to [br]get the position of the object holding 0:21:37.560,0:21:42.180 this script. That will make it spawn on [br]top of the spawner. And for rotation, 0:21:42.180,0:21:46.800 let's just use transform.rotation so, [br]again, it's the same as the spawner. 0:21:48.300,0:21:52.740 Let's run it and oh my god, that's not [br]what we want. Spawning works great, 0:21:52.740,0:21:55.560 but they're coming out every single [br]frame - and we want them to come 0:21:55.560,0:21:59.760 out on a nice interval that we can [br]control. So, back to Visual Studio. 0:21:59.760,0:22:05.100 What we're going to do now is to write some [br]code to make a timer. this will count up for 0:22:05.100,0:22:10.140 a specified number of seconds, run some code, [br]and then start the count again. To do this, 0:22:10.140,0:22:14.520 we'll need to make a couple variables. A [br]spawnRate is how many seconds it should be 0:22:14.520,0:22:19.560 between spawns. And then a timer is the [br]number that counts up. We can make this 0:22:19.560,0:22:22.980 one private as we won't be changing [br]it in the editor or anywhere else. 0:22:22.980,0:22:29.400 In update, we'll do another if statement. This [br]time, if the timer is less than the spawnRate, 0:22:29.400,0:22:34.980 then we want to make the timer count up by one. [br]So we'll take the timer as it currently is, 0:22:34.980,0:22:39.780 and add Time.deltaTime to it. This creates [br]a number that counts up every frame, 0:22:39.780,0:22:42.600 and works the same no matter what [br]your computer's frame rate is. 0:22:42.600,0:22:48.480 We can actually shorten this by changing it to +=, [br]but, don't feel like you need to make your code as 0:22:48.480,0:22:53.040 short as humanly possible just to avoid [br]getting sniffy YouTube comments. If 0:22:53.040,0:22:58.440 timer = timer + is easier to read and [br]grasp, then that's absolutely fine. You 0:22:58.440,0:23:01.740 can always swap to the other version in [br]the future when you feel more confident. 0:23:01.740,0:23:05.220 Now, before I said an if statement is like a gate. 0:23:05.220,0:23:09.540 And we can add another gate to the [br]side of it, with else. This means, 0:23:09.540,0:23:14.520 if the condition isn't met, then skip the [br]code - and do the code in else, instead. 0:23:14.520,0:23:22.560 So we'll put the spawn code in here, and also [br]reset the timer to zero. So now, every frame, it 0:23:22.560,0:23:28.500 asks if the timer is less than the spawn rate. If [br]it is, then count the timer up. If it's not - i.e. 0:23:28.500,0:23:33.420 the timer has actually met or exceeded the spawn [br]rate, then spawn a pipe and start the timer again. 0:23:33.420,0:23:38.820 Put this in Unity and - pretty good. I'm happy [br]with that. The only problem is... we have to 0:23:38.820,0:23:44.100 wait ages for the first pipe to spawn. It would [br]be good if this came out immediately, right? 0:23:44.100,0:23:49.560 Now, we could copy and paste the spawn code [br]into start, so it happens once in start. 0:23:49.560,0:23:55.320 And then happens over and over in update. But [br]that's a bad idea. You should generally try to 0:23:55.320,0:24:00.900 avoid having the same, or even similar code [br]in multiple places. What happens if we want 0:24:00.900,0:24:06.120 to change how the spawn works? We'll have [br]to find and change it everywhere. No good. 0:24:06.120,0:24:10.260 Instead, we can put the spawn code in [br]a new function, and then just run that 0:24:10.260,0:24:15.180 function. So here, below update - but [br]above the final curly bracket - we'll 0:24:15.180,0:24:21.420 make a function called void spawnPipe(). And then [br]cut and paste the Instantiate code into there. 0:24:21.420,0:24:27.720 Now we can just write spawnPipe, with empty [br]brackets, in both update and start. This will run 0:24:27.720,0:24:32.340 all the code in that function when these lines [br]are executed. And with that done, it will make 0:24:32.340,0:24:37.800 a pipe as soon as the game begins, and will make [br]new pipes every time the timer maxes out. Perfect. 0:24:38.760,0:24:44.700 However - this is a pretty boring game, right? The [br]pipes always come out in the middle. we want them 0:24:44.700,0:24:49.080 to come out at random heights. So, remember [br]that when we wrote the instantiate code, 0:24:49.080,0:24:52.980 we had to pick a position for the object [br]to appear? We'll change that value. 0:24:52.980,0:24:58.140 Right now the pipes always spawn on the same [br]position as the spawner. We want the X value 0:24:58.140,0:25:03.960 to be the same... but for Y, we want to pick a [br]random point somewhere above or below the spawner. 0:25:03.960,0:25:08.520 So let's create a public variable [br]for a heightOffset, maybe 10. 0:25:08.520,0:25:14.220 And then we'll make a float called lowestPoint. [br]Because we're making this variable inside the 0:25:14.220,0:25:19.800 function, rather than at the top of the script, it means [br]it can only be used within the function. But, 0:25:19.800,0:25:22.200 also, it means we can set [br]it by doing a calculation. 0:25:22.200,0:25:29.820 so we'll do equals transform.position.y - [br]heightOffset. And then we'll make another 0:25:29.820,0:25:36.600 one for highestPoint, but this time it's plus [br]heightOffset. That gets us these two numbers. 0:25:37.560,0:25:41.400 Then we'll replace the transform.position [br]in our Instantiate code. 0:25:41.400,0:25:47.400 We're gonna write new Vector3, we have to write that [br]whenever we're specifying our own numbers for a 0:25:47.400,0:25:53.700 vector. and then in brackets we'll specify the X, [br]Y, and Z values as three different floats. For X, 0:25:53.700,0:25:59.160 we want this to be the same as the spawner, [br]so we'll do transform.position.x. But for Y, 0:25:59.160,0:26:05.160 we can do Random.Range. And in the brackets for [br]that, we can supply a minimum and maximum point 0:26:05.160,0:26:12.300 to pick from. That's lowestPoint and highestPoint. [br]Then a 0 for Z. And close the brackets. 0:26:14.220,0:26:19.560 Back in Unity.... nice! The pipes will [br]spawn anywhere between these two numbers. 0:26:19.560,0:26:24.780 Oh, one last thing. Every time these [br]pipes spawn they'll appear and move 0:26:24.780,0:26:30.120 left.... forever. Which isn't great practice - [br]they're off screen and doing absolutely nothing, 0:26:30.120,0:26:35.100 and yet they're still in memory and running code [br]every frame. And if too many spawn they'll start 0:26:35.100,0:26:40.140 to spill out the side of your monitor and [br]make a right mess of your desk. So let's fix that. 0:26:40.140,0:26:45.060 Now we could make a timer, and delete the [br]pipe after a few seconds. But instead, 0:26:45.060,0:26:50.700 we'll check the X position of the pipe, and delete [br]it if it goes past a certain point. We'll borrow 0:26:50.700,0:26:58.260 the bird to find out the X coordinate of the [br]left of the screen. Looks about minus 45. In 0:26:58.260,0:27:06.120 the pipe move script, we'll add a float for a [br]deadzone. -45. And then a simple if statement - if 0:27:06.120,0:27:13.020 transform.position.x is less than deadZone, then [br]destroy the GameObject that holds this script. 0:27:15.720,0:27:18.840 Run it in Unity and, bam, they're dead. 0:27:18.840,0:27:24.420 Let's do one more thing, just as a teachable [br]moment. Just before the destroy line, 0:27:24.420,0:27:32.880 let's write Debug.Log, and in brackets, Pipe Deleted. [br]Then, back in Unity, you'll see one other panel 0:27:32.880,0:27:39.420 I skipped during the UI demo - it's a tab next [br]to project, called console. Then when we run the 0:27:39.420,0:27:44.820 game... every time a pipe is deleted, our [br]message is sent to the console. This is a 0:27:44.820,0:27:50.280 wonderfully useful way to debug our code, because [br]we can find out exactly what the code is up to. 0:27:51.900,0:27:52.800 Recap time! 0:27:52.800,0:27:55.560 GameObjects can be turned into prefabs, 0:27:55.560,0:27:58.980 by dragging them from the hierarchy, [br]and dropping them into the project. 0:27:58.980,0:28:04.020 You can then drag these into scenes - I use [br]prefabs to create levels in my puzzle game, 0:28:04.020,0:28:08.940 for example. Or you can make a spawner [br]to instantiate new ones during the game. 0:28:08.940,0:28:12.660 Timers are a great way to make [br]code happen on a certain interval, 0:28:12.660,0:28:17.400 but always use Time.deltaTime to keep things [br]consistent across different computers. 0:28:17.400,0:28:21.540 If statements can have an else gate, [br]to make code fire if the condition 0:28:21.540,0:28:26.520 is not met. You can also have else [br]if, to make more complicated gates. 0:28:26.520,0:28:30.960 And you should try to delete GameObjects if [br]they're no longer needed, to free up memory. 0:28:32.400,0:28:35.820 Okay, our next step is to keep [br]track of the player's score, 0:28:35.820,0:28:39.360 and show it to the player [br]on the user interface. Then, 0:28:39.360,0:28:43.380 we want the score to go up by one, every [br]time the bird goes through the pipes. 0:28:43.380,0:28:47.460 So, remember that a GameObject doesn't [br]have to be a physical thing in your game 0:28:47.460,0:28:52.560 world like a character or an enemy - it [br]can be a completely invisible manager 0:28:52.560,0:28:57.540 that's just keeping track of critical data [br]like health, or time, or score. And then, 0:28:57.540,0:29:01.260 we can make that information visible [br]to the player, using a user interface. 0:29:01.260,0:29:06.600 So let's start by making the UI. Like [br]everything else, it's a GameObject in 0:29:06.600,0:29:11.520 the hierarchy. This time go down to [br]UI and pick text - which may be under 0:29:11.520,0:29:16.680 legacy. We'll need to zoom really far out [br]on the scene view to actually see the UI. 0:29:16.680,0:29:21.660 To make sure the UI looks the same on every [br]device, we'll pick this new canvas GameObject 0:29:21.660,0:29:28.260 and set the canvas scaler component's UI scale [br]to scale with screen size, and choose a sensible 0:29:28.260,0:29:34.200 reference resolution - I'm gonna use 1080p again. [br]We can then move our text around. You'll notice 0:29:34.200,0:29:40.140 that UI has a rect transform, rather than a normal [br]transform. The most important thing to note is 0:29:40.140,0:29:44.700 that you don't really want to mess with scale of [br]elements - instead, change the width and height. 0:29:46.620,0:29:49.080 I'll then increase the font size and set the default 0:29:49.080,0:29:53.280 text to 0. And then check it [br]all looks nice on the game view. 0:29:53.280,0:29:56.640 Okay, now we want to make a script [br]that will store the player's score, 0:29:56.640,0:29:59.820 and change the number on the UI to that score. 0:29:59.820,0:30:02.940 We'll make a GameObject called Logic Manager. 0:30:03.960,0:30:08.820 And we'll give it a script. This script is [br]going to keep track of high level information 0:30:08.820,0:30:13.860 like the player's score. And it will have [br]various meta-level functions that we can run. 0:30:13.860,0:30:18.180 So we'll delete start and update, we don't [br]need them in this script. We can always add 0:30:18.180,0:30:22.740 them back later if we change our mind. We want to [br]store a number for the player's score. This time, 0:30:22.740,0:30:27.600 we don't want a float because we only ever [br]want round numbers. So let's do an int, 0:30:27.600,0:30:30.360 instead. That's an integer. No decimal places. 0:30:30.360,0:30:34.980 And because we want to update the UI [br]text we just made we will, as always, 0:30:34.980,0:30:40.680 have to make a reference. Except... [br]text doesn't seem to be a thing? 0:30:40.680,0:30:46.320 Ah, well. By default, a script only loads [br]in the stuff you need for basic Unity 0:30:46.320,0:30:52.500 functionality - but if we go up to the top [br]and type using UnityEngine.UI;, we can now 0:30:52.500,0:30:58.680 access more functionality - in this case, UI [br]stuff. Now we can make a reference to text. 0:30:59.280,0:31:04.680 We'll need to drag the text component into this [br]field back in Unity. Because we're referencing a component 0:31:04.680,0:31:09.540 on another GameObject - the text on the UI [br]- the best way to do this is to just drag 0:31:09.540,0:31:16.020 the whole GameObject into our slot. This will [br]automatically find the text component for us. Handy. 0:31:16.020,0:31:20.460 So now we want to make a function. And [br]we'll call it addScore. And because 0:31:20.460,0:31:25.009 we're going to run this function from [br]other scripts, we'll set it to public void. 0:31:25.740,0:31:30.840 This function needs to do two things. Add [br]one to the player's score. Easy enough, 0:31:30.840,0:31:39.240 we know how to do that now. And change the text [br]on the UI to be this number. Oh, the text box is 0:31:39.240,0:31:45.720 looking for a string - a sequences of characters [br]- and our score is an integer. They look identical 0:31:45.720,0:31:51.720 to us humans, but robots are fussy. Easily fixed, [br]mind you, by adding .toString() to the game score. 0:31:52.800,0:31:56.580 To make sure this works, let's give [br]ourselves the power to run this function 0:31:56.580,0:32:02.820 from Unity itself. All we need to do is write [br]ContextMenu, and a name, above the function. 0:32:05.340,0:32:10.140 Now, in Unity, while the game is running, hit the [br]little dots on this script and pick the function. 0:32:10.800,0:32:14.460 Nice! This sort of thing comes [br]in real handy for testing. 0:32:15.060,0:32:18.660 Okay, so now that we know the function [br]runs, we specifically want to run it 0:32:18.660,0:32:23.220 when the bird goes between the pipes. [br]And the way to do this is collisions. 0:32:23.220,0:32:28.380 Now if two objects have colliders, they will [br]bash into each other - in fact, in our game, 0:32:28.380,0:32:33.060 the bird will already crash into the pipes because [br]we've added colliders to both. However - you 0:32:33.060,0:32:37.740 can also have invisible colliders, called [br]triggers. They don't create an actual collision, 0:32:37.740,0:32:42.000 but they do let you know that two objects have [br]touched - and you can run code at that moment. 0:32:42.000,0:32:45.180 So we're going to put a [br]trigger in between the pipes, 0:32:45.180,0:32:50.700 so we know that the bird has passed through them.[br]And then at that moment, we'll run addScore. 0:32:50.700,0:32:55.920 Let's open up the prefab for the pipes. We'll [br]make another GameObject called middle - and 0:32:55.920,0:33:00.900 it needs a box collider. Let's make it [br]this sort of shape. And this time we'll 0:33:00.900,0:33:06.420 tick the box isTrigger. Finally, let's add [br]a script to this new middle GameObject. 0:33:06.420,0:33:13.920 Beneath Update, type ontrig, and the autocorrect [br]will help us type out OnTriggerEnter2D. Just 0:33:13.920,0:33:18.960 press tab to autofill. Anything in this [br]function will run whenever an object first 0:33:18.960,0:33:25.620 hits the trigger. There's also OnTriggerExit and [br]OnTriggerStay, for future reference. And its in here, 0:33:25.620,0:33:30.060 that we want to run the addscore function we [br]wrote earlier... except. ah. once again, 0:33:30.060,0:33:34.440 this script doesn't know about any other scripts [br]in the game, until we make a reference to it. 0:33:34.440,0:33:41.340 So we can write public LogicScript logic. [br]But back in Unity, you'll quickly realise 0:33:41.340,0:33:45.840 that you can't drag the script into this [br]slot. You can't drag it from the project 0:33:45.840,0:33:51.060 panel - we can only talk to an instance of a [br]script that lives on a GameObject. But we also 0:33:51.060,0:33:56.880 can't drag from the scene into the prefab. That's [br]because the pipe doesn't exist in the scene yet, 0:33:56.880,0:34:01.320 it will only exist when the game is running, [br]and the spawner starts making pipes. 0:34:01.320,0:34:05.520 So, instead, we'll need to fill [br]this reference using code. and 0:34:05.520,0:34:08.340 this needs to happen when the pipe first spawns. 0:34:08.340,0:34:12.720 To do this, we'll need to help the [br]code find the logic script. To do this, 0:34:12.720,0:34:16.800 take the Game Logic object, and [br]look at the top of the inspector: 0:34:16.800,0:34:23.820 you'll see tags. From the drop down, [br]choose add tag. Make a new tag called, say, 0:34:23.820,0:34:30.060 Logic. And make sure you go back to the GameObject [br]and actually set this new tag. You will forget to 0:34:30.060,0:34:34.680 do this approximately eight thousand times in [br]your Unity career, so look forward to that. 0:34:34.680,0:34:37.380 Now, back in the PipeMiddleScript, 0:34:37.380,0:34:46.740 under start we can write logic = [br]GameObject.FindGameObjectWithTag("Logic"). 0:34:46.740,0:34:50.820 this will look for the first GameObject [br]in the hierarchy with the tag, 0:34:50.820,0:34:54.420 Logic. In our case, there will [br]only ever be one in the scene, 0:34:54.420,0:34:57.900 so we know it will always find the [br]right one - but do be mindful of that. 0:34:57.900,0:35:02.280 And then we can add .GetComponent<LogicScript>(); 0:35:03.480,0:35:09.300 So, as soon as a new pipe spawns, it will look [br]through the hierarchy to find a GameObject with 0:35:09.300,0:35:13.800 the tag Logic. Then, it will look through [br]that object's components to find a script 0:35:13.800,0:35:19.020 of the class LogicScript. And if it finds [br]one, it will put that in our reference slot. 0:35:19.020,0:35:22.740 It has done the exact same thing as [br]dragging and dropping the component 0:35:22.740,0:35:28.200 in the Unity editor - except it has done [br]it instantly, during run time. Excellent. 0:35:28.860,0:35:33.960 So now, the pipe's middle script can [br]find and talk to the logic script. 0:35:35.400,0:35:41.340 And if we write logic.addScore, this [br]will run that code. Back in Unity, 0:35:41.340,0:35:47.340 hit play and if we did everything right, the score [br]will go up by one when we pass between the pipes. 0:35:47.340,0:35:52.860 Oh, and just for future proofing and whatnot, [br]let's make sure that it was actually the bird 0:35:52.860,0:35:57.660 that went through. We'll do this by putting the [br]bird on a layer, and checking if the colliding 0:35:57.660,0:36:03.900 object was on that layer. Go to the bird's GameObject [br]and this time, instead of the tag, we'll change 0:36:03.900,0:36:10.620 the bird's layer. Make a new one, remember to [br]actually assign it, and make a note of the number. 0:36:10.620,0:36:15.480 Now, on the pipe's middle script,[br]we can add an if statement around addScore, 0:36:15.480,0:36:20.400 and check if the collision that just happened [br]was with a GameObject on the bird's layer. 0:36:22.560,0:36:27.494 One more bit of future proofing, while we're on [br]the subject. Go back to the Logic Script. 0:36:27.494,0:36:33.420 And, let's take the AddScore function, and in these empty brackets we'll write int 0:36:33.420,0:36:38.520 scoreToAdd. And then instead of [br]adding one, we'll add scoreToAdd. 0:36:39.240,0:36:45.360 Then in the pipe middle script, we can write [br]a 1 in the brackets after addScore. Right 0:36:45.360,0:36:50.160 now this does exactly the same thing as we [br]had before. But, as you can surely guess, 0:36:50.160,0:36:54.900 you could later add some other goal [br]in the game that adds, say, 5 to your score. 0:36:54.900,0:36:58.980 This allows us to make a function [br]more versatile, as it can be used 0:36:58.980,0:37:03.120 in different ways, from different places. [br]Part of being a good programmer, I think, 0:37:03.120,0:37:07.440 is making stuff less rigid, and keeping it open for [br]future ideas. 0:37:07.440,0:37:12.660 This makes it easier and faster [br]to iterate on your designs. 0:37:12.660,0:37:14.040 Right! Recap! 0:37:14.040,0:37:17.880 UI is just another GameObject, [br]but if we want to reference any 0:37:17.880,0:37:22.920 of these components we'll need to add [br]using UnityEngine.UI to the top of the script. 0:37:22.920,0:37:25.680 GameObjects can be completely invisible things, 0:37:25.680,0:37:29.760 merely there to keep track of rules, [br]logic, score, and so on. 0:37:29.760,0:37:34.080 If we want to a reference a component [br]when one of the GameObjects is not in the scene, 0:37:34.080,0:37:36.660 we'll need to find that component during run time. 0:37:36.660,0:37:42.300 One way to do this is to use tags, [br]findGameObject, and GetComponent. 0:37:42.300,0:37:46.740 A public function can be run from another [br]script, as long as you have a reference 0:37:46.740,0:37:50.940 to that script. And we can even pass [br]in variables when that function runs. 0:37:50.940,0:37:56.580 And Collisions and triggers can be used to make [br]stuff happen when two objects touch. 0:37:56.580,0:37:59.400 Speaking of collisions, let's [br]move on to the next step... 0:38:01.020,0:38:06.900 The final step is to add a fail state. When [br]the bird hits the pipes, the game is over. 0:38:06.900,0:38:11.340 We'll do this by making a game over screen, [br]and have it appear when the bird crashes 0:38:11.340,0:38:16.320 into a pipe. The game over screen will have [br]a button, which we can use to reset the game. 0:38:16.980,0:38:21.060 First, let's make that game over [br]screen. On the canvas GameObject, 0:38:21.060,0:38:28.440 add a new empty one called game over screen. [br]Then, in that parent, add a text for game over. 0:38:30.960,0:38:36.900 And also a a button - that's also [br]under legacy. Resize it. And change 0:38:36.900,0:38:41.580 the text on the button - the text can be [br]found as a child on the button itself. 0:38:43.440,0:38:48.000 So back on the button GameObject, [br]on the button component, you'll see 0:38:48.000,0:38:53.520 this bit that says On Click. This is [br]an event, and it allows us to call a 0:38:53.520,0:38:58.680 public function on a GameObject. So let's [br]make a function for restarting the level. 0:38:59.520,0:39:02.100 We can put this code in the logic script, 0:39:02.100,0:39:07.080 underneath our addScore function. You [br]could make a seperate script if you want, 0:39:07.080,0:39:12.060 but I think this is fine. Let's make [br]another public function called restartGame, 0:39:12.060,0:39:17.580 and in here we'll write code to restart the [br]scene. Just like before with the UI, if we're 0:39:17.580,0:39:22.560 managing scenes then we'll need to add a line the [br]top - this time, using UnityEngine.SceneManagment. 0:39:23.940,0:39:30.180 Now in our function, we'll call up the [br]SceneManager and then, dot, LoadScene. This 0:39:30.180,0:39:35.460 is looking for the name of a scene. Literally the [br]filename. But because we want the current scene we 0:39:35.460,0:39:42.720 can simply type SceneManager dot GetActiveScene, [br]brackets, dot name. Close off all the brackets. 0:39:44.580,0:39:48.000 Now back in Unity, add an event to this button. 0:39:48.960,0:39:53.340 Then drag in the logic GameObject. [br]and find the restartGame function. 0:39:57.300,0:40:02.580 Give it a test and... nice. Every time we [br]press the button, the game begins anew. 0:40:02.580,0:40:07.560 Now obviously we don't want this to be on the screen [br]all the time - just when we fail. So, 0:40:07.560,0:40:12.000 we can just take the whole game over screen [br]GameObject and disable it with this checkmark. 0:40:13.380,0:40:16.260 Then we'll make it show up when [br]the bird hits into the pipes. 0:40:16.260,0:40:22.620 Let's write the function first. Again in the [br]logic script, let's make a public function for gameOver. 0:40:22.620,0:40:27.750 We'll need to make a reference to the game over screen GameObject. 0:40:27.900,0:40:30.116 And fill it in Unity. 0:40:31.467,0:40:38.611 And then we can simply type [br]gameoverscreen.SetActive true in this function. 0:40:38.820,0:40:42.300 So we want this function to trigger [br]when the bird crashes into a pipe. 0:40:42.300,0:40:48.180 Back on the bird script, let's reuse that [br]code from before to access the logic script 0:40:48.180,0:40:52.680 from the bird script. Yes, we could drag [br]and drop the reference in Unity, but hey, 0:40:52.680,0:40:57.720 we've written this code now. And then we're going to do [br]a similar thing to the trigger code, but this time 0:40:57.720,0:41:03.360 we'll use OnCollisionEnter2D, because the pipes [br]are solid objects, and not set to be triggers. 0:41:03.360,0:41:10.277 And when that collision occurs, trigger [br]the game over script with logic.gameOver. 0:41:10.277,0:41:16.440 Back in Unity... it kind of works, but we can [br]still play in the game over screen. Not ideal. 0:41:16.440,0:41:21.420 So, I've talked about a few key [br]variable types, already. Floats 0:41:21.420,0:41:28.380 and ints are numbers. And string is usually [br]for text. The other important one is a bool, 0:41:28.380,0:41:34.320 short for boolean. This is a really simple [br]type that is either true, or false. On, 0:41:34.320,0:41:38.340 or off. Yes, or no. It's a great way to [br]simply check or change something's state. 0:41:38.940,0:41:44.160 So let's have a bool called birdisalive, [br]and make sure it starts as true. 0:41:44.160,0:41:49.080 Then when the collision happens, [br]we'll set birdisalive to false. 0:41:49.080,0:41:54.480 And finally, we'll add an extra condition to [br]our very first if statement. We're going to 0:41:54.480,0:42:01.200 say if the space bar has just been pressed and... [br]written with two ampersands... and birdisalive is 0:42:01.200,0:42:06.360 equal to true. Actually, we don't need to [br]add this equals equals true thing. It does the exact 0:42:06.360,0:42:11.400 same thing without it. But, again, it's up to you [br]- maybe it's easier to read this with the full code written out. 0:42:11.400,0:42:17.040 Anyway, now, the bird won't flap if it's [br]dead, which seems quite logical to me. 0:42:17.040,0:42:24.360 The final thing to do is to build the game. Which is really easy.[br]Pick file, build settings, and build. Pick a folder on 0:42:24.360,0:42:31.500 your hard drive. And let Unity do its work. Then you [br]can open this file to play your game! Amazing. 0:42:31.500,0:42:35.400 In a very short period of time, we [br]have made a pretty functional game. 0:42:35.400,0:42:39.960 And what’s more, we’ve learned loads [br]of fundamental lessons about Unity. 0:42:39.960,0:42:43.980 We have made a character that moves [br]in response to our input. We have 0:42:43.980,0:42:48.660 spawned in new objects on a timer. We [br]have created a UI that shows a score, 0:42:48.660,0:42:53.700 and made that score tick up when conditions are [br]met. And we've got the ability to get a game over, 0:42:53.700,0:42:55.573 and start again. 0:42:55.573,0:43:00.060 Now, I should note that there are different - and [br]perhaps better ways to do pretty much everything 0:43:00.060,0:43:05.220 in this tutorial. For example - I used Unity's [br]old way of checking for inputs, and the company 0:43:05.220,0:43:10.440 has since developed a much, much better Input [br]System. But it's a lot more complicated to 0:43:10.440,0:43:15.660 use - so this simple method is great for now, and [br]you can look into the new input system later down 0:43:15.660,0:43:21.420 the line, when you feel more confident. That's how [br]it went for me. There's also TextMeshPro, which 0:43:21.420,0:43:27.559 has replaced the old legacy UI system - so you'll [br]want to graduate to that, at some point, as well. 0:43:27.559,0:43:31.674 Anyway, these are lessons that will be useful,[br]for making all sorts of games. 0:43:31.674,0:43:37.260 But... the game isn't quite finished yet. [br]There's still a few more things to figure out. Though, 0:43:37.260,0:43:41.160 I don't want to tell you how to do everything. [br]So i'm gonna give you some suggestions for how 0:43:41.160,0:43:43.860 to finish up the game, but I want you [br]to try and figure it out for yourself. 0:43:43.860,0:43:48.360 So first of all, we need to have a game [br]over if the bird goes off the screen. 0:43:48.360,0:43:51.900 That shouldn't be too hard. There's [br]also a bug where the score can go up, 0:43:51.900,0:43:55.020 even after a game over. Try to solve that one too. 0:43:55.020,0:44:00.300 We also want sound effects. I want you to add [br]an Audio Source component to the logic manager. 0:44:00.300,0:44:05.700 fill it with a sound effect file. Reference it on the [br]script. And have it play when the score goes up. 0:44:05.700,0:44:11.220 Then, i want you to play around with the particle [br]system to make clouds appear in the game. 0:44:11.220,0:44:15.600 Next, open the animation window, and [br]add some flapping wings to the bird. 0:44:15.600,0:44:19.320 Then i want you to add another [br]scene to make a title screen, 0:44:19.320,0:44:22.800 so the game doesn't immediately [br]launch into the action. Here's a clue: 0:44:22.800,0:44:25.740 you'll need to add this new scene [br]to the build settings window. 0:44:25.740,0:44:30.540 And finally, if you want a real [br]challenge - use PlayerPrefs 0:44:30.540,0:44:35.400 to save the player's high score to the hard [br]drive, and draw that on the UI as well. 0:44:35.400,0:44:40.620 For each one of these, you will probably want to [br]Google the relevant terms, read the Unity docs, 0:44:40.620,0:44:45.120 watch some quick tutorial videos, or [br]ask for help in the comments down below. 0:44:45.120,0:44:48.840 Next, you could expand on [br]Flappy Bird. Get creative 0:44:48.840,0:44:53.280 and add in ideas or designs that weren't [br]there in the original iPhone game. 0:44:53.280,0:44:58.320 For example, with a little messing around I gave [br]the bird the ability to shoot out a missile, 0:44:58.320,0:45:03.480 and then I added targets to the pipes. You've [br]now got to hit the target with a missile to open 0:45:03.480,0:45:07.500 a gap you can flap through. It's pretty cool, [br]and adds a lot more depth to the simple game. 0:45:07.500,0:45:11.580 In fact, I'd love to see how you [br]might expand on the original game. If 0:45:11.580,0:45:15.060 you make something interesting, record [br]a bit of footage, pop it on YouTube, 0:45:15.060,0:45:19.200 and drop a link in the comments. I might [br]feature some of them in the future. 0:45:19.200,0:45:20.940 And then, finally, I'd recommend that you take 0:45:20.940,0:45:25.680 another simple game and try to [br]remake it in Unity, like we just did right now. 0:45:25.680,0:45:30.600 This is a great technique because you don't have [br]to worry about art or design... 0:45:30.600,0:45:35.223 just code. And the problem-solving puzzles you'll face are[br]a perfect example of 0:45:35.223,0:45:38.879 what real game development will be like. 0:45:38.879,0:45:46.366 Good candidates for this include Pong, Space [br]Invaders, Breakout, Pop the Lock, Angry Birds, 0:45:46.366,0:45:52.279 various WarioWare mini games, and that dinosaur game [br]that plays in Chrome if your internet's broken. 0:45:52.279,0:45:54.540 So, in this video I wanted to teach you the 0:45:54.540,0:45:58.680 fundamental concepts behind Unity [br]- but, the rest is up to you. 0:45:58.680,0:46:04.380 Luckily, I reckon this sort of hands-on, [br]self-directed, learn from your mistakes style of 0:46:04.380,0:46:10.380 learning is the most fun and effective way [br]to make stuff stick. But we’ll see! 0:46:10.380,0:46:14.040 Let me know how you got on in the [br]comments down below. And if you 0:46:14.040,0:46:17.460 want to watch my game development [br]story - which is still ongoing, 0:46:17.460,0:46:22.140 promise - then click here, [br]for episode one of Developing. 0:46:22.140,0:46:25.440 Thanks very much to my Patrons - they're [br]the reason you don't get mid-roll 0:46:25.440,0:46:30.540 ads in a looong video like this one. You [br]can help support GMTK at Patreon.com.