How to make Space Invaders in Unity (Complete Tutorial) πŸ‘ΎπŸ›Έ

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello my name is adam in this video we are going to learn how to recreate the game space invaders in unity space invaders was developed in 1978 and was the first fixed shooter that set the template for the shoot em up genre the goal is to defeat wave after wave of descending aliens with a horizontally moving laser to earn as many points as possible this is the fourth video in my series of recreating classic arcade games in unity so far we have done snake pong asteroids and now space invaders subscribe to the channel to be notified whenever i release a new tutorial let's go ahead and jump right in let's begin by creating a new project using the unity hub in the top right we can select the blue drop down to choose the specific version of unity we want to use so in this case i'm going to be using unity 2020.2 from here let's select the 2d template since this will be a 2d game and i'm going to call this space invaders and choose wherever you would like to save your project and go ahead and click the create button from here it might take a couple minutes to initialize so give it a second and we'll pick it up as soon as it finishes first we're going to want to set up our entire scene this includes creating various game objects and adding some components to those game objects so we can actually start scripting them but first i'm actually going to pull in a bunch of spreads that i have already created and we'll use all of these sprites throughout our entire project let me go ahead and create a new folder and i will call this sprites let me drag in the sprites that i already have created you can use all of these sprites alongside me they will all be available to download through github with the rest of the project as well there's a link in the description of the video that contains all of these but also if you want feel free to create your own sprites first we're going to want to change a few settings on some of these sprites or all of these spreads actually so let's select all of them and let's change first pixels per unit from 100 to 16. 100 is going to be too large i designed all of these sprites around 16 pixels per unit next we're going to want to change our filter mode to point and finally we can change our max size to 64 and our compression to none and this will make sure the pixel graphics actually shine through and they're not getting filtered or anything like that and that's it for our sprites now we can continue setting up the rest of our scene unity by default provides us our sample scene here i'm going to actually just rename this to space invaders and reload the scene in the left here is our hierarchy where we can see all of the game objects that exist in this scene and once again by default unity provides us one game object which is our camera since this already exists this will be the first object we change really all i want to change is the color to black and i'm also going to change the size here from 5 to around 15. what that's going to do is effectively zoom out so for example let me just throw a sprite into our scene you can see a little invader there and at the size of five it's very large it's too large for our entire scene so by setting our camera to 15 we're effectively zooming out and it's giving us more space to work with let me go ahead and delete that invader the other thing i want to do right now is actually just change our resolution for the game so here in the drop down we can go to um we can click the plus button to add a new one and we can set a custom resolution i'm just using a resolution of one by one so you could type in aspect ratio you could say one to one i already have it created here so i'll select that and this will make it a square i also can uncheck uncheck the low resolution aspect ratio here to make it fill the entire space and that looks a lot better next let's add our player to the scene so we go to our sprites folder we have our player sprite here and i can drag this directly into the hierarchy for it to show up in our scene this will automatically create a game object add a sprite render component to that game object and assign the correct sprite from here i want to change the color from white to green looks good and let's move it towards the bottom of the screen let's say negative 13 looks good there's a little bit of space between the bottom edge and where the player is i also want to add one more component to this object which is going to be our box collider 2d here the non-2d is intended for 3d games so since this is a 2d game we will choose box letter 2d and i want to change one property which is to set this to be a trigger all of our objects in the this entire game will be marked as trigger colliders to simplify our code later on also you'll notice that the size is automatically set to fit this sprite exactly if i change the size here you can see the green outline which indicates the area of this collider and of course as i mentioned by default it automatically sizes it to our sprite as long as you add your box collider after you have already added your sprite so that's exactly what we want we'll keep that how it is next let's add our bunkers to our scene as well so the process will look exactly the same as what we did for our player let's find our bunker sprite and let's drag that into our hierarchy let's change the color from white to green the same green that our player is and let's position this towards the bottom of the screen so let's say negative 10 looks good there's a little bit of space between the player and the bunker let's go ahead and add our box collider as well let's mark that as a trigger as i mentioned every object will be marked as a trigger and you can see the size has been set automatically for us to match the size of our sprite and that will be it for our bunker but we actually need four of these so let's go ahead and duplicate these you can right click and select duplicate and we have four of them and let's position them now so let's say there's maybe six units of space in between each one so we can do negative three and then negative nine and we'll do positive three and then we'll do positive nine to space them out pretty evenly and that looks good there's our four bunkers i'm just going to rename them so they all have the exact same name as bunker and that's it next let's add our mystery ship object to the scene so just like the others let's find our mystery ship sprite let's drag it into the hierarchy let's change the color to red for this one and let's position it towards the top of the screen and i'll just make this pretty much the exact opposite of the player the player was negative 13 so i'll make this one positive 13. let's also add our box collider component box collider 2d let's mark it as a trigger and the size is already set for us so that's all we need to do for our mystery ship finally we want to create two invisible boundaries that will surround our scene this will just be a simple game object with a box collider component let's go ahead and right click in our hierarchy select create empty let's reset our transform here so all the values are at their defaults and let's add our box collider 2d let's mark this as a trigger i will call this boundary as well let's switch over to our scene view so we can actually see this you can see right in the middle is our box glider let's scale this so it is exactly the same width as our scene so it looks like 30 is good and that's positioned at the very top right on the edge of our scene doesn't have to be perfect but i will go ahead and round this let's say 15.5 and let's duplicate this and let's move this now to the bottom as well negative 15.5 that looks good so what these two boundaries are going to do is catch any laser shots or missiles from the player and invasion invaders so for example when the player shoots a laser let's say the player completely misses we don't want that laser to go off the screen and out of bounds and then just move on forever so this collider will make sure that that laser still gets destroyed even when you miss and then same thing for the missiles that are shot by our invaders going downwards next we can start forming our grid of invaders if we wanted to we could just manually place each individual invader in our scene but i would prefer to write a script that will do it for us and then it'll also give us more control over customizing that grid if we want maybe a different amount of rows or columns but before we can write that script we need to set up a couple prefabs so let's go ahead and do that let's create a new empty game object in our scene so we can right click on our hierarchy suck create empty i'm going to reset this game object here or the transform i mean so all the default all the values get set to their defaults i'm going to call this invader actually i'm going to call this invader base and i'll explain why in a minute and let's add the couple components we're going to need so one's a sprite renderer and we're also going to want to add our box glider 2d now in this case we don't have a sprite yet because there's multiple sprites for us to choose from and we're going to utilize something called prefab variants that allow us to set a different sprite but keep the rest of the properties and components the same so given this game object here we can create our base prefab so let me create a new folder called prefabs and all we need to do to create a prefab is drag this into our folder down here so now we have a prefab called invader base a prefab is a custom type of asset that we can re-utilize throughout our entire project it'll also allow us to instantiate new instances of this game object through our script later on so that's just our base prefab but we want to create a variant of this that has a specific um invader sprite attached to it as well as the size of our collider and so on so for example maybe by default this is just one but this will get overridden based on the sprite that gets set so let's say here let's say here we want to assign a our specific invader this time so let's say invader one can drag that into our sprite there we can see it show up in the scene and let's reset our box collider 2d so the size gets um updated in this case the size actually was fine but for some of our other sprites the size will be slightly different so now that we've made a couple changes here we can revert that you can see on the left here anytime you make a change of something you'll see a little blue to indicate that property has been changed compared to what the prefab is currently set to so all that's been changed on this particular object is our sprite here now if we drag this again into our um into our project we can our project folder we can create a different prefab that's a variant of the base let me first rename this i'm going to call this one invader one so i'll drag this in and it's going to ask us would you like to create a new original prefab or a variant of the prefab that i'm already using and we're going to create a variant and so now we have vader a1 variant i'm just going to get rid of the variant there and just have vader a one and notice how we can actually see the sprite show up there too let's do this again now this time let's delete that let's start with our base once again and let's assign a different sprite here so let's assign number two and let's reset our box collider to see if the size change so yeah in this case the size actually did change let's revert our trigger we want to keep that how it was but our size and our sprite are the differences in this particular game object so let's do this once again let's drag this into our project folder let's create a prefab variant i forgot to rename it but that's okay we can rename it right here so i'll just call this invader two it's gonna actually rename the game object for us as well and one final time let's do this for a third one so we start with our base so i drag that into our scene and now i'm going to assign the correct sprite so the third one here and you might be wondering why there is actually six different sprites later on we're going to animate these invaders so there's really two different sprites per invader so for now i'm just selecting the first one um for each but so invader three let's reset our box clutter so the size gets automatically updated to fit that particular sprite let's revert or is trigger so that stays the same and once again you can see the only changes on this object are sprite and our size and we can drag this in one final time credit proof app variant let's name this one vader03 so now we have our different invaders which later on we can write a script to lay them out in a grid i'm going to get rid of the base there from our scene and at any point i could drag in different um different invaders into my scene or if we want to write a script to do it we can do that as well let's write our first script to animate our invader sprites let's go ahead and first create a new folder to contain all of our scripts let's go ahead and open that up right click create c-sharp script i'm just going to call this invader let's open this up and unity by default provides a class called that inherits from monobehavior there's also going to be a couple of functions that are just very commonly used but i actually like to kind of just delete everything and start from scratch it's just my preference so to animate our invader sprites we're going to need to keep track of an array of sprites that we want to cycle between and so let's create a variable for that i'm just going to call this animation sprites this is an array an array of sprites it's public so we can actually change it in the editor which we'll be able to see in a minute we're also going to want a variable for the animation time which is essentially how often does it cycle to the next sprite and then we're going to need a reference to our sprite renderer so we can actually change which sprite is currently being rendered so let's just call a sprite renderer and we also need one other variable to keep track of which you know sprite we're currently using which is just going to be an index within our array so this will be an integer and we'll just call this animation frame okay so now first let's establish our reference here to our sprite render so we'll do this inside our awake function which will be the very first function that gets called for the life cycle of the script and unity will call this for us automatically and so we can assign our sprite renderer by you saying git component and then we specify the type of component we want to get and that's it and so what this is going to do is it's going to look on the same game object that our invader script is attached to it's going to look for the component that we specified which in this case is sprite renderer and when our script starts executing so the start gets called the very first frame this script is enabled on an active game object so on that very first frame we want to start our animation loop basically and we can do something called invoke repeating so invoke is allowing us to invoke a method after some amount of seconds and then repeating is that it's going to repeat that over and over and over again so we need to provide a method to actually invoke so let's actually create that first and i'm just going to call this animate sprite and so in our invoke repeating we can reference that method here and then we need to provide the how often it's going to repeat this and there's the initial time like the initial first time this gets called like how many seconds and then from that point on how many seconds every time after um and we're just gonna use our single variable animation time for both of those okay so once again basically what this is going to do is it's going to call our animate sprite every you know x amount of seconds whatever is provided here and let's say by default this has a time of one second we can change this in the editor because we've marked it as public but one second should be pretty sufficient so within here anytime we say animate sprite we're basically going to update um which frame we're on so we're going to say animation frame plus plus to increment it to the next one and then we need to check to make sure that our current frame does not exceed however many sprites we actually have provided if you're exceeding how many sprites you've provided well then we should loop back to the very beginning again so we can say if animation frame is greater than or equal to however many sprites we have so dot length well then we will simply set that back oops let me fix that [Music] we will simply set that back to zero to start over again and then from here all we need to do is update which sprite we're rendering so we say sprite render dot sprite equals animation sprites and then we're going to get the element at the index here which is our animation frame and that should be it for um that should be it for animating our invaders let's go ahead and add our script to our invader prefabs here and really all we need to do is add it to the base prefab and it will automatically add that to the variants that we've created so i have my base prefab selected let's drag our script into the empty space there here you can see by default you know there's zero sprites being provided our animation time is one and now we can set the individual sprites on each of these variants and notice once again i only added this script to our base proof fab but because we created these as variants of the base prefab these other prefabs also get that script and so for each one of these let's go ahead and assign our two sprites let's do this one we're gonna do one and two if you are using the same sprites as me once again they're all available to download for free in github with the rest of the project as well um if you're using your own sprites then you'll feel free to assign these and you can have more than two you can have as many as you want really uh and then finally our third one here oops two okay that should be good let me double check yep looks good okay and just for the sake of testing this out let me bring all of these into our scene here let me just spread them out so i can see them all let's go ahead and play our game real quick and we should see these updating yep so every second you can see the sprite kind of change to the next one and then it starts over again there we go so that's it that's all we need to do for animating our sprites now that we've set up and animated our individual invaders let's finally form our grid of invaders so first let's create a new script and i'm going to just call this invaders plural and let's also create a game object to go along with that so this will be an empty game object we'll call it invaders let's reset the transform so it's at the origin which is the middle of our screen let's add our script to that game object so this game object will act as appearance which all the individual invaders will be attached to let's go ahead and edit our script here and let's add a couple variables so we're forming a grid and a grid you can think of as rows and columns so let's have two variables rows and columns so this is the number of rows and the number of columns by default space emitters has 5 rows and 11 columns but feel free to set those to be whatever you'd want we're making these public variables so we can change them in the editor if we want to and we also want to have an array of prefabs that will correspond to each row within our grid so each row instantiates a different prefab so we can set up an array of invaders here and we'll call this prefabs and then finally in our awake function we can perform all of the actual logic so let's start by just creating our 2d for loop or our you know our nested for loop to loop over each row and column so we can say something like four inch row equals zero so it'll start at zero row is less than however many rows we have and then row will get incremented then same exact thing for columns we'll say col equals zero column is less than however many columns we have and column plus plus so pretty standard for loops there and within here we can basically just instantiate each one of our invaders and then we need to provide a prefab that we want to clone from so we the prefab will just be whatever row we're currently in so once again this array of prefabs corresponds to each of the rows so we can just say row and then we can provide a second argument here which is going to be the transform we want to parent this invader to which is this transform our our invaders as a whole and that's it and we can even store the result of this into a variable here so let's go ahead and take a look at what this does there's definitely more we still need to do but let's run this only first we need to assign our prefabs here so let's set the size to be five because there's five rows let's go ahead and um drag in our different prefabs to these spots so element zero will actually be the bottom row and the last element will be the top row normally there's two of these at the bottom and there's two of this middle one and then the top row just has one one row of those okay let's run that one more time so we can see all of our invaders here are stacked on top of each other that's because we haven't yet set their positions but you can see all of the invaders being cloned here even have the word clone in their name because we're instantiating them from the prefab and they're all being parented to this game object here and the reason for that is because we're eventually going to change the position of our invaders as a whole and so instead of having to change the position of each one individually all we'll have to do is change the position of the parent here okay now let's actually do the math to set their positions so we're going to need to know the position of the row and then from the position of the row we can offset that based on the the column so let's establish a variable here called row position and i'm actually going to come back to this i want to kind of just lay the outline of our code first and then we'll actually assign a value to this but after we instantiate our invader we will set our position so initially the position of our invader will be whatever the row position is and once we have our row position we can offset that by whatever our column is so the columns refer to the x-axis and the rows refer to the y-axis so for our column here we're going to change the x say plus equals and then basically we just need to say whatever column we're currently on and then multiply that by some amount of spacing so let's say there's one unit of spacing well so let's say each individual invader is one unit and then let's say there's an extra unit of spacing or padding in between the invaders so for a total of two spacing we can hard code that value now we could actually turn this into a variable so we can change it in the editor but just for the sake of simplicity i'm just going to hard code that so column times spacing and then finally we can set our invader's transform position to be that position okay now we need to do our row position here this one's going to be a little bit more involved but initially here it's going to look the same i mentioned earlier that the x or the columns refer to the x-axis and the rows refer to the y-axis so the y here is the main thing we need to set it's really the same thing it's whatever row we're on times some amount of spacing once again and so you know what's the you know we can say the size is the size of each invader is one unit and let's also say there's one unit of padding in this axis as well so we'll just use two just like we did for that and that's it really there will be more we need to do that due to this but let's just see what this looks like for now so there's our grid of invaders they're all being animated looks good now notice how as a whole they are not centered correctly so that's what we want to set our x-axis here for the row position we will kind of want to apply an offset so everything becomes centered so let's say we have a variable here called centering and our centering is basically needs to be we need to subtract half of the total size of our grid all right so let's say our total the total size of our grid in the width would be our spacing 2 that we've specified times the total amount of columns minus one this that comes minus one and then the same thing for the total height of our grid it's whatever spacing we specify two and then the total amount of rows minus one so this gives us the total width and height of our grid and then from here our centering is just going to be subtracting half of that because the center point should be you know the center it's halfway from one you know halfway from each edge of our grid so that's why it's half so new vector three or we can this can actually be a vector two it only needs two axises so with or once again we're supposed to subtract so it should be negative width divided by two and then negative height divided by two so that's our centering and then we just need to simply apply that to our row position so our x here becomes centering that x and then our y will become centering dot y plus the position of the actual row so that logic still remains [Music] and that should be it let's go ahead and test it out okay there we go so our grid looks centered perfectly within our screen now and we can even go to our scene view and you can see the very middle invader the very center invader is literally at our origin of our entire scene so our grid is completely centered and everything now normally our grid should start off a little bit higher but that's where we can just simply change the position of our parent game object and so for our parent game object we can change it and then it'll move them as a whole now i want to make sure this isn't running so this value gets saved so let's say it has a y position of seven let's rerun it um seven there oh you know what i made one mistake in our code i made one mistake and that we want to set the local position not the world position notice how i changed the parent game object here i know i could set this to some really large number but it doesn't do anything at all and that's because we're setting the world position of each our each of our invaders but if we set this to be a local position it will now be relative to our parent here so let's try that again let me set this back to what the value i thought it should be seven let's play it again and notice how now they're spawning at the top i could change this to you know two it'll just be slightly above the center but yeah that value of seven works well they'll spawn near the top of the screen and they'll slowly work their way down so that looks good let's focus on making our invaders move now let's open up our invader script again and let's continue expanding upon this to get them to move all we need to do is update this single transform for the parent object here instead of having to update each individual invader so we can do that inside of our update function update is called every single frame the game is running and this is where you very commonly do things such as movement or input things like that so here to update our transform's position our invader's position we need to essentially know the direction they're moving multiply that by speed and that's really it so let's add a variable for direction and our direction will be a vector three and let's say initially they move to the right and that's also add a variable for speed and let's just set this to maybe 10 for now we're going to end up changing this but let's just throw that in for now and so our position here we can plus equal direction times speed and we also want to multiply by time that delta time to account for frame rate this will make sure our movement is consistent regardless of the frame rate of our game and at the gist that's it but of course there's going to be a lot more we need to do to this but let's test it out you can see they're moving to the right it's definitely way too fast that's okay i can change that to one and actually i'll have to change it here in the editor so let's set that to one for now the other thing we need to do is when it touches the edge of the screen when any of the invaders hit the edge of the screen it needs to flip the direction so instead of going right it should then go left and then vice versa should continue to flip back and forth every time it hits the edge of the screen so how can we do that well we need a way to check each individual invaders position so we can say for each transform and then we can give this a name let's call it invader in this transform so this is a way of looping through every child objects or every child transform that's parented to this one so for each invader in our transform we can perform some logic so we can check is this invader has this invader touch the edge of the screen if so we'll flip the direction of the entire thing one thing we'll want to first do though is check that our invader has not been disabled so later on in our game when you shoot an evaser with your laser that invader will get deactivated and so we don't want to check for any of this for any deactivated invaders right now it's not going to matter but later on it will so for this we can just say if invader.gameobject dot active in hierarchy is a way of knowing whether this game object is active in the hierarchy the hierarchy being you know in the unity editor you can see your hierarchy on the left so if or we should say if it's not active then we can just continue on to the next one so this will just jump to the whatever the next one is in their transform and then from here we need to check the edge of the screen to see if the transform or the position of the evader hits the edge so and then we need to know is it the right edge or the left edge so first we should say if our direction is moving to the right well then we need to check the position of the invader on the right side of the screen so if the position is greater than or equal to whatever that right edge is then we know they've hit and we should have them flip direction now the question is what is the value here so there's a little bit of calculations we need to do but luckily unity provides the functions to do it for us so let's go ahead and create some variables outside of our for each loop let's call one left edge and what we're going to do is say camera dot main view port to world point world point what this is going to do is translate the a some viewport coordinates into world space coordinates so the viewport here is just for the the viewport for our left edge is just simply going to be zero and then the viewport um coordinates for our right edge will be one let me finish writing this first so this is going to be um [Music] one in the x axis which a shorthand way of writing that is to say vector three dot right and you can see if i hover over this you can see the x axis here is set to one so that's our left edge of the screen and this is the right edge of our screen so just kind of a simple way to get those world space coordinates using the some of these transform functions that unity provides to us and so now we can say okay if the invader position is greater than or equal to our left or our right edge dot x then we know we need to flip the direction really we're going to actually do more than just flip the direction we want to flip the direction and we want to advance the invaders to the next row so they're going to go down the screen as well so maybe we have a new function called advanced row and in there we can flip the direction direction.x times equals negative one which will negate it or just you know if it's positive it'll go negative if it's negative it'll go positive and then we want to move our invaders down so let's take their current position this how transformed that position and we can just simply update the y here to be one unit less which will move it to the next row and then we can assign this back to our transform we'll call this inside our if statement um and then this should be if direction oh this should be vector 3 not vector two and then same thing in our left uh for our left edge so if the direction is left and the invader position dot x is less than or equal to the left edge well then we'll go ahead and also advance row now one small thing we should probably do here is add a little bit of padding between the edge and where the invader hits so we can say right edge minus one and then left edge plus one just to give it a little bit of padding so it doesn't we make sure our invaders don't clip off of the edge of the screen and let's go ahead and try this out i think that should be good so we'll wait till they hit the edge of the screen and hopefully they should move down yep they just move down and they switch directions so now they're going to the left let's see if they go back to the right in a moment here as well and they do awesome so that is working perfectly now one thing we're going to do not now but later is change the speed of our invaders based on how many invaders are currently alive so normally in the game of space invaders the less invaders that are alive the faster they move so in other words they move pretty slow at first but as the game progresses they go faster and faster as you start killing them but we will worry about that a little bit later next let's shift our focus over to the player so let's go ahead and handle player movement and shooting let's create our new script called player let's go ahead and drag that onto our player game object right away and let's edit this script so first for movement we're going to want a speed variable of course let's add that say speed maybe the default value is five and inside of our update function we can change the position of our player based on that speed but we need to check for input so we can say if input dot get key from here you could say keycode.you know maybe a or you know if you're using wasd or we could say input.getkey code dot left arrow if you want to also use the arrow keys well then we will move our transform position plus equals our direction times the speed right just like we did for our invaders so direction will be left times our speed and we're also going to times by delta time to account for frame rate the important thing here is to use get key versus get key down the difference there is get key is going to return true every single frame you're pressing that key down whereas get key down will only return true the first frame you're pressing it down and so if we want to be able to consistently move for as long as you're holding that down then we should use get key and we should do the opposite direction as well so let's add get key keycode.d or input getkey keycode.right arrow there keycode.rightarrow and in this case we'll update our transform vector three dot right times speed times time dot delta time and that should be it for player movement let's go ahead and test it out here we go i'm pressing left and right and i can move left and right perfect it's really simple and we can always adjust that speed if we need to let's move right on to our laser now so let's say when the player presses a different input we will shoot our laser so we can say if input get key and in this case i'm going to use get key down because you should only have to press the button once to shoot and then you have to press it again in order to shoot again so get key down keycode dot space and we can also do maybe our left click of our mouse button so we can say get mouse button down and we provide an index here which represents which mouse button in case in this case zero is our left click and we can say we can call some new function maybe we call a shoot right so when you press that input down we will shoot inside here we need to instantiate a new laser and then from there the laser will move oh we actually need an entirely different script to handle that so let's create a new script and i'm going to call this projectile i'm specifically going to call it that and not laser because we're going to reuse the same projectile script for both our lasers and for the missiles that invaders shoot so in our projectile script let's delete everything and just like we've done for some of our other transforms we're gonna update its position based on direction and speed so let's add variables for that direction and let's add a variable for speed and in this case we're making our direction a public variable because we want to be able to set that in our editor because we're going to set the direction to be different for our lasers it'll be up and for our missiles it'll be down and same thing speed will probably be different between those as well within our update function we can just simply say this now transform that position plus equals this dot direction time speed times delta time to account for frame rate that's really it for our projectile script now if we go back to our player inside our shoot function we need to instantiate a new projectile but we're going to need a prefab that contains that projectile script on it so let's set that up and we'll call this laser prefab and we'll go ahead and set that up in the editor in a minute but let's finish writing our script so when we shoot we will instantiate a new instance of our prefab the position should be the position of where you're shooting from so since this is the player that's shooting we want to use our player's position initially and rotation shouldn't really matter so i should be able to just set this to identity which is basically no rotation and that's really it we can test this out but of course we'll need to set up our prefab so let's create our laser prefab if we go to our sprites we can drag in our laser into our scene so there's a little laser there i'm going to make this green to match the player so the user knows um it is the player's laser and this is going to need a box collider 2d let's mark that as a trigger the size gets set for us that looks good and then finally we can drag this into our prefabs folder here and now we have our laser let's go ahead and assign this to our player oh let's delete it from our scene let's switch over to our player now you can see the prefab property shows up here and we can drag in our new prefab to that slot oh it doesn't let us because it doesn't have a projectile script on us and our prefab is expecting that script to be there so let's go ahead and add our projectile script to our prefab and for our laser we want to set the direction so in our case the direction will be up which is just simply a value of 1 in the y axis let's say maybe our speed is maybe 30 we can play around with that let's go back to our player and now we can drag this in there we go let's run our game and see what happens so i can shoot looks good there's no collision detection yet but that's okay we'll worry about that soon that looks good now one thing that is a problem is in space invaders you're supposed to only be able to shoot one bullet at a time or one laser at a time but there should only be one laser active in the scene at a given time so how can we go ahead and set that up in our player script we can create a new variable here that keeps track of whether our laser is active so it'll be a boolean which will be a true or false laser active and so we only want to shoot if our laser is not active so only instantiate a new laser prefab if our laser is currently not active and from this point um after you do instantiate a laser we want to set that to be true now the problem is we're never setting this back to false so how can we determine when this object actually gets destroyed and then we can shoot a new one so in our projectile we can check for collision so we can say on trigger enter 2d glider 2d and we'll call this other so this is a function unity is going to call for us automatically because we added a collider component to this object this will get triggered for us and so anytime the projectile collides with anything we should just destroy the projectile right away so we can say destroy this.game object and we might end up doing some additional logic in here later but at the very minimum we want to make sure when our projectile is destroyed or yeah when our projectile collides with something it gets destroyed we also need to inform our player though that that projectile was destroyed so we need some kind of callback or some way to inform the player that that happened so what we could do is add an action here um which is a c specifically a c sharp thing so this is using like c sharps del um delegate pattern so we'll say actually this will be system.action and let's say this is destroyed right that'll be the name of our callback and so before we actually destroy our object we will first call that callback so we'll say this.destroyed invoke and this is a way to allow other scripts to sort of register when some event happens so you can think of this as an event and then this is where we're actually invoking that event to say this event is happening right now and so when we actually create our projectile first we need to grab a reference to it when we actually create our projectile here we can subscribe to that event essentially that's one way you can think about it you can say projectile.destroyed and we can add a function we want to call whenever this gets invoked so let's say there's a function called laser destroyed and we can say laser destroyed here so we're saying we want this function to be called when this delegate gets invoked and that delegate will get invoked when it collides with something and then of course when the object will get destroyed as well so when the laser is destroyed now we can set our boolean back to false which will then allow us to shoot again let's go ahead and test that out we shoot we shoot there we go so i shot once and i can't shoot again it's not letting me shoot again you can see on the left here our laser is still here it actually hasn't been destroyed yet it and you can see it's its position is still moving and so it's it's still flying through space we just don't see it anymore because it's way off the screen so we might be wondering why did it not actually it's supposed to collide in this case it should have collided with you know let's say this top boundary up here right in this case it didn't and that's because our object is missing a rigid body in order for collisions to happen unity's physics engine is going to assume unity physics engine will simulate all collisions so to have those collisions happen we need at least one of those objects to be a physics object to make something a physics object we need to add a rigid body to it so we can add a rigid body 2d component to this i'm going to drag it up here usually i like to have it but the order doesn't actually matter but i like to have it rigid body then the collider and by simply adding this component this object is now a physics object which means physics will be simulated on this object including collisions now the problem is we don't want the movement of this object to be stimulated through the physics engine we just simply want the collisions so we can change the body type from dynamic to kinematic and that'll collapse a bunch of those properties those physics properties that we don't really care about so this will allow collisions to still happen but without actually simulating movement because we're handling movement ourselves in our script let's go ahead and test this out one more time and see if it works this time so you might see you might have saw that it showed up and then it disappears right away and that's because our laser is colliding with our player so we don't want our laser to collide with the player so how could we change that well one way is we can use different layers here and unity provides a way to tell different layers if they can or can't collide with one another so let's go ahead and add some new layers here i'm gonna go to layer add layer and we'll add a layer for our player let's add a layer for our i'm gonna add several here even though we're really only care about the player and the laser i'm going to add all of them we're going to need for our entire project let's have player invader let's also have laser and let's have missile so those are four layers you can kind of imagine the player and the laser go together so we're going to turn off collisions between those two layers and we're going to also turn off collisions between our invaders and our missiles missiles because we don't want the missiles they shoot to collide with one another either so now that we have those layers we can actually set this on our laser so that's set let's go to our player and let's also set our layer on our player player let's go to our invader base and let's set the layer on that so that's our base prefab we don't have the missiles yet so we'll worry about that later and then from here if we go to edit project settings you should get a new window i'm going to drag this over here on the side we want to look for physics 2d you can see at the very bottom is a collision matrix so this is what's indicating what layers can collide with what other layers so we want to turn off collision between our player and laser we also want to turn off collision between our invader and missile so now collisions cannot occur on objects that exist on both of those or if yeah if those if the objects that are collide have those two layers set they will not actually collide the physics engine just completely ignores it so you can see now i can properly shoot it collides with the bunker it collides with the enemies and let's make sure that we can shoot beyond the edge of the screen well not really it should get caught by the border up there that boundary we had created at the very beginning of our tutorial and it does you can see i shoot the laser gets created and then it gets destroyed once it hits that so everything is working perfectly let's handle the collision between our lasers and our invaders so we can actually kill the invaders when we shoot them let's go ahead and open up our invader script and let's add that on trigger enter function we had added for our projectile on trigger enter 2d it takes in one parameter here which is a glider 2d we'll call that other it's the other collider or game object you're colliding with so we need to verify that this other object is indeed a laser so one way we can do this is say if other game object that layer so if we if you remember we had set the layer on our laser to be called layer so we can actually just check that directly um we could say layer mask dot name to layer and the name is laser right so this is a way of guaranteeing that the object we're colliding with is the laser and when it is the laser that means we need to be killed this invader should be killed so one of the first things we want to do is actually set this object to be deactivated that way it disappears it stops rendering you know collisions stop happening on it it's going to completely turn off this game object and we also want to inform back to our invader scripts that this invader has been killed because the invader script is going to keep track of the total amount of invaders that have been killed the reason for this is well for one so we can know when all the invaders have been killed and we can start a new round and also in space invaders the invader's speed is supposed to be dependent upon the number of invaders alive so we'll use that variable to adjust our speed here so how can we inform the invaders as a whole that this particular invader has been killed well we can do the same thing we did for our projectile in our player where we have this action here that gets invoked whenever something happens so let's kind of set it up the same way in our invader here we'll add a new action we'll say system.action and we'll call this killed and so when our laser collides with the game object we will invoke that desktop killed dot invoke and now whoever registers with this delegate will will know when the invader was killed so our invaders since this is the script that's actually instantiating our invader we can go ahead and register our callback there so we can say invader.kill plus equals some function we want to call whenever the invader is actually killed let's add that function we can say on or we can just say invader killed right and we'll do something in there but let's go ahead and finish this invader killed oops vader killed there we go so once again this function will be called any time this delegate gets invoked and that delegate will get invoked whenever our invader collides with the laser so inside here what should we do well we want to keep track of how many have been killed in total let's add a variable for that and i'll actually make this um i'm going to make this half public half private um i'm going to say um invaders killed and we'll just say amount killed i'm going to make this public getter but a private setter because we might want to access this information from other scripts later on and then we're gonna also need to use this to calculate a percentage a percentage of how many invaders are still alive um so to calculate a percentage we just need to take this and divide it by the total amount of invaders so you might want to have a property here that calculates the total invaders for us and that's just going to be rows times columns in this case the the what i'm typing here is an equal sign followed by a right chevron or like a greater than symbol and this turns us into a computed property or calculated property so we can just say this rows times columns and then we can have this similar thing this should be a float actually which is you know something like percent alive or it should be percent killed maybe um and this we'll say this that amount killed divided by our total invaders so if a hundred of them have been killed and there's a hundred total meters and then that'll be 100 divided by 100 which is one or in percentages are often expressed between zero and one so one would actually indicate 100 percent so 100 have been killed in that situation so given these properties here oh there's one more problem because these are integers when we divide two integers it's just gonna then round down to zero right so be because this is supposed to be between zero and one we need to make sure these are floats here otherwise they're just going to get rounded down and we're not going to get the accurate value there so let's just cast those to be floats percent killed now we can factor this into our speed here because speed should be determined by however many however many invaders have been killed so instead of just having a flat value like this we're going to change this and we're going to use something called an animation curve it'll make a lot more sense in the editor where we can visualize what this looks like but it's basically just like an x y graph so the x axis of this graph indicates our percentage and our y-axis indicates the speed you want so let's say at a percentage of zero being killed maybe they move very slowly and at a percentage of 100 they'll move very fast right so let's get rid of that well that'll just be speed and here our speed becomes um this dot speed dot evaluate based on how the percentage killed so when we evaluate our graph we need to provide time obviously it's called animation curve so usually you use those in animation and animation is you know dealing with time but we don't have to use this specifically for animation we can we can use them just for interpolation and stuff um so in this case time you can think as our percentage so speed evaluate based on how many have been killed and that will give us the correct value now of course we need to assign we need to set up that graph in our editor so we have the right you know we have values that make sense for our game so let's go ahead and do that let's go to our invaders objects inspector if we click on our new speed property here you can see it brings up a graph and i can choose one of these preset ones you can have various graphs here and so for us we might want um we might want it so at times zero or at percentage zero our value is really small our speed is really small right so when all of them are alive so zero percent have been killed we want the speed to be very slow and the more you kill the faster they get all right so maybe add a percentage of 100 or one maybe the speed here will be like eight all right let me close this and reopen it so it readjusts and maybe i want to just change this a little bit here i can change this curve a little bit more so maybe it becomes more like this it's like almost exponential in a sense so it stays slow it almost stays pretty slow all the way up until you have 50 percent of them killed and then after 50 it starts getting faster like a lot faster compared to that first 50 but of course you can completely adjust this however you desire and this is one way you can change the difficulty of your game let's go ahead and test this out and see if everything's working so for one we should put a shoot they disappear great and we should be able to know um or we should be able to indicate or see that these are becoming faster the more we kill so let's go ahead and kill a bunch of these invaders and hopefully they get faster and faster the more that are killed it should start to get a little faster now of course i could always adjust that curve if i wanted to it doesn't seem like it's getting any faster so we might need to look at our code there definitely does not seem to be changing oh that's because we never went back to our function here and did anything so we forgot to implement that so when invader is killed when that function gets called we need to increment our variable here amount killed all we need to say this dot amount killed plus plus that's it that should solve our problem there just for the sake of testing i'm going to um change this so we can even change it while it's running let's say initially it has a value of 4 so they're a lot faster now right i can even set this back maybe to one but we should see this we should definitely see this get faster now as we kill these invaders like if i set this to be almost zero or what what we had it originally 0.25 that's 0.25 let's say maybe at um you know if we adjust this yeah so you can see how the speed is changing depending on how many are actually alive so that seems to be working we can of course adjust this to get it to our desired effect i might adjust it a little bit but overall that looks good let's handle the missile attacks from our invaders now open up that script again and what we're going to want to do is perform a function every x amount of seconds that will have a chance to spawn missiles so let's go ahead and add our start function here where we can invoke repeating some function that will happen every x amount of seconds so we need a function that we can invoke let's add a new one called missile attack so the name of that function there and then we need to invoke this every x amount of seconds so let's have a variable for that and we can say close missile attack rate so how often will there be missiles this that missile attack rate the stop missile attack rate okay so we will call our missile attack every x amount of seconds on repeat oh and let's say this has an initial value of one second maybe that should probably be fine so in here we want a chance to spawn a missile from each and every invader that is alive now if all of our invaders are alive we want that chance to be pretty low if the chance was high well then you know all 55 invaders would spawn a missile at the same time and that would not be good gameplay so it should be sort of an inverse relationship there where the more that are alive the um smaller chance there is to spawn on missile let's go ahead and set up our basic for loop where we can loop over each of the invaders the exact same thing we did in our update function so i'll go ahead and just copy that i'm also going to copy this first if statement which is checking if the invader is currently active if the invader's not active well that means they were killed if you remember we had set that invader to not be active whenever they get killed so if invaders um not alive they skip over that one and then from here we can check to see if they should actually spawn a missile and this will be a random chance so we can say random.value which generates a value between zero and one you can think of that as a percentage and if that is less than some chance percentage here well then we can spawn our missile so we want to say one divided by the amount of um the amount that have been or the amount that are alive actually so we don't have a variable for that let's go ahead and add that amount alive will actually be um this dot total invaders minus however many have been killed and let's go ahead and use that rare amount live so let me explain what's happening here so let's say there are 55 invaders alive which would be all of them it's going to be 1 divided by 55 which is going to be a really small number so the chance that this value here is going to be less than that really small number is going to be very very low and so it might occur for one of those 55 and that's actually what this is saying this is saying one out of 55 of those would have um would likely spawn in missile which is appropriate now as more and more die that chance is going to get lower and lower or higher and higher i guess you could say and depending on how you look at it and so it should balance out over time even as you kill different invaders so in here is where we can actually spawn our missile but we don't have that yet so let's go ahead and add a prefab for our missile we'll say and this will be a projectile so remember earlier i mentioned we're going to use that same projectile script we use for our laser as well as for our missiles so we'll call a special prefab into projectile using that prefab we can instantiate we can instantiate it an instance of it or a copy of it and the position here should be the position of our invader so we can say invader position and then the rotation shouldn't matter so we can just set that to identity and when this happens i want to guarantee that only one missile can be active at a time so i'm gonna break out as soon as we spawn one as soon as we spawn one we won't try to spawn anymore for this attack so it kind of guarantees only one missile can be shot at once but remember this is going to be repeated you know every second or however often we set that to so it's still going to there's still going to be plenty of missiles happening let's go ahead and set up our prefab in our scene it's pretty much going to be the same as our laser here but let's go ahead and use our misfiles sprite there's our sprite render let's add those same components as our laser rigid body 2d we want to change this to be kinematic that way we have collisions but we aren't simulating the movement let's add our box collider 2d set that to be a trigger let's add our projectile script we're going to change the direction to be down which would be negative 1 in the y-axis and let's say the speed is going to be 20. it'll be a little bit slower than the lasers i believe we set the lasers to 30. so given this object here oh and also we can set the layer we had added that previously so let's go ahead and do that given this game object now we can drag it into our prefabs folder and now we have our missile prefab we can delete it from our scene now go back to our invader script and we can assign that reference there and let's go ahead and try it out there's one boom now we got an error there so there's some kind of error happening but something i mean overall looks like it's working let's go ahead and debug what this error is um so projectile so it's saying this destroyed callback is not um is null it's not being set and so when we try to invoke it it causes an error that's because we're only using this destroyed for our player we're not using it for our invader so all we can do is just add a simple check here to make sure that our callback is not null and then we can invoke it otherwise if it is null or in other words if no one's actually using this then we won't invoke it and it won't cause that error either let's go ahead and test that again and there we go looks good and is let's make sure collision is happening yeah so it's colliding now properly it collides with me of course we don't have any logic yet to kill the player but we'll get to that and there we go so there is our missile attacks at this point we have an almost fully playable game of space invaders there's just a couple things we need we need to do to tie it all together so for the sake of time i'm not going to do a full game state for this tutorial so i'm not going to do scoring and lives and game over and ui and all of that because it would probably add in another hour onto the length of this video so i'm going to show you one very simple way we can kind of reset the entire game you know under certain conditions such as when the player wins or kills all the invaders or when the player dies we can just reset the entire game with pretty much one line of code so let's go ahead and take a look at how to do that so in our invader script in our callback of where an invader gets killed we can check to see how many invaders have been killed overall so we can check if all of them have been killed so if this that amount killed is greater than total invaders well that means you've killed them all and we can consider that a win condition now normally speaking you might just move on to the next round and it would kind of restart but you would maintain your score and stuff like that because we're not going to do score in this tutorial we're just going to reset the entire game and all we need to do to do this is one line of code but first we need to import some things we need to import unity engine.scene management and this is going to allow us to just reload our scene and when we reload our scene everything will be back to their initial states so we can say scene manager.loadscene and then we can say c manager.getactivescene and then we need to load it by the name of it so we'll say.name and that's it so as soon as all of the invaders have been killed we will just reload our entire scene which is like the simplest way you can restart your game now once again if you're going to handle additional things like scoring and stuff then you might not want to do this but for simplicity this is the easiest way to do it now we're going to want pretty much the same thing when our player dies and so our player can die in a couple of different ways let's go ahead and add some collision detection for that ontrigger enter 2d collision 2d other and so when the player collides with either a missile or the player collides with the invaders themselves um that will be considered a loose condition so we can check both of those if other game object.layer equals layer mask name to layer invader or other game object layer equals layer mask name to layer missile well then we will also consider that a game over and we'll just restart the game in the same way we did here so i can actually just copy that line of code paste that in let me import scene management and there we go so once again just a very simple way of resetting our entire game um let's go ahead and also let me make sure this is correct collision this is wrong this was supposed to be collider 2d not collision 2d collider 2d my mistake the only other thing we have missing is the collision between the invaders and the bunkers alright so when the invaders get farther enough down they should actually destroy the bunkers entirely so let's go ahead and add a script for bunker for our bunkers select all four of our bunkers and drag this script onto it and here we can check for collision between the bunker and the invader so we can say once again on trigger enter 2d glider 2d other if other game object layer equals layer mask names layer invader well then we can destroy our bunker and by destroy i'm just going to turn it off we could fully destroy it since we're just restarting our scene however if you end up refactoring this to add scoring and other things like that new rounds you probably don't want to destroy the bunkers entirely you probably just want to turn them off and then turn them back on whenever you reset but yeah so that should be good and let's go ahead and test out these different things we've just added so for one um let's test out when i get hit by a missile [Music] and for the collision to happen we gotta make sure we have a rigid body on our missile which we do at the moment so just double check that you do have a rigid body make sure it's set to be kinematic and that'll at least allow us to detect collisions between our game objects uh let's try to get i just need to get lucky here get a missile to fall oh i shouldn't move there we go so look so i got hit everything reset the invaders i'd killed are back to normal we're all good there let's test a couple other conditions i'm going to just for the sake of testing increase the speed here i want to see when they get all the way to the bottom and they eventually touch me i should also die and lose and the game should reset i'll also destroy the bunkers that's working correctly good and they touch me and the game resets so there we go and the other condition is when i kill all the invaders the game should reset now once again for the sake of testing i'm just going to set the number of um i'm just going to change them or rows and columns let's say there's one row and there is you know five columns um and i can just quickly destroy all of these [Music] two more here one and oh i hit the missile on that guy let's wait till he comes back i missed i missed again i missed it there we go got it so boom i kill all the invaders the game restarts and so there everything is working correctly um let me reset these back so five rows eleven comes by default now you might be wondering about this mystery ship that's been sitting up there the entire tutorial and we have yet to touch it i actually going to leave this for you to try to implement yourself so challenge you to see how you can implement this mystery ship the normal logic of this is that it goes back and forth from right to left and left or right so on every maybe 30 seconds or so you could actually implement it in different ways i'm not entirely sure what the exact logic is of the original space invaders game but i ended up implementing this separately and what i did is basically every 30 seconds the ship will appear and it will move it'll just move across the screen and if you do happen to kill that ship you will get extra points compared to the invaders so that's the challenge for you to see is to see how you can implement that on your own if you're having trouble with that i do have the source code for all of this available on github the entire project and that project contains it contains everything it contains scoring lives the mystery ship it contains the bunker destruction physics so like when you shoot a bunk a bunker it starts to crumble apart and destroy i really wanted to do that for this tutorial however it's pretty advanced compared to the rest of this to the rest of the tutorial so i don't feel like it's appropriate to do here and it's complicated enough that's going to eat up a lot of time so once again all of that stuff is available in the full source code for this game there's a link in the description of the video to github where you can have all of that if you deem but also challenge you to see how you can implement some of this stuff on your own as well i appreciate you taking the time to learn how to make space invaders with me if you feel like you learned something then give the video a like to let me know i did a good job i know we did not cover absolutely everything in this video but i highly encourage you to take what you learned and see what else you can implement on your own if you get stuck at any point you can download the entire project and and source code on github the full project includes scoring lives game over new game user interface and even the advanced bunker destruction physics you can also join our community discord server to ask for additional help directly from me and others finally if you really want to support my work consider joining me on patreon to receive a number of exclusive benefits relating to my unity assets tutorials and my upcoming game links to everything in the description of the video thank you for watching you
Info
Channel: Zigurous
Views: 10,052
Rating: undefined out of 5
Keywords: unity tutorial, unity, how to use unity for beginners, how to make a game, unity 2d tutorial, game development, how to make a game in unity, unity 3d, unity 2d, game dev
Id: qWDQgmdUzWI
Channel Id: undefined
Length: 85min 16sec (5116 seconds)
Published: Wed May 12 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.