1 00:00:00,000 --> 00:00:05,940 Hi, my name is Mark. For years I've wanted to  make my very own video games, using software 2 00:00:05,940 --> 00:00:13,320 like Unity. Unity is the powerful game engine  behind titles like Cuphead, Neon White, Tunic, 3 00:00:13,320 --> 00:00:18,600 Outer Wilds, Hearthstone, Firewatch,  and even the Pokemon Diamond remake. 4 00:00:18,600 --> 00:00:26,280 But I've always found that lengthy, multi-part,  meandering tutorials just send me to sleep. I 5 00:00:26,280 --> 00:00:31,140 can't learn by watching someone else - I have to  get hands-on and figure things out for myself. 6 00:00:31,140 --> 00:00:37,500 And so last year I developed a solution that  actually works. It's a three-step technique 7 00:00:37,500 --> 00:00:44,820 where you: one just learn the absolute basics of  Unity. Then, two, cement those lessons with simple 8 00:00:44,820 --> 00:00:51,180 exercises. And then, three, figure out the rest as  you go along. And it totally worked! In the space 9 00:00:51,180 --> 00:00:57,000 of about a year, I went from ripping off iPhone  games to working on my very own puzzle platformer 10 00:00:57,000 --> 00:01:03,480 about magnets. And I released an interactive  video essay that's had over 100,000 plays. 11 00:01:03,480 --> 00:01:08,580 But wait, I hear you say! How do you do  step one? How do you learn the basics, 12 00:01:08,580 --> 00:01:11,580 when the software is so complicated to figure out? 13 00:01:11,580 --> 00:01:15,360 Well for me it was about writing down  a list of things I would need to know, 14 00:01:15,360 --> 00:01:19,980 regardless of what game I was going to make.  Things like how to make a character appear and 15 00:01:19,980 --> 00:01:24,060 move them around the screen. How to make  stuff spawn in and then delete it again 16 00:01:24,060 --> 00:01:28,680 later. How to have collisions and game  over and animations and sound effects. 17 00:01:28,680 --> 00:01:33,540 Then I learned all that by hunting through  lengthy tutorials, reading the Unity docs, 18 00:01:33,540 --> 00:01:36,780 Googling esoteric words, and doing a lot of trial 19 00:01:36,780 --> 00:01:41,820 and error. And so the whole point of  this video is to save you that hassle. 20 00:01:41,820 --> 00:01:46,380 This video is the tutorial I wish  I had when I was learning Unity. 21 00:01:46,380 --> 00:01:52,080 So in the next 40 minutes we're going to use the  engine to make Flappy Bird. Not because we want 22 00:01:52,080 --> 00:01:57,060 to make Flappy Bird, but because in order to  remake this addictive iPhone game, we'll need 23 00:01:57,060 --> 00:02:02,220 to learn basically everything I just listed,  from spawning objects to getting game overs. 24 00:02:02,220 --> 00:02:08,460 This tutorial will cover every step of the way  from downloading Unity, to understanding the UI, 25 00:02:08,460 --> 00:02:13,680 to writing your very first line of programming  code, to building a game that you can share with 26 00:02:13,680 --> 00:02:19,080 your friends. And then, when the tutorial is over,  I'll share some concrete next steps that you can 27 00:02:19,080 --> 00:02:25,920 take in order to continue learning the rest by  yourself. Sound good? Then let's get started. 28 00:02:26,880 --> 00:02:34,200 Okay, let's start by getting Unity from the  website. Download and install the Unity Hub. 29 00:02:34,800 --> 00:02:39,660 And then you'll need to make a free account  to actually use it. Once that's done, 30 00:02:39,660 --> 00:02:45,840 you'll be asked to install the Unity Editor  - I'm using version 2021.3 for this tutorial, 31 00:02:45,840 --> 00:02:49,020 if you're watching a million years  in the future and wondering why 32 00:02:49,020 --> 00:02:52,380 things are different. Let's pretend  I have fast internet - Neeooowwwwmm. 33 00:02:52,380 --> 00:03:00,300 We're not quite done yet. Under installs, hit the  cog icon on the Unity Editor and pick modules. 34 00:03:00,300 --> 00:03:04,980 You'll see that Microsoft Visual Studio has  been ticked - this is the software we'll use 35 00:03:04,980 --> 00:03:10,620 to write programming code. So hit continue.  And install Visual Studio. On this screen, 36 00:03:10,620 --> 00:03:15,420 scroll down and tick game development  with Unity, and untick Unity Hub, 37 00:03:15,420 --> 00:03:20,340 because we already have it. Neeooowwwwmm.  We don't need to make an account to use Visual Studio, 38 00:03:20,340 --> 00:03:23,580 so skip that. And don't bother loading  it, we'll open it later. 39 00:03:23,580 --> 00:03:31,140 Okay, that's all done now. So in Unity Hub, pick  new project. Choose all templates. And use 2D, 40 00:03:31,140 --> 00:03:35,940 Core. This is an empty project, with a  few configurations to make it suitable 41 00:03:35,940 --> 00:03:41,220 for 2D games. Give your project a name,  hit create, and let's get game makin'. 42 00:03:43,260 --> 00:03:48,720 In step one, we're going to become familiar  with the default Unity user interface. And 43 00:03:48,720 --> 00:03:52,500 as we explore the different panels,  we'll make the bird appear on screen. 44 00:03:52,500 --> 00:03:58,800 Right. So this is the default screen layout  for Unity, and it's split into four panels. 45 00:03:59,880 --> 00:04:05,340 First of all, down here, is the Project panel.  This will contain everything that is in our game 46 00:04:05,340 --> 00:04:12,660 - like sprites, sound effects, scripts, tiles,  fonts, and so on. Some of this stuff will be made 47 00:04:12,660 --> 00:04:17,940 in Unity as we go along. But we can also just drag  and drop files from elsewhere on our computer. 48 00:04:17,940 --> 00:04:22,800 Like, I've made some sprites for the bird  and the pipe in Photoshop and I'm going to 49 00:04:22,800 --> 00:04:27,540 import them into my project like so. I'd  recommend you make your own - that's always more 50 00:04:27,540 --> 00:04:32,400 fun - but if you have zero artistic ability  then check the description for these assets. 51 00:04:32,400 --> 00:04:38,940 The next panel is the hierarchy. This contains all  of the stuff that's in the current scene - which, 52 00:04:38,940 --> 00:04:42,960 in most games, will be a level. We're  going to start by making the bird, 53 00:04:42,960 --> 00:04:50,280 so right click and choose Create Empty. This  has made an empty GameObject... so what's that? 54 00:04:50,280 --> 00:04:56,700 Well, a GameObject is essentially an invisible  container. It has a position in space, a rotation, 55 00:04:56,700 --> 00:05:03,300 and a scale. Then, you can fill that container  with components - to add extra features. For 56 00:05:03,300 --> 00:05:08,220 example, if we add a Sprite Renderer component,  we can slap the bird image onto the GameObject. 57 00:05:08,220 --> 00:05:13,260 Absolutely everything in our level will be  a GameObject with components - the bird, 58 00:05:13,260 --> 00:05:16,440 the pipes, even the user interface and the camera. 59 00:05:16,440 --> 00:05:22,680 All of this magic happens in the third panel, the  Inspector - which is for messing with GameObjects. 60 00:05:22,680 --> 00:05:28,440 So, once we've selected our new, empty GameObject  we can put a name in the top field - let's call it 61 00:05:28,440 --> 00:05:34,860 Bird. And we can see and change the GameObject's  position, rotation, and scale, under Transform. 62 00:05:34,860 --> 00:05:41,520 We can now press Add Component, pick Rendering,  and pick Sprite Renderer. To make this work, 63 00:05:41,520 --> 00:05:45,540 we need to fill in the sprite field -  so just drag the bird image from the 64 00:05:45,540 --> 00:05:49,620 project panel into the field  and viola, we have graphics! 65 00:05:49,620 --> 00:05:53,280 That will, of course, show up  in the fourth and final panel, 66 00:05:53,280 --> 00:05:57,240 the scene view. Here we can see  what's in our current scene, and, 67 00:05:57,240 --> 00:06:01,560 if you want, you can use these tools to  move stuff around, scale it, and so on. 68 00:06:01,560 --> 00:06:06,900 This section has an extra tab for game view,  which shows us what the game will look like from 69 00:06:06,900 --> 00:06:13,440 the main camera when it's running. Also, from this  dropdown, we can set a resolution or aspect ratio 70 00:06:13,440 --> 00:06:19,080 to get a better idea of what it will look like  when played - so I'm going to choose 1920 by 1080. 71 00:06:19,080 --> 00:06:23,940 Oof, the bird takes up way too much space.  We could scale it down, but let's actually 72 00:06:23,940 --> 00:06:29,700 just zoom out the camera. Like I said before, the  camera itself is a GameObject in the hierarchy. 73 00:06:29,700 --> 00:06:35,880 And it has a camera component with stats we can  mess with. By changing the size, we can zoom out. 74 00:06:36,420 --> 00:06:39,420 I'm also going to change the  background colour. Lovely. 75 00:06:39,420 --> 00:06:44,340 We can now press the play button up  here to start... the world's most 76 00:06:44,340 --> 00:06:47,760 boring game. Okay, let's make it a bit more exciting. 77 00:06:49,320 --> 00:06:55,440 A quick recap. Unity has four panels by  default. Project holds all the stuff in our 78 00:06:55,440 --> 00:07:00,900 game. Hierarchy lists all of the GameObjects  in the current level. Inspector lets us see 79 00:07:00,900 --> 00:07:04,740 and change those GameObjects. And we  can see the level in the scene view. 80 00:07:04,740 --> 00:07:10,800 And a GameObject is an invisible container that we  can fill with components, like a sprite renderer. 81 00:07:11,580 --> 00:07:15,900 In step two we're going to use more  components to make the bird into a 82 00:07:15,900 --> 00:07:19,320 physics object that is affected by  gravity. And then we're going to 83 00:07:19,320 --> 00:07:23,400 write some programming code to make the  bird fly up when we press the space bar. 84 00:07:23,400 --> 00:07:29,760 So let's add another component to our bird:  a Rigidbody 2D. This turns our bird into a 85 00:07:29,760 --> 00:07:36,600 physics object, with gravity. So when we hit play,  the bird drops, and falls off the screen. Cool. 86 00:07:36,600 --> 00:07:42,279 We'll also want this bird to be able to interact  with other objects, so let's add a collider. 87 00:07:42,279 --> 00:07:43,920 A circle collider 2D. 88 00:07:43,920 --> 00:07:49,920 Back in scene view we can see the collider as  a green outline. It's a bit off-center for me, 89 00:07:49,920 --> 00:07:55,260 so I'll use the offset to move it. And, a  little game design trick - if we make the 90 00:07:55,260 --> 00:08:00,720 collider a bit smaller than the image, it will  let the player get through pipes even if they 91 00:08:00,720 --> 00:08:05,220 juuust touched the edge. It gives the game a  bit of leniency and makes it feel more fair. 92 00:08:05,220 --> 00:08:10,920 The final thing to add right now: a script.  This essentially lets us make our own custom 93 00:08:10,920 --> 00:08:16,680 component - but we'll have to write it ourself  using programming code. Choose New Script from 94 00:08:16,680 --> 00:08:21,780 the components list. And call it BirdScript.  Once it's loaded, double click the script 95 00:08:21,780 --> 00:08:27,000 field to open it up. This will open the file  in Visual Studio, which we installed earlier. 96 00:08:27,000 --> 00:08:31,020 So, welcome to programming! It's not too scary, 97 00:08:31,020 --> 00:08:35,460 promise. We'll take it slow. We're writing  in C sharp, that's the programming language. 98 00:08:35,460 --> 00:08:41,340 And the only thing to worry about right now  is these two chunks here: start and update. 99 00:08:41,340 --> 00:08:46,620 Start is for any code that will run as  soon as this script is enabled. And it 100 00:08:46,620 --> 00:08:52,620 runs precisely once. Update runs constantly  while the script is enabled. And it will fire 101 00:08:52,620 --> 00:08:57,360 off every line of code, every single  frame. Over and over and over again. 102 00:08:58,080 --> 00:09:02,820 So the main thing we're going to be doing  with code right now is - well, if we go back 103 00:09:02,820 --> 00:09:08,340 to Unity - see these numbers and text fields in  the components? And how we can change them in 104 00:09:08,340 --> 00:09:13,320 the Unity editor? We're just going to write code  to change these stats while the game is running. 105 00:09:13,320 --> 00:09:17,880 Just as a dumb example, and we'll  delete this in a second. In start, 106 00:09:17,880 --> 00:09:25,920 we can type gameObject - that refers to this bit  up here. And then a dot. You'll see a list appear, 107 00:09:25,920 --> 00:09:30,720 and many of the items refer to stuff back  in the Inspector, like isStatic, 108 00:09:30,720 --> 00:09:38,940 tag, layer, and name. So let's pick name.  Then write an equals sign. And in quotes, 109 00:09:38,940 --> 00:09:44,640 give our bird a name. Finally, we must always  use a semi-colon to mark the end of a command. 110 00:09:45,433 --> 00:09:49,573 And we must always save the script before we go back to Unity. 111 00:09:49,573 --> 00:09:54,360 Now, when we run the game... the name of  the GameObject has been changed. Nice. 112 00:09:55,080 --> 00:09:58,560 Okay, delete that code. That was just  for sillies - but it shows us how we 113 00:09:58,560 --> 00:10:03,120 can use code to talk to the game. We can  write a command by choosing someone to 114 00:10:03,120 --> 00:10:08,280 talk to - in this game, the GameObject  - and then a topic of conversation - its 115 00:10:08,280 --> 00:10:14,160 name - and then a command - change it to  Bob Birdington. We'll be doing this a lot. 116 00:10:14,880 --> 00:10:19,740 So what we actually want to do is... in  the Rigidbody 2D's component, under info, 117 00:10:19,740 --> 00:10:23,400 we'll see a greyed-out field  for velocity. And we want to 118 00:10:23,400 --> 00:10:27,120 write some code to add upward velocity  to the bird to make it fly into the air. 119 00:10:27,120 --> 00:10:32,640 The problem is... initially, a script can  only talk to the GameObject's top bit and 120 00:10:32,640 --> 00:10:37,200 the transform. Right now, this script is  completely unaware of the other components. 121 00:10:37,200 --> 00:10:42,480 So we need to sort that out first. We need  to make a special slot on this script for 122 00:10:42,480 --> 00:10:48,360 a Rigidbody2D - so we can then talk to it and  send it commands. This is called a reference. 123 00:10:48,360 --> 00:10:52,380 We're going to create the reference  up here, between the class name and 124 00:10:52,380 --> 00:10:57,840 the start function. We're going to  write public Rigidbody2D myRigidbody. 125 00:11:00,540 --> 00:11:06,480 So we now have a slot to store a Rigidbody2D.  And we have a name that we can refer to - to make sure 126 00:11:06,480 --> 00:11:12,060 we're talking about this specific Rigidbody2D. And  because we made it public, it means we can access 127 00:11:12,060 --> 00:11:18,240 this slot from outside the script. So, if we save.  And go back to Unity, we'll see that the script 128 00:11:18,240 --> 00:11:24,780 component now has a field for a Rigidbody2D. We  can drag the component into that slot, and viola. 129 00:11:24,780 --> 00:11:28,740 We have established a line of communication  between the script and the Rigidbody. 130 00:11:29,340 --> 00:11:36,300 Okay, back in Visual Studio. In update,  we can type myRigidbody. Then dot. And 131 00:11:36,300 --> 00:11:40,440 now look at all the things we can talk  about. Angular drag, gravity scale, 132 00:11:40,440 --> 00:11:45,540 mass - these are all properties on the  component. The one we want is velocity. 133 00:11:45,540 --> 00:11:51,060 We want to set this to a new number, and so, just  like before with the name, we'll write an equals. 134 00:11:51,060 --> 00:11:57,180 Now what we're actually writing here is a vector,  which is two numbers, to represent a position in 135 00:11:57,180 --> 00:12:02,400 2D space. And in this case, it's used to represent  a direction for the bird to travel. We want the 136 00:12:02,400 --> 00:12:08,940 bird to go straight up, so zero, comma one would  be a good one. I'm just going to use Vector2.up, 137 00:12:08,940 --> 00:12:14,400 which a built-in shorthand for zero comma one.  And to give it a bit more power, I'm going 138 00:12:14,400 --> 00:12:19,920 to multiply that vector by a number. Say, 10,  which should send the bird flying up in the sky. 139 00:12:19,920 --> 00:12:24,720 Now, like I said before, any code in  update will run, over and over again, 140 00:12:24,720 --> 00:12:31,560 every frame. So if we save the script and  hit play in Unity... off goes our bird. Bye!! 141 00:12:31,560 --> 00:12:36,360 That's not what we want. We want this to only  happen when the player hits the space bar. So 142 00:12:36,360 --> 00:12:41,040 it's time to use the most fundamental bit  of programming code: the if statement. 143 00:12:41,040 --> 00:12:43,380 An if statement is like a gate. 144 00:12:43,380 --> 00:12:47,640 You can surround some code with a fence,  and every frame that code will be completely 145 00:12:47,640 --> 00:12:52,020 ignored. Unless, the game meets some  specific conditions that are written 146 00:12:52,020 --> 00:12:56,700 on the gate - in which case the gate is  open, and the code is read and executed. 147 00:12:56,700 --> 00:13:00,960 So we want to say "if the player hits  the space bar, then add upward velocity". 148 00:13:00,960 --> 00:13:06,300 To do this... we can write if, and then in  brackets we can write the condition. This time 149 00:13:06,300 --> 00:13:12,360 we're not talking to a component, we're talking to  Unity itself - specifically its input system. So 150 00:13:12,360 --> 00:13:20,160 we'll write Input. Then we can pick GetKeyDown,  and in brackets, KeyCode.Space. This asks Unity 151 00:13:20,160 --> 00:13:25,200 if the space bar has been pressed on this frame.  And then we'll finish with equals, equals true. 152 00:13:25,200 --> 00:13:31,020 A quick note on equals signs - we use one  to make the thing on the left be the same 153 00:13:31,020 --> 00:13:35,280 as the thing on the right. And we  use two if we're just checking if 154 00:13:35,280 --> 00:13:38,520 the thing on the left is the same  as the thing on the right. Cool? 155 00:13:38,520 --> 00:13:43,740 Anyway. So this code says... if the space bar  has just been pressed, then... and then we'll 156 00:13:43,740 --> 00:13:48,960 use curly brackets - these are the fence in our  little analogy - and put the flap code in here. 157 00:13:50,100 --> 00:13:54,240 So, now in update - every frame the game  will go to the gate and be asked "hey, 158 00:13:54,240 --> 00:13:59,340 has the spacebar just been pressed?" If yes, the  code will fire and the bird will flap. If not, 159 00:13:59,340 --> 00:14:02,760 it will skip the code in the curly  brackets and try again next frame. 160 00:14:02,760 --> 00:14:08,160 So - save the script and go back to  Unity. We can now hit play and tada: 161 00:14:08,160 --> 00:14:11,460 the bird goes up when we press space. We have now 162 00:14:11,460 --> 00:14:17,340 created a character and made it react  to input. This is a video game. Hooray! 163 00:14:17,340 --> 00:14:23,100 However, it feels like trash. The flap isn't  right, and it doesn't feel like the original 164 00:14:23,100 --> 00:14:31,860 iPhone game. So we could change this number.  Save. Open Unity. Run the game. Not quite right. Stop. Change 165 00:14:31,860 --> 00:14:36,540 the number. Save. But that's slow and  dumb. Let's do something smarter. 166 00:14:36,540 --> 00:14:41,520 First, we're going to make a variable. Let's  go back to the top of the script and under 167 00:14:41,520 --> 00:14:47,160 our reference to the Rigidbody, let's make  a public float called flapstrength. A float 168 00:14:47,160 --> 00:14:51,960 is a floating point number - basically a  number that can have a decimal place. And 169 00:14:51,960 --> 00:14:57,720 then back in our update code, we'll multiply  the vector2.up by flapstrength, instead of 10. 170 00:14:57,720 --> 00:15:02,040 Now, back in Unity, you'll see that  the script component has a new field: 171 00:15:02,040 --> 00:15:06,900 flapStrength. And we can change that  whenever we want to make the game feel 172 00:15:06,900 --> 00:15:11,340 different. We can even change it during the  game, but note that anything you change while 173 00:15:11,340 --> 00:15:15,360 the game is running won't save when you  press stop. This means you can play with 174 00:15:15,360 --> 00:15:18,660 values to your heart's content without  worrying about screwing up your game. 175 00:15:18,660 --> 00:15:22,680 So, if we mess with the flapStrength,  and also the gravity scale on 176 00:15:22,680 --> 00:15:26,700 the Rigidbody, we'll hopefully get  to something that feels good. Ah, 177 00:15:26,700 --> 00:15:29,640 changing numbers back and forth:  honey, that's game design! 178 00:15:30,300 --> 00:15:31,440 Recap time. 179 00:15:31,440 --> 00:15:36,000 We can use code to change the properties  of a component, while the game is running. 180 00:15:36,000 --> 00:15:40,860 A script cannot talk to the other components  on the gameobject, by default. You have to 181 00:15:40,860 --> 00:15:45,000 make a line of communication by storing  a reference to that specific component. 182 00:15:45,000 --> 00:15:49,860 We create the reference in code, and then  fill it in Unity by dragging and dropping. 183 00:15:50,400 --> 00:15:56,280 Code in start runs once, when the script comes  into existence. Code in update runs continuously, 184 00:15:56,280 --> 00:16:01,920 every single frame. But, we can use if statements  to skip some code, unless a condition is met. 185 00:16:01,920 --> 00:16:04,740 And we can use public variables to change certain 186 00:16:04,740 --> 00:16:07,860 values in Unity's inspector -  even while the game is running. 187 00:16:09,480 --> 00:16:13,680 Okay, so the secret to Flappy Bird  is that while it looks like a bird 188 00:16:13,680 --> 00:16:18,180 is flapping along through a world of  pipes - it's actually not. The bird 189 00:16:18,180 --> 00:16:23,640 stays completely still and the pipes move  across the screen. So in step three we're 190 00:16:23,640 --> 00:16:28,620 going to make pipes spawn into the world, move  across the screen, and then delete themselves. 191 00:16:28,620 --> 00:16:32,280 We'll start by making the object  we want to spawn. This will be 192 00:16:32,280 --> 00:16:35,100 two pipes which move across the  screen, from the left to right. 193 00:16:35,100 --> 00:16:40,500 Let's make another GameObject called  pipe. Put it exactly on the bird for now, 194 00:16:40,500 --> 00:16:44,520 to get the sizing right. And then we'll  make another object within this one, 195 00:16:44,520 --> 00:16:50,700 called top pipe. This is a child of the  first GameObject's parent. This way we 196 00:16:50,700 --> 00:16:55,080 can nest multiple GameObjects, and move all  of them at once just by moving the parent. 197 00:16:55,980 --> 00:17:00,000 So let's repeat what we did for the bird.  Add a sprite renderer for the pipe image. 198 00:17:00,900 --> 00:17:06,840 And add a collider - a Box Collider 2D, this time.  We don't need a RigidBody because it's not going 199 00:17:06,840 --> 00:17:13,440 to be affected by physics. We can then move it up  above the bird - but keep the X position as zero. 200 00:17:13,440 --> 00:17:19,020 Finally, we can duplicate this whole top  pipe object. Call it bottom pipe. And flip 201 00:17:19,020 --> 00:17:24,420 it upside down by changing the Y scale to  minus one. Then move it down below the bird. 202 00:17:24,420 --> 00:17:31,020 As you can see, if we mess with the pipe  parent GameObject, both pipes move, scale, 203 00:17:31,020 --> 00:17:35,400 and rotate along with it, with the  parent as the pivot point. So let's 204 00:17:35,400 --> 00:17:38,940 add a script to this parent's object  to make it move across the screen. 205 00:17:40,680 --> 00:17:46,080 We'll start by creating a variable for  moveSpeed. If we give it a number here, 206 00:17:46,080 --> 00:17:50,700 it will fill this as the default value in  Unity. But we can always change it there, later. 207 00:17:50,700 --> 00:17:56,100 Then we'll write code to move the object, in  update. Now it would be lovely if we could just 208 00:17:56,100 --> 00:18:03,360 type transform.position.x, and change this number  directly - but, no, boo, you have to change the 209 00:18:03,360 --> 00:18:08,580 entire Vector in one go. Oh, and this time we're  gonna have to use Vector3, instead of Vector2, 210 00:18:08,580 --> 00:18:14,760 because the transform has three numbers. Even  though we're making our game in 2D, Unity is still 211 00:18:14,760 --> 00:18:19,980 fundamentally a 3D engine and so it's keeping  track of the object's depth with the Z value. 212 00:18:20,580 --> 00:18:26,160 So, here's what we'll do. We'll take the  current transform.position. And then equals. 213 00:18:26,160 --> 00:18:31,200 We want to add to its current position,  so write transform.position again. And 214 00:18:31,200 --> 00:18:37,320 then plus. And finally, in brackets,  we'll do Vector3.left * moveSpeed. 215 00:18:39,000 --> 00:18:43,500 Back in Unity, press play and  vroooof. That's way too fast. Now, 216 00:18:43,500 --> 00:18:47,520 you might think that you could just change  this moveSpeed variable down to a really 217 00:18:47,520 --> 00:18:53,280 small number like 0.001. And that will work  - but that's not actually the problem here. 218 00:18:53,280 --> 00:18:58,860 You see, code in update just runs as often as it  can. In fact, if we check the stats in Game view, 219 00:18:58,860 --> 00:19:03,060 we'll see the game is running at  over 1,000 frames per second. Heh, 220 00:19:03,060 --> 00:19:07,680 sorry PlayStation 5. 120 fps? Pfft,  that's got nothing on Flappy Bird. 221 00:19:07,680 --> 00:19:11,880 And the real problem is that the game may run  at different speeds on different computers, 222 00:19:11,880 --> 00:19:16,440 and we don't want the pipe to move  faster or slower depending on your rig. 223 00:19:16,440 --> 00:19:22,680 Real games have actually made this mistake - in  Dark Souls 2, weapon durability was once tied 224 00:19:22,680 --> 00:19:30,660 to frame rate, so your swords would break twice  as fast at 60 FPS, compared to 30 FPS. That was a whoopsie. 225 00:19:30,660 --> 00:19:36,420 Luckily, it's a pretty easy fix. We  just multiply it by Time.deltaTime. 226 00:19:36,420 --> 00:19:41,280 This ensures the multiplication happens the  same, no matter the frame rate. We didn't 227 00:19:41,280 --> 00:19:44,820 need it for the velocity code because  physics runs on its own little clock, 228 00:19:44,820 --> 00:19:49,320 but otherwise we will need it. if you want to  know more - about this, or anything really, 229 00:19:49,320 --> 00:19:53,580 the Unity docs are a good place to  check. You'll find info and sample code. 230 00:19:53,580 --> 00:19:59,400 Okay, now with that fix in place, our pipe  moves smoothly across the screen. Lovely. 231 00:19:59,400 --> 00:20:04,560 Next, we want to create a system that will  continually spawn new pipes. To start, 232 00:20:04,560 --> 00:20:10,740 take the parent GameObject from the hierarchy  and drag it into your project. This creates a 233 00:20:10,740 --> 00:20:16,980 prefabricated GameObject. Or prefab. This is like  a blueprint for a GameObject and we can create new 234 00:20:16,980 --> 00:20:22,500 versions of this entire GameObject- with all  its children, components, and properties. Oh, 235 00:20:22,500 --> 00:20:26,460 and before we move on, we can delete the  original in our hierarchy now. Bye bye. 236 00:20:26,460 --> 00:20:29,460 Let's make a new GameObject called Pipe Spawner. 237 00:20:30,720 --> 00:20:36,240 We'll put it just to the right of the camera. And  we'll make a script for it. The purpose of this 238 00:20:36,240 --> 00:20:41,820 script is to spawn new versions of the pipe prefab  every few seconds. And because the pipe already 239 00:20:41,820 --> 00:20:46,800 has code to move left, the pipe will automatically  move across the screen as soon as it spawns in. 240 00:20:47,400 --> 00:20:51,360 We're going to write some code to  spawn that prefab we just made. So 241 00:20:51,360 --> 00:20:54,276 we'll start by making a reference  to the prefab. 242 00:20:54,276 --> 00:20:58,680 Up here, we'll type Public GameObject pipe. 243 00:20:58,680 --> 00:21:03,840 Then in Unity, we'll use the same drag and  drop method to fill the slot, but this time, 244 00:21:03,840 --> 00:21:07,920 instead of a component, we'll drag  the prefab from the project panel. 245 00:21:07,920 --> 00:21:11,580 Now, Unity has a nice built-in method for spawning 246 00:21:11,580 --> 00:21:17,880 new GameObjects. We'll type Instantiate, and then  open the brackets. In here, the command is asking 247 00:21:17,880 --> 00:21:22,980 for some extra details. we can actually flip  through these to find different, I dunno, recipes? 248 00:21:22,980 --> 00:21:28,500 I guess? Number 4 looks good - it will create  an object at a specified position and rotation. 249 00:21:28,500 --> 00:21:32,760 So, for the GameObject, we can  type pipe. For position 250 00:21:32,760 --> 00:21:37,560 we can just type transform.position to  get the position of the object holding 251 00:21:37,560 --> 00:21:42,180 this script. That will make it spawn on  top of the spawner. And for rotation, 252 00:21:42,180 --> 00:21:46,800 let's just use transform.rotation so,  again, it's the same as the spawner. 253 00:21:48,300 --> 00:21:52,740 Let's run it and oh my god, that's not  what we want. Spawning works great, 254 00:21:52,740 --> 00:21:55,560 but they're coming out every single  frame - and we want them to come 255 00:21:55,560 --> 00:21:59,760 out on a nice interval that we can  control. So, back to Visual Studio. 256 00:21:59,760 --> 00:22:05,100 What we're going to do now is to write some  code to make a timer. this will count up for 257 00:22:05,100 --> 00:22:10,140 a specified number of seconds, run some code,  and then start the count again. To do this, 258 00:22:10,140 --> 00:22:14,520 we'll need to make a couple variables. A  spawnRate is how many seconds it should be 259 00:22:14,520 --> 00:22:19,560 between spawns. And then a timer is the  number that counts up. We can make this 260 00:22:19,560 --> 00:22:22,980 one private as we won't be changing  it in the editor or anywhere else. 261 00:22:22,980 --> 00:22:29,400 In update, we'll do another if statement. This  time, if the timer is less than the spawnRate, 262 00:22:29,400 --> 00:22:34,980 then we want to make the timer count up by one.  So we'll take the timer as it currently is, 263 00:22:34,980 --> 00:22:39,780 and add Time.deltaTime to it. This creates  a number that counts up every frame, 264 00:22:39,780 --> 00:22:42,600 and works the same no matter what  your computer's frame rate is. 265 00:22:42,600 --> 00:22:48,480 We can actually shorten this by changing it to +=,  but, don't feel like you need to make your code as 266 00:22:48,480 --> 00:22:53,040 short as humanly possible just to avoid  getting sniffy YouTube comments. If 267 00:22:53,040 --> 00:22:58,440 timer = timer + is easier to read and  grasp, then that's absolutely fine. You 268 00:22:58,440 --> 00:23:01,740 can always swap to the other version in  the future when you feel more confident. 269 00:23:01,740 --> 00:23:05,220 Now, before I said an if statement is like a gate. 270 00:23:05,220 --> 00:23:09,540 And we can add another gate to the  side of it, with else. This means, 271 00:23:09,540 --> 00:23:14,520 if the condition isn't met, then skip the  code - and do the code in else, instead. 272 00:23:14,520 --> 00:23:22,560 So we'll put the spawn code in here, and also  reset the timer to zero. So now, every frame, it 273 00:23:22,560 --> 00:23:28,500 asks if the timer is less than the spawn rate. If  it is, then count the timer up. If it's not - i.e. 274 00:23:28,500 --> 00:23:33,420 the timer has actually met or exceeded the spawn  rate, then spawn a pipe and start the timer again. 275 00:23:33,420 --> 00:23:38,820 Put this in Unity and - pretty good. I'm happy  with that. The only problem is... we have to 276 00:23:38,820 --> 00:23:44,100 wait ages for the first pipe to spawn. It would  be good if this came out immediately, right? 277 00:23:44,100 --> 00:23:49,560 Now, we could copy and paste the spawn code  into start, so it happens once in start. 278 00:23:49,560 --> 00:23:55,320 And then happens over and over in update. But  that's a bad idea. You should generally try to 279 00:23:55,320 --> 00:24:00,900 avoid having the same, or even similar code  in multiple places. What happens if we want 280 00:24:00,900 --> 00:24:06,120 to change how the spawn works? We'll have  to find and change it everywhere. No good. 281 00:24:06,120 --> 00:24:10,260 Instead, we can put the spawn code in  a new function, and then just run that 282 00:24:10,260 --> 00:24:15,180 function. So here, below update - but  above the final curly bracket - we'll 283 00:24:15,180 --> 00:24:21,420 make a function called void spawnPipe(). And then  cut and paste the Instantiate code into there. 284 00:24:21,420 --> 00:24:27,720 Now we can just write spawnPipe, with empty  brackets, in both update and start. This will run 285 00:24:27,720 --> 00:24:32,340 all the code in that function when these lines  are executed. And with that done, it will make 286 00:24:32,340 --> 00:24:37,800 a pipe as soon as the game begins, and will make  new pipes every time the timer maxes out. Perfect. 287 00:24:38,760 --> 00:24:44,700 However - this is a pretty boring game, right? The  pipes always come out in the middle. we want them 288 00:24:44,700 --> 00:24:49,080 to come out at random heights. So, remember  that when we wrote the instantiate code, 289 00:24:49,080 --> 00:24:52,980 we had to pick a position for the object  to appear? We'll change that value. 290 00:24:52,980 --> 00:24:58,140 Right now the pipes always spawn on the same  position as the spawner. We want the X value 291 00:24:58,140 --> 00:25:03,960 to be the same... but for Y, we want to pick a  random point somewhere above or below the spawner. 292 00:25:03,960 --> 00:25:08,520 So let's create a public variable  for a heightOffset, maybe 10. 293 00:25:08,520 --> 00:25:14,220 And then we'll make a float called lowestPoint.  Because we're making this variable inside the 294 00:25:14,220 --> 00:25:19,800 function, rather than at the top of the script, it means  it can only be used within the function. But, 295 00:25:19,800 --> 00:25:22,200 also, it means we can set  it by doing a calculation. 296 00:25:22,200 --> 00:25:29,820 so we'll do equals transform.position.y -  heightOffset. And then we'll make another 297 00:25:29,820 --> 00:25:36,600 one for highestPoint, but this time it's plus  heightOffset. That gets us these two numbers. 298 00:25:37,560 --> 00:25:41,400 Then we'll replace the transform.position  in our Instantiate code. 299 00:25:41,400 --> 00:25:47,400 We're gonna write new Vector3, we have to write that  whenever we're specifying our own numbers for a 300 00:25:47,400 --> 00:25:53,700 vector. and then in brackets we'll specify the X,  Y, and Z values as three different floats. For X, 301 00:25:53,700 --> 00:25:59,160 we want this to be the same as the spawner,  so we'll do transform.position.x. But for Y, 302 00:25:59,160 --> 00:26:05,160 we can do Random.Range. And in the brackets for  that, we can supply a minimum and maximum point 303 00:26:05,160 --> 00:26:12,300 to pick from. That's lowestPoint and highestPoint.  Then a 0 for Z. And close the brackets. 304 00:26:14,220 --> 00:26:19,560 Back in Unity.... nice! The pipes will  spawn anywhere between these two numbers. 305 00:26:19,560 --> 00:26:24,780 Oh, one last thing. Every time these  pipes spawn they'll appear and move 306 00:26:24,780 --> 00:26:30,120 left.... forever. Which isn't great practice -  they're off screen and doing absolutely nothing, 307 00:26:30,120 --> 00:26:35,100 and yet they're still in memory and running code  every frame. And if too many spawn they'll start 308 00:26:35,100 --> 00:26:40,140 to spill out the side of your monitor and  make a right mess of your desk. So let's fix that. 309 00:26:40,140 --> 00:26:45,060 Now we could make a timer, and delete the  pipe after a few seconds. But instead, 310 00:26:45,060 --> 00:26:50,700 we'll check the X position of the pipe, and delete  it if it goes past a certain point. We'll borrow 311 00:26:50,700 --> 00:26:58,260 the bird to find out the X coordinate of the  left of the screen. Looks about minus 45. In 312 00:26:58,260 --> 00:27:06,120 the pipe move script, we'll add a float for a  deadzone. -45. And then a simple if statement - if 313 00:27:06,120 --> 00:27:13,020 transform.position.x is less than deadZone, then  destroy the GameObject that holds this script. 314 00:27:15,720 --> 00:27:18,840 Run it in Unity and, bam, they're dead. 315 00:27:18,840 --> 00:27:24,420 Let's do one more thing, just as a teachable  moment. Just before the destroy line, 316 00:27:24,420 --> 00:27:32,880 let's write Debug.Log, and in brackets, Pipe Deleted. Then, back in Unity, you'll see one other panel 317 00:27:32,880 --> 00:27:39,420 I skipped during the UI demo - it's a tab next  to project, called console. Then when we run the 318 00:27:39,420 --> 00:27:44,820 game... every time a pipe is deleted, our  message is sent to the console. This is a 319 00:27:44,820 --> 00:27:50,280 wonderfully useful way to debug our code, because  we can find out exactly what the code is up to. 320 00:27:51,900 --> 00:27:52,800 Recap time! 321 00:27:52,800 --> 00:27:55,560 GameObjects can be turned into prefabs, 322 00:27:55,560 --> 00:27:58,980 by dragging them from the hierarchy,  and dropping them into the project. 323 00:27:58,980 --> 00:28:04,020 You can then drag these into scenes - I use  prefabs to create levels in my puzzle game, 324 00:28:04,020 --> 00:28:08,940 for example. Or you can make a spawner  to instantiate new ones during the game. 325 00:28:08,940 --> 00:28:12,660 Timers are a great way to make  code happen on a certain interval, 326 00:28:12,660 --> 00:28:17,400 but always use Time.deltaTime to keep things  consistent across different computers. 327 00:28:17,400 --> 00:28:21,540 If statements can have an else gate,  to make code fire if the condition 328 00:28:21,540 --> 00:28:26,520 is not met. You can also have else  if, to make more complicated gates. 329 00:28:26,520 --> 00:28:30,960 And you should try to delete GameObjects if  they're no longer needed, to free up memory. 330 00:28:32,400 --> 00:28:35,820 Okay, our next step is to keep  track of the player's score, 331 00:28:35,820 --> 00:28:39,360 and show it to the player  on the user interface. Then, 332 00:28:39,360 --> 00:28:43,380 we want the score to go up by one, every  time the bird goes through the pipes. 333 00:28:43,380 --> 00:28:47,460 So, remember that a GameObject doesn't  have to be a physical thing in your game 334 00:28:47,460 --> 00:28:52,560 world like a character or an enemy - it  can be a completely invisible manager 335 00:28:52,560 --> 00:28:57,540 that's just keeping track of critical data  like health, or time, or score. And then, 336 00:28:57,540 --> 00:29:01,260 we can make that information visible  to the player, using a user interface. 337 00:29:01,260 --> 00:29:06,600 So let's start by making the UI. Like  everything else, it's a GameObject in 338 00:29:06,600 --> 00:29:11,520 the hierarchy. This time go down to  UI and pick text - which may be under 339 00:29:11,520 --> 00:29:16,680 legacy. We'll need to zoom really far out  on the scene view to actually see the UI. 340 00:29:16,680 --> 00:29:21,660 To make sure the UI looks the same on every  device, we'll pick this new canvas GameObject 341 00:29:21,660 --> 00:29:28,260 and set the canvas scaler component's UI scale  to scale with screen size, and choose a sensible 342 00:29:28,260 --> 00:29:34,200 reference resolution - I'm gonna use 1080p again.  We can then move our text around. You'll notice 343 00:29:34,200 --> 00:29:40,140 that UI has a rect transform, rather than a normal  transform. The most important thing to note is 344 00:29:40,140 --> 00:29:44,700 that you don't really want to mess with scale of  elements - instead, change the width and height. 345 00:29:46,620 --> 00:29:49,080 I'll then increase the font size and set the default 346 00:29:49,080 --> 00:29:53,280 text to 0. And then check it  all looks nice on the game view. 347 00:29:53,280 --> 00:29:56,640 Okay, now we want to make a script  that will store the player's score, 348 00:29:56,640 --> 00:29:59,820 and change the number on the UI to that score. 349 00:29:59,820 --> 00:30:02,940 We'll make a GameObject called Logic Manager. 350 00:30:03,960 --> 00:30:08,820 And we'll give it a script. This script is  going to keep track of high level information 351 00:30:08,820 --> 00:30:13,860 like the player's score. And it will have  various meta-level functions that we can run. 352 00:30:13,860 --> 00:30:18,180 So we'll delete start and update, we don't  need them in this script. We can always add 353 00:30:18,180 --> 00:30:22,740 them back later if we change our mind. We want to  store a number for the player's score. This time, 354 00:30:22,740 --> 00:30:27,600 we don't want a float because we only ever  want round numbers. So let's do an int, 355 00:30:27,600 --> 00:30:30,360 instead. That's an integer. No decimal places. 356 00:30:30,360 --> 00:30:34,980 And because we want to update the UI  text we just made we will, as always, 357 00:30:34,980 --> 00:30:40,680 have to make a reference. Except...  text doesn't seem to be a thing? 358 00:30:40,680 --> 00:30:46,320 Ah, well. By default, a script only loads  in the stuff you need for basic Unity 359 00:30:46,320 --> 00:30:52,500 functionality - but if we go up to the top  and type using UnityEngine.UI;, we can now 360 00:30:52,500 --> 00:30:58,680 access more functionality - in this case, UI  stuff. Now we can make a reference to text. 361 00:30:59,280 --> 00:31:04,680 We'll need to drag the text component into this  field back in Unity. Because we're referencing a component 362 00:31:04,680 --> 00:31:09,540 on another GameObject - the text on the UI  - the best way to do this is to just drag 363 00:31:09,540 --> 00:31:16,020 the whole GameObject into our slot. This will  automatically find the text component for us. Handy. 364 00:31:16,020 --> 00:31:20,460 So now we want to make a function. And  we'll call it addScore. And because 365 00:31:20,460 --> 00:31:25,009 we're going to run this function from  other scripts, we'll set it to public void. 366 00:31:25,740 --> 00:31:30,840 This function needs to do two things. Add  one to the player's score. Easy enough, 367 00:31:30,840 --> 00:31:39,240 we know how to do that now. And change the text  on the UI to be this number. Oh, the text box is 368 00:31:39,240 --> 00:31:45,720 looking for a string - a sequences of characters  - and our score is an integer. They look identical 369 00:31:45,720 --> 00:31:51,720 to us humans, but robots are fussy. Easily fixed,  mind you, by adding .toString() to the game score. 370 00:31:52,800 --> 00:31:56,580 To make sure this works, let's give  ourselves the power to run this function 371 00:31:56,580 --> 00:32:02,820 from Unity itself. All we need to do is write  ContextMenu, and a name, above the function. 372 00:32:05,340 --> 00:32:10,140 Now, in Unity, while the game is running, hit the  little dots on this script and pick the function. 373 00:32:10,800 --> 00:32:14,460 Nice! This sort of thing comes  in real handy for testing. 374 00:32:15,060 --> 00:32:18,660 Okay, so now that we know the function  runs, we specifically want to run it 375 00:32:18,660 --> 00:32:23,220 when the bird goes between the pipes.  And the way to do this is collisions. 376 00:32:23,220 --> 00:32:28,380 Now if two objects have colliders, they will  bash into each other - in fact, in our game, 377 00:32:28,380 --> 00:32:33,060 the bird will already crash into the pipes because  we've added colliders to both. However - you 378 00:32:33,060 --> 00:32:37,740 can also have invisible colliders, called  triggers. They don't create an actual collision, 379 00:32:37,740 --> 00:32:42,000 but they do let you know that two objects have  touched - and you can run code at that moment. 380 00:32:42,000 --> 00:32:45,180 So we're going to put a  trigger in between the pipes, 381 00:32:45,180 --> 00:32:50,700 so we know that the bird has passed through them. And then at that moment, we'll run addScore. 382 00:32:50,700 --> 00:32:55,920 Let's open up the prefab for the pipes. We'll  make another GameObject called middle - and 383 00:32:55,920 --> 00:33:00,900 it needs a box collider. Let's make it  this sort of shape. And this time we'll 384 00:33:00,900 --> 00:33:06,420 tick the box isTrigger. Finally, let's add  a script to this new middle GameObject. 385 00:33:06,420 --> 00:33:13,920 Beneath Update, type ontrig, and the autocorrect  will help us type out OnTriggerEnter2D. Just 386 00:33:13,920 --> 00:33:18,960 press tab to autofill. Anything in this  function will run whenever an object first 387 00:33:18,960 --> 00:33:25,620 hits the trigger. There's also OnTriggerExit and  OnTriggerStay, for future reference. And its in here, 388 00:33:25,620 --> 00:33:30,060 that we want to run the addscore function we  wrote earlier... except. ah. once again, 389 00:33:30,060 --> 00:33:34,440 this script doesn't know about any other scripts  in the game, until we make a reference to it. 390 00:33:34,440 --> 00:33:41,340 So we can write public LogicScript logic.  But back in Unity, you'll quickly realise 391 00:33:41,340 --> 00:33:45,840 that you can't drag the script into this  slot. You can't drag it from the project 392 00:33:45,840 --> 00:33:51,060 panel - we can only talk to an instance of a  script that lives on a GameObject. But we also 393 00:33:51,060 --> 00:33:56,880 can't drag from the scene into the prefab. That's  because the pipe doesn't exist in the scene yet, 394 00:33:56,880 --> 00:34:01,320 it will only exist when the game is running,  and the spawner starts making pipes. 395 00:34:01,320 --> 00:34:05,520 So, instead, we'll need to fill  this reference using code. and 396 00:34:05,520 --> 00:34:08,340 this needs to happen when the pipe first spawns. 397 00:34:08,340 --> 00:34:12,720 To do this, we'll need to help the  code find the logic script. To do this, 398 00:34:12,720 --> 00:34:16,800 take the Game Logic object, and  look at the top of the inspector: 399 00:34:16,800 --> 00:34:23,820 you'll see tags. From the drop down,  choose add tag. Make a new tag called, say, 400 00:34:23,820 --> 00:34:30,060 Logic. And make sure you go back to the GameObject  and actually set this new tag. You will forget to 401 00:34:30,060 --> 00:34:34,680 do this approximately eight thousand times in  your Unity career, so look forward to that. 402 00:34:34,680 --> 00:34:37,380 Now, back in the PipeMiddleScript, 403 00:34:37,380 --> 00:34:46,740 under start we can write logic =  GameObject.FindGameObjectWithTag("Logic"). 404 00:34:46,740 --> 00:34:50,820 this will look for the first GameObject  in the hierarchy with the tag, 405 00:34:50,820 --> 00:34:54,420 Logic. In our case, there will  only ever be one in the scene, 406 00:34:54,420 --> 00:34:57,900 so we know it will always find the  right one - but do be mindful of that. 407 00:34:57,900 --> 00:35:02,280 And then we can add .GetComponent(); 408 00:35:03,480 --> 00:35:09,300 So, as soon as a new pipe spawns, it will look  through the hierarchy to find a GameObject with 409 00:35:09,300 --> 00:35:13,800 the tag Logic. Then, it will look through  that object's components to find a script 410 00:35:13,800 --> 00:35:19,020 of the class LogicScript. And if it finds  one, it will put that in our reference slot. 411 00:35:19,020 --> 00:35:22,740 It has done the exact same thing as  dragging and dropping the component 412 00:35:22,740 --> 00:35:28,200 in the Unity editor - except it has done  it instantly, during run time. Excellent. 413 00:35:28,860 --> 00:35:33,960 So now, the pipe's middle script can  find and talk to the logic script. 414 00:35:35,400 --> 00:35:41,340 And if we write logic.addScore, this  will run that code. Back in Unity, 415 00:35:41,340 --> 00:35:47,340 hit play and if we did everything right, the score  will go up by one when we pass between the pipes. 416 00:35:47,340 --> 00:35:52,860 Oh, and just for future proofing and whatnot,  let's make sure that it was actually the bird 417 00:35:52,860 --> 00:35:57,660 that went through. We'll do this by putting the  bird on a layer, and checking if the colliding 418 00:35:57,660 --> 00:36:03,900 object was on that layer. Go to the bird's GameObject  and this time, instead of the tag, we'll change 419 00:36:03,900 --> 00:36:10,620 the bird's layer. Make a new one, remember to  actually assign it, and make a note of the number. 420 00:36:10,620 --> 00:36:15,480 Now, on the pipe's middle script, we can add an if statement around addScore, 421 00:36:15,480 --> 00:36:20,400 and check if the collision that just happened  was with a GameObject on the bird's layer. 422 00:36:22,560 --> 00:36:27,494 One more bit of future proofing, while we're on  the subject. Go back to the Logic Script. 423 00:36:27,494 --> 00:36:33,420 And, let's take the AddScore function, and in these empty brackets we'll write int 424 00:36:33,420 --> 00:36:38,520 scoreToAdd. And then instead of  adding one, we'll add scoreToAdd. 425 00:36:39,240 --> 00:36:45,360 Then in the pipe middle script, we can write  a 1 in the brackets after addScore. Right 426 00:36:45,360 --> 00:36:50,160 now this does exactly the same thing as we  had before. But, as you can surely guess, 427 00:36:50,160 --> 00:36:54,900 you could later add some other goal  in the game that adds, say, 5 to your score. 428 00:36:54,900 --> 00:36:58,980 This allows us to make a function  more versatile, as it can be used 429 00:36:58,980 --> 00:37:03,120 in different ways, from different places.  Part of being a good programmer, I think, 430 00:37:03,120 --> 00:37:07,440 is making stuff less rigid, and keeping it open for  future ideas. 431 00:37:07,440 --> 00:37:12,660 This makes it easier and faster  to iterate on your designs. 432 00:37:12,660 --> 00:37:14,040 Right! Recap! 433 00:37:14,040 --> 00:37:17,880 UI is just another GameObject,  but if we want to reference any 434 00:37:17,880 --> 00:37:22,920 of these components we'll need to add  using UnityEngine.UI to the top of the script. 435 00:37:22,920 --> 00:37:25,680 GameObjects can be completely invisible things, 436 00:37:25,680 --> 00:37:29,760 merely there to keep track of rules,  logic, score, and so on. 437 00:37:29,760 --> 00:37:34,080 If we want to a reference a component  when one of the GameObjects is not in the scene, 438 00:37:34,080 --> 00:37:36,660 we'll need to find that component during run time. 439 00:37:36,660 --> 00:37:42,300 One way to do this is to use tags,  findGameObject, and GetComponent. 440 00:37:42,300 --> 00:37:46,740 A public function can be run from another  script, as long as you have a reference 441 00:37:46,740 --> 00:37:50,940 to that script. And we can even pass  in variables when that function runs. 442 00:37:50,940 --> 00:37:56,580 And Collisions and triggers can be used to make  stuff happen when two objects touch. 443 00:37:56,580 --> 00:37:59,400 Speaking of collisions, let's  move on to the next step... 444 00:38:01,020 --> 00:38:06,900 The final step is to add a fail state. When  the bird hits the pipes, the game is over. 445 00:38:06,900 --> 00:38:11,340 We'll do this by making a game over screen,  and have it appear when the bird crashes 446 00:38:11,340 --> 00:38:16,320 into a pipe. The game over screen will have  a button, which we can use to reset the game. 447 00:38:16,980 --> 00:38:21,060 First, let's make that game over  screen. On the canvas GameObject, 448 00:38:21,060 --> 00:38:28,440 add a new empty one called game over screen.  Then, in that parent, add a text for game over. 449 00:38:30,960 --> 00:38:36,900 And also a a button - that's also  under legacy. Resize it. And change 450 00:38:36,900 --> 00:38:41,580 the text on the button - the text can be  found as a child on the button itself. 451 00:38:43,440 --> 00:38:48,000 So back on the button GameObject,  on the button component, you'll see 452 00:38:48,000 --> 00:38:53,520 this bit that says On Click. This is  an event, and it allows us to call a 453 00:38:53,520 --> 00:38:58,680 public function on a GameObject. So let's  make a function for restarting the level. 454 00:38:59,520 --> 00:39:02,100 We can put this code in the logic script, 455 00:39:02,100 --> 00:39:07,080 underneath our addScore function. You  could make a seperate script if you want, 456 00:39:07,080 --> 00:39:12,060 but I think this is fine. Let's make  another public function called restartGame, 457 00:39:12,060 --> 00:39:17,580 and in here we'll write code to restart the  scene. Just like before with the UI, if we're 458 00:39:17,580 --> 00:39:22,560 managing scenes then we'll need to add a line the  top - this time, using UnityEngine.SceneManagment. 459 00:39:23,940 --> 00:39:30,180 Now in our function, we'll call up the  SceneManager and then, dot, LoadScene. This 460 00:39:30,180 --> 00:39:35,460 is looking for the name of a scene. Literally the  filename. But because we want the current scene we 461 00:39:35,460 --> 00:39:42,720 can simply type SceneManager dot GetActiveScene,  brackets, dot name. Close off all the brackets. 462 00:39:44,580 --> 00:39:48,000 Now back in Unity, add an event to this button. 463 00:39:48,960 --> 00:39:53,340 Then drag in the logic GameObject.  and find the restartGame function. 464 00:39:57,300 --> 00:40:02,580 Give it a test and... nice. Every time we  press the button, the game begins anew. 465 00:40:02,580 --> 00:40:07,560 Now obviously we don't want this to be on the screen  all the time - just when we fail. So, 466 00:40:07,560 --> 00:40:12,000 we can just take the whole game over screen  GameObject and disable it with this checkmark. 467 00:40:13,380 --> 00:40:16,260 Then we'll make it show up when  the bird hits into the pipes. 468 00:40:16,260 --> 00:40:22,620 Let's write the function first. Again in the  logic script, let's make a public function for gameOver. 469 00:40:22,620 --> 00:40:27,750 We'll need to make a reference to the game over screen GameObject. 470 00:40:27,900 --> 00:40:30,116 And fill it in Unity. 471 00:40:31,467 --> 00:40:38,611 And then we can simply type  gameoverscreen.SetActive true in this function. 472 00:40:38,820 --> 00:40:42,300 So we want this function to trigger  when the bird crashes into a pipe. 473 00:40:42,300 --> 00:40:48,180 Back on the bird script, let's reuse that  code from before to access the logic script 474 00:40:48,180 --> 00:40:52,680 from the bird script. Yes, we could drag  and drop the reference in Unity, but hey, 475 00:40:52,680 --> 00:40:57,720 we've written this code now. And then we're going to do  a similar thing to the trigger code, but this time 476 00:40:57,720 --> 00:41:03,360 we'll use OnCollisionEnter2D, because the pipes  are solid objects, and not set to be triggers. 477 00:41:03,360 --> 00:41:10,277 And when that collision occurs, trigger  the game over script with logic.gameOver. 478 00:41:10,277 --> 00:41:16,440 Back in Unity... it kind of works, but we can  still play in the game over screen. Not ideal. 479 00:41:16,440 --> 00:41:21,420 So, I've talked about a few key  variable types, already. Floats 480 00:41:21,420 --> 00:41:28,380 and ints are numbers. And string is usually  for text. The other important one is a bool, 481 00:41:28,380 --> 00:41:34,320 short for boolean. This is a really simple  type that is either true, or false. On, 482 00:41:34,320 --> 00:41:38,340 or off. Yes, or no. It's a great way to  simply check or change something's state. 483 00:41:38,940 --> 00:41:44,160 So let's have a bool called birdisalive,  and make sure it starts as true. 484 00:41:44,160 --> 00:41:49,080 Then when the collision happens,  we'll set birdisalive to false. 485 00:41:49,080 --> 00:41:54,480 And finally, we'll add an extra condition to  our very first if statement. We're going to 486 00:41:54,480 --> 00:42:01,200 say if the space bar has just been pressed and...  written with two ampersands... and birdisalive is 487 00:42:01,200 --> 00:42:06,360 equal to true. Actually, we don't need to  add this equals equals true thing. It does the exact 488 00:42:06,360 --> 00:42:11,400 same thing without it. But, again, it's up to you  - maybe it's easier to read this with the full code written out. 489 00:42:11,400 --> 00:42:17,040 Anyway, now, the bird won't flap if it's  dead, which seems quite logical to me. 490 00:42:17,040 --> 00:42:24,360 The final thing to do is to build the game. Which is really easy. Pick file, build settings, and build. Pick a folder on 491 00:42:24,360 --> 00:42:31,500 your hard drive. And let Unity do its work. Then you  can open this file to play your game! Amazing. 492 00:42:31,500 --> 00:42:35,400 In a very short period of time, we  have made a pretty functional game. 493 00:42:35,400 --> 00:42:39,960 And what’s more, we’ve learned loads  of fundamental lessons about Unity. 494 00:42:39,960 --> 00:42:43,980 We have made a character that moves  in response to our input. We have 495 00:42:43,980 --> 00:42:48,660 spawned in new objects on a timer. We  have created a UI that shows a score, 496 00:42:48,660 --> 00:42:53,700 and made that score tick up when conditions are  met. And we've got the ability to get a game over, 497 00:42:53,700 --> 00:42:55,573 and start again. 498 00:42:55,573 --> 00:43:00,060 Now, I should note that there are different - and  perhaps better ways to do pretty much everything 499 00:43:00,060 --> 00:43:05,220 in this tutorial. For example - I used Unity's  old way of checking for inputs, and the company 500 00:43:05,220 --> 00:43:10,440 has since developed a much, much better Input  System. But it's a lot more complicated to 501 00:43:10,440 --> 00:43:15,660 use - so this simple method is great for now, and  you can look into the new input system later down 502 00:43:15,660 --> 00:43:21,420 the line, when you feel more confident. That's how  it went for me. There's also TextMeshPro, which 503 00:43:21,420 --> 00:43:27,559 has replaced the old legacy UI system - so you'll  want to graduate to that, at some point, as well. 504 00:43:27,559 --> 00:43:31,674 Anyway, these are lessons that will be useful, for making all sorts of games. 505 00:43:31,674 --> 00:43:37,260 But... the game isn't quite finished yet.  There's still a few more things to figure out. Though, 506 00:43:37,260 --> 00:43:41,160 I don't want to tell you how to do everything.  So i'm gonna give you some suggestions for how 507 00:43:41,160 --> 00:43:43,860 to finish up the game, but I want you  to try and figure it out for yourself. 508 00:43:43,860 --> 00:43:48,360 So first of all, we need to have a game  over if the bird goes off the screen. 509 00:43:48,360 --> 00:43:51,900 That shouldn't be too hard. There's  also a bug where the score can go up, 510 00:43:51,900 --> 00:43:55,020 even after a game over. Try to solve that one too. 511 00:43:55,020 --> 00:44:00,300 We also want sound effects. I want you to add  an Audio Source component to the logic manager. 512 00:44:00,300 --> 00:44:05,700 fill it with a sound effect file. Reference it on the  script. And have it play when the score goes up. 513 00:44:05,700 --> 00:44:11,220 Then, i want you to play around with the particle  system to make clouds appear in the game. 514 00:44:11,220 --> 00:44:15,600 Next, open the animation window, and  add some flapping wings to the bird. 515 00:44:15,600 --> 00:44:19,320 Then i want you to add another  scene to make a title screen, 516 00:44:19,320 --> 00:44:22,800 so the game doesn't immediately  launch into the action. Here's a clue: 517 00:44:22,800 --> 00:44:25,740 you'll need to add this new scene  to the build settings window. 518 00:44:25,740 --> 00:44:30,540 And finally, if you want a real  challenge - use PlayerPrefs 519 00:44:30,540 --> 00:44:35,400 to save the player's high score to the hard  drive, and draw that on the UI as well. 520 00:44:35,400 --> 00:44:40,620 For each one of these, you will probably want to  Google the relevant terms, read the Unity docs, 521 00:44:40,620 --> 00:44:45,120 watch some quick tutorial videos, or  ask for help in the comments down below. 522 00:44:45,120 --> 00:44:48,840 Next, you could expand on  Flappy Bird. Get creative 523 00:44:48,840 --> 00:44:53,280 and add in ideas or designs that weren't  there in the original iPhone game. 524 00:44:53,280 --> 00:44:58,320 For example, with a little messing around I gave  the bird the ability to shoot out a missile, 525 00:44:58,320 --> 00:45:03,480 and then I added targets to the pipes. You've  now got to hit the target with a missile to open 526 00:45:03,480 --> 00:45:07,500 a gap you can flap through. It's pretty cool,  and adds a lot more depth to the simple game. 527 00:45:07,500 --> 00:45:11,580 In fact, I'd love to see how you  might expand on the original game. If 528 00:45:11,580 --> 00:45:15,060 you make something interesting, record  a bit of footage, pop it on YouTube, 529 00:45:15,060 --> 00:45:19,200 and drop a link in the comments. I might  feature some of them in the future. 530 00:45:19,200 --> 00:45:20,940 And then, finally, I'd recommend that you take 531 00:45:20,940 --> 00:45:25,680 another simple game and try to  remake it in Unity, like we just did right now. 532 00:45:25,680 --> 00:45:30,600 This is a great technique because you don't have  to worry about art or design... 533 00:45:30,600 --> 00:45:35,223 just code. And the problem-solving puzzles you'll face are a perfect example of 534 00:45:35,223 --> 00:45:38,879 what real game development will be like. 535 00:45:38,879 --> 00:45:46,366 Good candidates for this include Pong, Space  Invaders, Breakout, Pop the Lock, Angry Birds, 536 00:45:46,366 --> 00:45:52,279 various WarioWare mini games, and that dinosaur game  that plays in Chrome if your internet's broken. 537 00:45:52,279 --> 00:45:54,540 So, in this video I wanted to teach you the 538 00:45:54,540 --> 00:45:58,680 fundamental concepts behind Unity  - but, the rest is up to you. 539 00:45:58,680 --> 00:46:04,380 Luckily, I reckon this sort of hands-on,  self-directed, learn from your mistakes style of 540 00:46:04,380 --> 00:46:10,380 learning is the most fun and effective way  to make stuff stick. But we’ll see! 541 00:46:10,380 --> 00:46:14,040 Let me know how you got on in the  comments down below. And if you 542 00:46:14,040 --> 00:46:17,460 want to watch my game development  story - which is still ongoing, 543 00:46:17,460 --> 00:46:22,140 promise - then click here,  for episode one of Developing. 544 00:46:22,140 --> 00:46:25,440 Thanks very much to my Patrons - they're  the reason you don't get mid-roll 545 00:46:25,440 --> 00:46:30,540 ads in a looong video like this one. You  can help support GMTK at Patreon.com.