JavaScript Game Tutorial - 2D Tower Defense

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

This post appears to be a direct link to a video.

As a reminder, please note that posting footage of a game in a standalone thread to request feedback or show off your work is against the rules of /r/gamedev. That content would be more appropriate as a comment in the next Screenshot Saturday (or a more fitting weekly thread), where you'll have the opportunity to share 2-way feedback with others.

/r/gamedev puts an emphasis on knowledge sharing. If you want to make a standalone post about your game, make sure it's informative and geared specifically towards other developers.

Please check out the following resources for more information:

Weekly Threads 101: Making Good Use of /r/gamedev

Posting about your projects on /r/gamedev (Guide)

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

๐Ÿ‘๏ธŽ︎ 1 ๐Ÿ‘ค๏ธŽ︎ u/AutoModerator ๐Ÿ“…๏ธŽ︎ Jan 29 2021 ๐Ÿ—ซ︎ replies

You have too much free time lol.

๐Ÿ‘๏ธŽ︎ 1 ๐Ÿ‘ค๏ธŽ︎ u/raysoncoder ๐Ÿ“…๏ธŽ︎ Jan 29 2021 ๐Ÿ—ซ︎ replies
Captions
is it too much to fight two bosses at once [Music] one of the most amazing things while learning javascript is when you get to the point where you are able to recreate some of the games you like from scratch by writing your own code adding your own features and using your own creative ideas and that's exactly what we will do today in this tutorial we are going to use vanilla javascript and html5 canvas element to build a game inspired by one of my favorite games of all time plants vs zombies we will use different types of units to defend our base against a variety of enemies we will collect resources and fight both battles you voted for it in a poll and i'm so happy because i get to show you some techniques we haven't done before welcome to another episode of my vanilla javascript game development series have fun and maybe get some coffee this will be a big one [Music] i love video games and everybody who knows me will tell you that i think a lot of programmers do i've always wanted to build a javascript game with mechanics similar to plans versus zombies today i will build it with you and i will explain all the code not only we will build a game we will also practice important vanilla javascript programming principles and techniques i will do a space theme robots vs aliens we set up a base on alien planet but it was too late when we discovered that at night dangerous alien creatures come out we have limited resources so we have to use our defenses carefully we have to collect more resources strategize use correct units against certain enemy types and defeat the final boss i will show you how to create a game grid and how to snap elements to that grid i will also show you little tricks for example how to prevent a player from placing multiple defenders on top of each other how to make defenders detect if there is an enemy approaching on their row they will switch between idle and attack modes depending on whether or not they detect any enemies in front of them i will also show you how to tie laser animation to character sprite sheet animation cycle we want their projectiles to come out at a very specific point so that the entire animation makes sense we will use free spreadsheets for this project so that everyone can follow but if you want to make your games unique there are beautiful animated characters and environments available online and they are not that expensive i featured work of some of my favorite artists in the previous videos if you purchase from them you are supporting talented artists and you are allowing them to make more awesome stuff for us also you are making your games look unique and tailored to your own creative vision it's a win-win scenario click like please and subscribe for more vanilla javascript games and creative projects you can also click the bell icon to make sure you get notified when new tutorial comes out now let's jump into the coding section and talk about javascript hope you have [Music] fun we will work with three files in index.html i create html5 canvas element with an id of canvas1 i link css stylesheet and i also link my javascript file install css i give body black background canvas will have border 1 pixel solid black actually white position absolute top fifty percent left fifty percent transform translate minus fifty percent minus fifty percent will center it both vertically and horizontally in the middle of the page width for example 900 pixels and height can be 600 pixels let's make the border thicker maybe 3 pixels or i can give canvas white background and remove the border everything else will be done with plain vanilla javascript here in the script.js file canvas setup is always the same variable i call for example canvas is equal to document.getelement and i pass it id i gave it earlier canvas1 now canva's variable holds a reference to the actual canvas html element so i create another variable called ctx shortcut for context and i set it equal to my canvas variable from line 1 dot get context and i pass it 2d get context called on canvas element like this will create an instance of built-in canvas api object which contains all 2d drawing methods we will need canvas width and canvas height will be the same values i just gave it in style css so 900 times 600 pixels to get our game working we will build its parts one by one i will declare all global variables in this area global variables are the ones we will need in multiple places in our codebase so putting them up top like this will make sure they are always available our tower defense will be a great game i want units to snap to grit when they are placed on battlefield i want our enemies to come in rows and i also want each individual cell in the grid to be highlighted when we hover over it with mouse in this section we will create our entire grid based game board projectiles will come from our defender units but i will make them available first so that they are ready when our defenders need them then i create defenders or towers because this is tower defense game we will also create and handle enemies resources and in utilities we will have all the helper functions we need to get our game to run such as animation loop and reusable collision detection function that's it now we just have to build these parts and make them work together this will really be vanilla javascript data manipulation exercise let's have fun with arrays and for loops now in global variables i create a constant variable called cell size i want each cell in game grid to be hundred times 100 pixels and i want gap between the cells to be for example 3 pixels const cell gap equals to 3. i create a constant variable called game grid and i set it equal to an empty array it will hold information about each individual cell in our grid i will explain it in a minute first let's create a bar on top of canvas that will hold game controls display score and so on i create an object called controlsbar and i give it width property that is the same as canvas width and height will be equal to size of one cell down in utilities i create main animation loop by writing a custom function i call for example animate all the code i put here will be constantly drawn and deleted over and over creating movement in our game there are many ways to optimize your games one of them is to only redraw parts of canvas that are moving and leave the static parts alone let's at first just build our game and make sure everything works before we worry about code optimization i think if i start explaining optimizations in a game like this one where there is a lot going on we might confuse beginners and i want this to be beginner friendly so let's just focus on the core game functionality and we will worry about optimizations later so for every animation frame i will redraw this static controls bar rectangle by calling built-in fill rectangle canvas method and i pass it 0 0 because i want it to start from the top left corner and as with i pass it value from line 13 and height from line 14. to make sure this runs over and over i call built-in request animation frame function and i pass it animate the name of its parent function from line 21 this way animate will run whatever code is inside of it and then request animation frame will trigger the same function again this will just run over and over create an animation loop in programming this is called recursion when function calls itself i have just declared animate so now i have to call it the kickoff animation loop i set fill style to blue and on line 23 i need to call fill rectangle on a ctx variable from line 2 because that's where all canvas drawing methods are stored it looks like we have a static blue bar but it is actually been redrawn over and over there are multiple ways to create a grid in our game what we will do today is create a pattern and then use the same pattern we used to create game grid to create projectiles enemies defenders and resources i think repeating the same code structure to create all of these will really help us to solidify this pattern in our brains this pattern is also very useful for particle animations i do in other videos if you followed any of my other tutorials you might already be familiar with it the pattern goes like this first create an array that holds all elements such as all cells in our grid or all enemies projectiles defenders or resources second step is to create javascript class that will serve as a blueprint to create many similar objects to fill that array the third step is to add and remove objects from this array as the game runs using this javascript class we have just created four run a for loop through the array updating and drawing each of the elements for every animation frame so that's the pattern we will use to organize and structure our entire game let's apply it to create a game grid first i already created an empty array called the game grid on line 9 that will hold data about each individual cell as a step 2 i create a javascript class called cell with capital c this will hold a blueprint and every time we call on this class using the new keyword its mandatory built-in constructor method will create one new blank javascript object and it will assign it values and properties based on the blueprint inside so let's create that blueprint in this case i want each cell to take x and y coordinates from the outside so cell constructor will expect two arguments for x and y i will show you how we get x and y for each cell to create a grade in a minute so inside we write that blueprint and i say this dot x which means horizontal x coordinate on this particular cell object we are creating right now is equal to x we pass as an argument on line 17. the same goes for this.y there will also be this dot with property but on line 17 we only pass two arguments x and y we don't pass it with so value for width will be assigned internally in this case i want width to be equal to cell size variable from line 7 which is currently set to 100 pixels this dot height will also be equal to cell size that's it for the blueprint each of our cell objects will have x and y coordinates as well as width and height each cell object will also have access to a custom method i call for example draw its job will be to take this dot x y width and height and draw a rectangle at these coordinates of that size to draw each individual cell first i set stroke style to black and i call built-in stroke rectangle method that expects four arguments x y width and height so i pass it coordinates of this particular cell from lines 18 to 21. now we need a way to use this cell class we just created on line 16 to build a full game grid we have cell size variable on line 7 that is set equal to 100 pixels i will now travel along the canvas row by row each row is 100 pixels high and on each row i will go step by step from left to right 100 pixels at the time each time i jump by 100 pixels i will create a new cell object at these coordinates i create a custom function i call for example create grid and inside i create a for loop this for loop represents rows in our grid so i call the variable y same as vertical y coordinate at first y is equal to cell size which is here if i set it to 0 i would start placing cells on this row but that is our top controls bar so we start here which is what our cell size variable is equal to as long as y is less or equal to canvas height keep increasing it by cell size so the first time it runs it will be this row then it will be this one and so on each time we enter a row we cycle through it horizontally jumping by 100 pixels and each jump we will place one cell there in a for loop inside each row will be horizontal values variable will be called x we will start at 0 and as long as x is less or equal to canvas width increase it by cell size 100 pixels we will enter this first row inside we will cycle through all horizontal x positions when we reach the end we exit the inner loop outer loop will increase by cell size so we jump to the next row and again in that row we cycle through all horizontal positions by creating two nested for loops like this we plotted coordinates over the canvas area that give us x and y coordinates for each individual cell in game grid so each time we jump horizontally i take a game grid array from line 9 and i call build an array push method push method will take whatever we pass to it as an argument and pushes that to the end of the array we call it on so each time we jump horizontally by 100 pixels which is the value of cell size variable i push new cell inside game grid array the new keyword will trigger class constructor on line 17 we can see it expects x and y as arguments so i pass it x from the inner loop on line 30 and y from the outer loop on line 29 since these two nested for loops cycle through the entire canvas area i have just placed individual cells all over the canvas in a great hundred times 100 pixels i create a custom function called handle game grade and inside i create a for loop that will cycle through all the elements inside game grid array which we have just filled with custom cell objects on line 31. we will run through the entire array and since each cell object has access to custom draw method we wrote earlier on line 23 i can call it here like this game grid i dot draw i will represent object 0 1 2 3 4 as the for loop runs so this code will call draw method for all cells objects in game grid array we created a blueprint for custom cell object on line 16. on line 28 we filled game grid array with cell objects to cover the entire canvas and on line 35 we are cycling through the array drawing each individual cell to actually draw the cell i will take handle game grid and i call it from inside animation loop like this it is not working so i console log game grid and i see the array is still empty it is because i declared create grid function on line 28 but i never called it to actually run its code i call create grid like this and here we have ourselves this is going really well now you have basically seen the entire pattern and we will use the same code structure to create projectiles defenders enemies and resources well done if you made it this far click the like please if you're getting any value from this tutorial i wanted to explain the overall structure in a beginner-friendly way and i will speed up a bit for the rest of the tutorial we will face some unique challenges but for the most part we will be repeating the same techniques thank you so much for spending your free time with me hope you're having fun with vanilla javascript because i am let's continue and build our game i removed console.log on line 41 we are drawing all cells in game grid at all times what if i want to draw only the cell that mouse is hovering over again there are many ways to do this first i create a custom object i call for example mouse it will have x and y properties as coordinates let's set them to undefined at first width will be 0.1 and height will be 0.1 as well i know this is a bit weird to give mouse width and height i will explain why i did that in a minute i create a led variable called canvas position and i set it equal to canvas variable from line 1 which holds a reference to our html canvas element and i call built-in get bounding client rectangle on it this function returns a dom rectangle object which contains information about the size of an element and its position relative to the viewport in this case our canvas element if i ctrl log it you see it contains position of canvas from the bottom left right and top as well as width and height of the canvas and its starting x and y coordinates i need this because canvas doesn't cover the entire window when i create event listener for mousemove event that mouse position we get from event object needs to be offset to account for distance of canvas from left and top edge of browser window otherwise we will get wrong values when we hover mouse over canvas so i create event listener for mouse move event and in callback function i passed the letter e shortcut for event you can also call it event or anything you want javascript knows that argument passed the callback function on event listener refers the built-in event object it contains a lot of information about event that is occurring in our case mousemove event we will need its x and y properties coordinates whenever mouse moves over canvas i set mouse.x to event dot x e is event and mouse y is equal to event dot y so whenever mouse move event happens on canvas this callback function will run and assign current x and y mouse position to our custom mouse object from line 12 making current mouse coordinates accessible globally across our project since the canvas doesn't start all the way from left i need to offset horizontal x position by the distance of canvas from the left i get it from my canvas position variable we just created on line 12 and i do the same for vertical y coordinate and top offset doing this will make sure we have the correct x and y mouse coordinates when we hover over canvas i will also create event listener for mouse leave event and when that happens i will set mouse x and mouse y to undefined you will see why when we get the code running so i want my cells to be invisible and i only want one cell to light up at the time when we hover over it i could do that by matching current mouse position to x and y properties on cells in game grid array but since we will need collision detection between defenders enemies and projectiles anyway let's just use it here as well i will create a custom reusable collision detection function that will take two arguments i will call them first and second this function will be detecting collision between two rectangles we'll compare their x y width and height and determine whether or not they're colliding overlapping that's why on lines 15 and 16 i gave my mouse width and height because whatever we pass to this collision function as argument as first and second argument we need to make sure they have x y with and height properties otherwise collision detection between two rectangles cannot be calculated this way i will go over this function fast there is no need to understand it completely i will explain collision detection between two rectangles properly in a separate video so i want this function to return true if they collide and false if they don't there is double negative here so it might be a bit difficult to understand for some of us i will write the code and then i will go over it and explain what's happening i will start by saying if the following statement is false i use exclamation mark for that then i want this collision function to return true please bear with me it will make more sense in a minute i check first and second rectangle by comparing their x and y coordinates and their width and height properties i check the following if first object's x position is more than second object's x plus second object's width or first objects x plus first object's width is less than second object's x or first object's vertical y position is more than second object's y plus second object's height or first object's y plus first object's height is less than second object's y i am comparing these four lines with or operator two vertical lines or operator will return true if any single one of these four statements is true if any one of these four statements is true i know there is no collision because if this is true if starting x coordinate of the first rectangle is more to the right than starting x of the second rectangle plus second rectangle's width i know they cannot collide if this line is true i know they don't collide horizontally so there couldn't possibly be a collision same for all other three lines if any single one of these statements is true i know there can't be collision because they don't overlap vertically or horizontally so if any of these four statements are true it means we know for sure there cannot be collision or operator will return true exclamation mark on line 73 will convert it to false and collision will be false however if all of these four statements are false or operator will return false and exclamation mark will convert it to true and collision function will return true if all of these four statements return false we know for sure first and second rectangles are colliding we will swap false to true use an exclamation mark and collision will return true this can get hard to follow with all these double negatives converting through the false i will explain it properly later to visualize better what's going on for now just believe me that our reusable collision detection function works now we have reusable collision detection function that expects two arguments first and second and as long as both objects we want to compare have x y with and height properties we can pass them as arguments and check if they collide on line 41 inside the draw method on our custom cell class constructor i can say if collision between this particular cell and my custom mouse object from line 12 returns true in that case run this drawing code to highlight the current cell it is still drawing all cells because initial mouse coordinates are set to undefined if i set the initial mouse coordinates to let's say 10 10 our collision detection works and we can see the current cell that collides with mouse gets drawn we also see old highlighted cells let's go down to line 67 inside animation loop and for every frame of animation i will clear old paint from canvas by calling built-in clear rectangle method now we see only the current cell being drawn but when mouse leaf event occurs something weird happens on line 41 inside the draw method where i check for collision between cell and mouse i also check if both mouse x and mouse y have values there is also a typo online 24. it should say mousex here but i typed mouse y so we have a fully functioning foundation for grid based 2d tower defense every tower defense needs towers defensive buildings in plants vs zombies we place plants on the lawn to protect our house against waves of zombies we will use a different sprite sheets for our game and i will call our towers defenders i will repeat the same pattern i used to create cells i will have an array to hold all defenders i will have javascript class as a blueprint to create each individual defender object and give them behavior with class methods i will have function that adds new defender in our game when we click somewhere and i will have a function that cycles through all defender objects in defender's array and calls their class methods to draw them move them animate them and so on so step one we create an array called defenders and i set it to an empty array i will also have a variable called defender cost every time we place a defender we will deduct this number from total amount of available resources i will make it a lead variable so that we can change the cost depending on what type of defender we choose click like this if you're getting any value from this tutorial you can also subscribe and hit the bell icon to make sure you get notified when new video comes out i create javascript class that will hold blueprint we will call this class every time we want to create a new defender constructor method will contain a blueprint it will expect two arguments for x and y coordinates because we want to place defenders where we click this is a grid based game i will show you in a minute how to take coordinates of mouse click and snap them to the grid so this dot x horizontal x position will be x property passed as argument on line 65. this.y is equal to property passed as argument on line 65 as well we will be checking for collision detection between defenders and enemies and as we said before a reusable custom collision detection needs every object in our game to have x y width and height both width and height will be equal to cell size variable i also want defenders to only shoot when they detect enemy on their row so i will give them this.shooting property later i will show you how to detect enemies so we can set this.shooting on each defender between true and false this.health will be hundred defenders will lose health when they collide with enemies i will also create this dot projectiles property and set it equal to an empty array this will hold information about projectiles this particular defender is shooting i will also have a property called timer whenever this defender is shooting timer will be increasing and i will be able to periodically trigger certain events that are specific just for this one individual defender object i could for example say every time timer is divisible by 100 and as long as this.shooting is true because enemy was detected push new projectile into this projectiles array i will explain it properly in a minute when we build this functionality each defender will have custom draw method i will set fill style to blue for example fill rectangle method will take this dot x from line 666 this.y this dot width and distort height and it will draw a blue rectangle representing each defender i also want to display their health so ctx font will be for example 20 pixels arial in quotes like this ctx fill text expects text we want to write and x and y coordinates were on canvas to write it so i want to display this dot health from line 71 and i draw that text wherever the defender is at the moment so this dot x and dot y from line 66 and 67 when defenders lose health that value can sometimes have decimal points in it but we don't want to display that so i will wrap it in method floor which will remove everything after the decimal point and it will display just integers whole numbers now i want to place a new defender on canvas if we click somewhere so i create an event listener for click event inside callback function i first want to take the current mouse coordinates and snap them to the grid to make sure everything in our grid based game is aligned for horizontal x coordinate i create a temporary const variable called grid position x and i set it equal to mouse.x from 922 this will give me current horizontal mouse position i want to take this value and find the closest grid position to the left mouse.x modulus cell size modulus operator gives us remainder value after dividing the first operand by the second so let's say horizontal mouse position is 250 and cell size is 100 250 modulus 100 is 50 because 250 divided by 100 is 2 200 is divisible by 100 and 50 is the remainder by saying mouse.x minus mouse.x modulus cell size in this example case it would be 250-50 it will give me 200. the value of the closest horizontal grid position to the left grid position y is the same but done horizontally if somebody clicks in blue area on top i don't want to place a defender there so i say if grid position y is less than cell size so somewhere in this area on the first row very first row up top where the bar is if they click somewhere here this function will just return and end nothing will be placed up on line 11 i have a variable called defender cost and i set it equal to 100. i will take this line of code and i move it to line 86 inside my click event listener later if i have different types of defenders i can change this value and have some cheaper and some more expensive units i will go to global variables again and i will create a letter variable called number of resources let's give player for example 300 resources when the game starts down on line 88 i check if we have enough resources to place a new defender i say if number of resources from line 11 is more than defender cost take defender's array from line 10 and call push method on it push method will take whatever we give it as argument and it will push it to the end of the array so i pass it new defender which will trigger constructor on line 65 and it will create a new blank defender object and assign it properties and values based on the blueprint between lines 66 and 73. we can see online 65 that defender constructor expects x and y coordinates so i pass it grid position x and grid position y variables from lines 84 and 85. after we placed new defender i will also deduct defender cost from number of resources to continue with the same pattern on line 57 i created a function called handle game grid that cycles through all cell objects in my custom game grid array and runs code in their draw method declared on line 42. i will do the same thing for defenders so here on line 93 i create a custom handle defenders function this function will contain a for loop to cycle through all elements in defenders array each defender is pushed to the array on line 89 and each one is created by calling defender class constructor from line 65 and has access to draw method we wrote earlier on line 75. for each individual defender in the array i call the draw method here like this to run this code i take my handle defenders function and i call it here online 108 inside animation loop that way this code will be called for every frame of our animation drawing each individual defender over and over if i test it and i click on canvas i can see i place the blue squares representing our defenders and they correctly snap to the grid so our game is aligned on lines 79 and 80 i adjust ctx font and ctx fill text to display health better this is okay for now i can place only two defenders if i click again nothing happens we start with 300 resources and each defender costs 100. i should be able to place three on line 88 where we check if available resources are more than defender cost i change this if statement to check for more or equal now i can place three i would like to display available resources somewhere inside the top of blue bar on canvas i go down to line 101 inside utilities section i create a custom function called for example handle game status i set fill style to black ctx font will be 30 pixels aerial and i call fill text the text i want to display here is resources colon space and i concatenate number of resources variable i want to display it at coordinates 0 0 so top left corner of canvas now i call handle game status from inside animation loop i can change x and y coordinates here to make it display properly on line 102 i need to call cjx fill style like this actually let's set it to gold color now every time i place a new defender that costs 100 resources i can see the number of available resources goes down perfect we have one problem look at what happens if i click three times in the same place we just placed three defenders on top of each other how do we prevent it i could for example go here to line 87 inside mouse click event listener before we actually push new defender into defenders array but after we calculated grid position x and grid position y before we place new defender we will first check if there is already one with the same coordinates so here i will cycle through the entire defenders array and for each individual defender in that array i check if their horizontal x-coordinate is the same as grid position x from 984 and at the same time if their vertical y-coordinate is the same as grid position y if there are we know we are trying to place a new defender on top of existing one so we will just use a return keyword to end the function here and the new defender will not be placed now if i click multiple times in the same place the game will place only one defender there nice that works so far we created game grid and defenders now we will use the same coding pattern to create enemies i want to show you how to make enemies to come slowly at first and i want the interval between placing a new enemy on the game board to be shorter and shorter hopefully players will have more defenders at that point and will be able to deal with the increasing number of enemies i will also show you how to make defenders shoot only when they detect enemies on their row there is no point in wasting lasers when there are no aliens attacking us i also want enemies to stop moving when they collide with defenders and i want them to resume move in at their original speed when they defeat the defender that was in their way i create a javascript class called enemy constructor will expect one property i call it for example vertical position i will pass it as an argument this way because i want this value to be available globally so that each defender can see if there are any enemies on their row on the same vertical position i will explain it in detail in a minute this.x will be canvas width because i want each enemy to spawn just behind the right edge of canvas this dot y will be vertical position passed as argument on line 103 this dot width will be cell size and this dot height will be cell size as well this dot speed will be a random number between 0.4 and 0.6 i will also create this dot movement which will be the same as distort speed from line 108. i do this because speed of enemies will change to zero when they collide with defenders and if they defeated that defender they start moving again so i need them to remember their original speed value i will show you in a minute this dot health will be hundred and this.max health will be equal to this.health from 910 again i do this because enemies will lose health when they collide with lasers but i want them to store information about their maximum health because when their health goes all the way to zero i want them to award resources depending on their maximum health that way when i have large slow enemy with let's say 300 hit points when defeated it will give player more resources than a small enemy with let's say only 50 hit points each enemy will have access to custom method i call for example update in here let's just make enemies walk to the left so this dot x from line 104 minus equals this dot movement from line 109 we will also have custom draw method that will take updated x and y coordinates and it will draw enemy there at first let's just draw red rectangles representing enemies i set fill style to write i call fill rectangle built in canvas method and i pass it at this dot x from line 100.04 this.y width and height i also want to display their health same as we did for defenders so i go up and i copy lines 78 79 and 80 and i paste them here on line 119 in the side draw method on enemy class it will just take distort health from line 110 and display it i will change fill style to black so our blueprint to create individual enemy object is complete i step outside of anime class and i create a function called handle enemies it will repeat the same pattern we did for defenders i will cycle through the entire enemies array and for each object in that array actually i haven't created that array yet so i go up to line 12 and i create constant variable called enemies and i set it equal to an empty array on line 126 we will cycle through this array with a for loop and for each enemy object in that array i call their update method from line 114 that will move them to the left and their draw method from line 117 which will take updated coordinates draw red rectangle there and display its current health so far the enemy's array is completely empty we need to find a way to add enemies into the array periodically one way to trigger periodic events in our codebase is this i go up to line 13 and i create a new global variable let frame is equal to 0 down on line 149 inside animation loop for every animation frame in our game i will increase this frame variable by 1 frame plus plus i can test if it works by console login frame and i see as the game runs frame increases inside handle enemies on line 131 i can say if frame variable modulus 100 equals to 0 this means if frame is a number divisible by 100 with a remainder of 0 this if statement will be true if frame variable that always increases is 0 100 200 300 400 and so on so every time frame is divisible by 100 we will add new enemy to the game i take enemies array we created and i push new anime inside this will trigger class constructor on line 105 create a new enemy object and push that object to the end of enemies array on line 105 we can see that constructor expects one argument called vertical position so i pass it vertical position down here as well that variable doesn't exist yet i need to create it on line 132 i create a led variable called vertical position and i want new enemy to spawn on one of the rows in our game that is aligned with game grid our game grid is made out of cells hundred times hundred pixels so i do method floor math at random times five plus one times cell size since cell size is 100 pixels vertical position will be a random number it will be either 100 200 300 400 or 500 which corresponds with vertical coordinates of our rows enemies will be placed exactly on their rows i want vertical positions of enemies to be globally available so that defenders can see if there are any enemies on their row on line 13 i create a new variable called enemyposition and i set it equal to an empty array down on line 135 inside handle enemies function every time frame is divisible by 100 and we push new enemy to enemies array we will also push that enemy's vertical position to enemies position array to run this code i take handle enemies and i call it online 153 inside animation loop great we can see enemies are coming that's perfect i want the interval between add a new enemy to be long at first and i want it to get shorter every time new enemy is added on line 14 i create a global variable called enemies interval and i set it equal to 600 for example down on line 133 inside handle enemies function i will swap hard coded value of 100 for this enemy's interval variable right now it's set to 600 so every time frame variable that increases by one every time our game loop runs is divisible by 600 with a remainder of zero in other words every 600 frames we will add new enemy on the game board and also we will store its vertical position in enemy positions array also every time new enemy is added as long as enemy's interval is more than 120 i will make enemies interval shorter by 100 so enemy will come after 600 frames the next one will come after 500 400 and so on this will just stagger the initial wave of enemies giving players some time to build up defenses you can play with this line to balance your game difficulty here it will affect the game difficulty in a big way especially later when we have different types of enemies with different amounts of health i will actually slow it down by reducing it only by 50 here on line 137 i also want to trigger game over if player allows enemies to reach the left edge of canvas on line 16 i add one more global variable called game over and i set it to false on line 133 inside handle enemies in a for loop that cycles through each individual enemy object i will check if any of them have horizontal x coordinate less than zero and if they do i set the game over variable from line 16 to true i want the game to freeze and display game over message if that happens inside animation loop on line 1163 where we call request animation frame i only want to serve the next frame of our game if game over variable is false exclamation mark in if statement checks for false on line 151 inside handle game status function i will also display game over message so if game over is true fill style will be black font will be 60 pixels aerial and fill text will say game over add coordinates 135 and 330. i need to remove fill text on line 166 that was just a test let's get a nicer font i go on google phones and i search for orbitron here i click on select this style i copy the entire link tag and i paste it inside my index.html inside head tag before the link to my custom css file because i want this font to be available there i also take css font family property here and i place it on line 12 in style css file now in script.js i can also change aerial to orbitron here on line 149 153 also to display health on enemies on line 125 let's align game over message on line 153 i change font size to 90 pixels let's have a look at projectiles actually before we do that on line 85 i change font to orbitron as well to display health on defenders inside handle defenders in a for loop that cycles through each object in defenders array for each one of them i will create another nested for loop this for loop will cycle through the entire enemy's array and it will check for collision between this defender and every enemy object i will use our custom utility collision detection function from line 175 that expects two arguments first and second so i pass it defender's i from the outer loop and enemies j from the inner loop doing this will check every defender against every enemy if they collide i will start slowly reducing health on that defender i will also set movement on that enemy to zero so it stops if defender health goes below zero i want to remove it from the game and i want anime to start moving again that's why i have this dot speed and this dot movement current movement can change but this dot speed will always remember how fast this enemy can move here on line 110 i say if defender's health is less or equal to 0 remove it from defender's array i'll remove element from array using built-in array splice method i will pass it i for index from 903 this number represents position of defender with health less than 0 inside defender's array as the second argument i pass it one because i want to remove one element at this index i will also adjust index by -1 to make sure the next element in the array doesn't get skipped after removing this one when i remove defender i want enemy to start moving again at its original speed so enemies j movement from line 126 is equal to enemies j speed from line 125 when defender gets removed i get some kind of error we will get back to this problem later for now i will just make sure on line 110 that defender's eye exists when i call this if statement so now if we have enough resources we can place defenders and we have enemies coming at us we also managed to set up collision detection between defenders and enemies so now they pose a real danger how are we going to protect ourselves with lasers of course on line 79 inside defender class constructor i created this dot projectiles property which was going to be an array that holds all projectile objects for each individual defender i've changed my mind instead i will create a single globally available array called projectiles which will hold projectile objects from all defenders in one place it will be easier to cycle through it i set it equal to an empty array at first i will also reorganize my code a bit here just to keep all arrays next to each other i create a javascript class called projectiles with capital p again i'm repeating very similar pattern we used for defenders and enemies this will be blueprint used to create all projectiles constructor will expect x and y these will be passed from the outside as arguments because they depend on position of defender that each particular projectile is coming from width will be 10 height will be 10 pixels power will be 20 for example in case we want to have stronger and weaker lasers depending on which enemy uses it or if we implement power ups for example this dot speed will be 5 update method will move them to the right based on their speed property this dot x plus equals this dot speed draw method will just draw a circle for now fill style block begin path to start drawing ctx arc to draw a circle i pass it this dot x from line 72 this dot y this dot with start angle 0 and end angle will be method by times 2. ctx fill will fill it with black color again following the same pattern i will have a function to handle projectiles we will have a for loop that cycles through all elements inside projectiles array for each object in that array we will call their update method from line 79 and draw method from line 82. in the same for loop while i'm cycling through all of them one by one i check if projectile still exists and double ampersand symbol if this projectile's horizontal exposition is more than canvas minus cell size i do minus cell size because i want my lasers to reach only up to here i don't want the new enemies to be hit by lasers before they are even fully visible and entered the game board so if projectile flies all the way here i will remove it from the array by calling splice method passing it this projectile's index from line 90. i want to remove just one element at this index so 1 here and i minus minus will make sure after removal my for loop index is adjusted and the next projectile object doesn't get skipped i will console lock projectile's length just to make sure they are being correctly added and removed i also need to have a way to add a new projectiles to the array i will not handle it from here i will go to line 21 inside defender class i create a method i call for example update each defender has its own timer property on line 112. i will increase this timer by 1 and then i can say every 100 ticks of this timer variable do something if timer is divisible by 100 with a remainder of 0 every 100 frames push new projectile into projectiles array the new keyword will call projectiles class constructor from line 70 create one new blank object and assign it values and properties from blueprint between lines 71 and 77. on line 71 we can see this constructor expects x and y coordinates as arguments i want each projectile to originate from that defender that created it so on line 124 i call new projectile and i pass it this dot x and this dot y from lines 105 106. x and y coordinates of its defender on line 143 inside handle defenders i am calling draw for every frame of animation i will also call update method i just created now i take handle projectiles function i declared on line 89 and i will call it online 216 from inside animation loop perfect we have projectiles i don't want them to come from top left corner of each defender so on line 127 i will adjust their initial x and y coordinates 70 is too much maybe y plus 50. because each defender has its own timer on line 112 you can see that each defender produces projectiles in their own interval i have an error i check the console and it's on line 224 inside my collision detection function it appears that javascript is trying to pass it first and second arguments and one of these objects no longer exists when the function is called to prevent this from happening i will go down to handle defender's function i call splice on line 151 removing defender if its health drop below zero on line 146 i will first check if defender's eye exists before i check for collision to prevent this error from happening our projectiles are still useless they don't do anything to enemies i have console log online 98 that checks if projectiles are being removed from the array when they reach right edge of canvas i can see in console that length of the array stays round three and doesn't grow endlessly even though we are producing new projectiles so all good here that works i remove that console log in handle projectiles i cycle through the entire projectiles array with a for loop i will create a nested for loop to cycle through all enemies for each projectile so that we can check for collision between them if enemies j exists and projectiles i exist i do this to prevent that error from earlier another and operator double ampersand and i also check if projectiles i and enemies j collide if they do i take that particular anime object and i set its health minus equals projectile's power from line 76 on line 172 we can see enemies have hundred health and projectile power is 20 so we need to hit each enemy five times to get the zero when projectile hits enemy i want to remove that projectile from the array so projectiles splice i pass it index and one to specify i want to remove this particular projectile that collided and as usual i adjust index by -1 i minus minus projectiles now remove enemy health and disappear when they collide now i need to make sure that enemies get removed from the game when their health reaches zero on line 195 inside handle enemies in a for loop that cycles through all enemies in enemies array i check if enemies health is less or equal to zero if it is i call splice and i remove it from enemies array and i adjust index of the for loop by -1 i also create a variable called gained resources and i set it equal to enemy's max health on line 175 i have max health set to this dot health this variable will just remember what was enemy's max health so that we can assign points and resources based on that maybe we want to have weak and strong enemies and we want some to give more points than others that's why we have max health variable to remember what was enemy's original health even though at the moment it can be zero back on line 196 i said gained resources to enemies max health so enemy with 100 health will give 10 resources when defeated increasing our available total resources i will also increase score by the same amount score plus equals gained resources i don't think i created score yet so up on line 13 i create a led variable called score and i set it initially to zero on line 217 inside the handle game status function i want to display my score so i call ctx fill text and i write score i concatenate this new score variable and i will adjust x and y properties on lines 217 and 218 to display both score and available resources on game board maybe vertical could be 40 and 80 yeah we can test it and when we defeat enemy that originally had 100 health we will gain 10 resources and 10 score points i don't want defenders to be shooting all the time i want them to be able to detect if there is an enemy on their row and only then fire lasers at them on line 117 inside the defender class i gave each defender this dot shooting property and i set it to false initially inside update i only want to run this code if this.shooting is true else i set this.timer from 920 back to 0. up on line 18 i create array called enemy positions on line 212 inside handle enemies function every time i push new anime to enemies array i also push its vertical position vertical y-coordinate distance from the top edge of canvas to enemy positions array so this array holds one number for each active enemy on line 205 when enemy reaches health 0 before i remove it from the array i will also remove its vertical y coordinate from enemy positions array how do i find the correct value in enemy positions array so i know what index to pass the splice method to remove it i can for example create a variable called find this index and i say nma positions index off and i pass it this enemy's vertical y coordinate index off is a built-in javascript method if you have multiple enemies on the same row let's say on the row one we have multiple items with value of 100 inside anime positions array it doesn't matter we just remove the first one we find i remove it by passing this find this index variable to splice method on line 206 because it contains index of that matching element inside the array and again i pass it 1. i want to remove one element at this particular index inside nmapositionsarray this way i make sure enemy positions array holds vertical coordinates of all active enemies and removes vertical coordinates of enemies we defeated information inside enemy positions is globally available in our game so i can check it from anywhere let's try and console lock enemy positions we have one enemy at vertical coordinate 500 that's row number 5 and another enemy at coordinate 400 row number four now we have an enemy at coordinate hundred row number one just to test it i will set this.shoot into true on line 117. i get an error on line 205 i misspelled enemies let's try again we have one enemy at coordinate 300 row number three another enemy poked at coordinate 100 and when we defeat enemy at 300 this value gets removed from enemy positions array perfect that works the information in nma positions is globally available so i can go to handle defenders and on line 158 i check if anime positions index of this defender's vertical y position is not equal to -1 let me explain this in javascript if index of returns -1 it means it didn't find any item with this value in the array because anime positions contains values 100 200 300 400 or 500 depending on the row enemy's vertical position in our grid base game i am just saying here if index of this defender y position is not equal to -1 meaning if anime positions array contains at least one value that is the same as this defender's y position we know that there is enemy on its row they have the same vertical coordinate if that's the case i said shooting on this particular defender object to true else if we don't find any matching values in enemy positions array we know there is nothing on our row and we set shoot into false also on line 117 i set it to false initially i tested and i can see defenders only shoot if there is an enemy on their lane there are many other ways how to detect enemies but i like this technique because we got to practice index of built-in array method well done this is going really well we have one small problem because of how we wrote our custom collision detection function if two rectangles meet tip to tip like this collision is triggered even though they are not on the same row and they shouldn't collide i will fix it by making defenders slightly smaller on line 8 i created cell gap property and i set it to 3 pixels on line 115 i set defenders with the cell size minus cell gap times 2 because i want gap of 3 pixels from the left and right side i also set height to cell size minus gap times 2 to get space on top and bottom on line 142 and 143 i need to account for this gap when clicking on canvas and placing a new defender somewhere on game board i just offset x and y by cell gap like this that fixed it you can see we have 3 pixel gap now and collision doesn't get triggered when it shouldn't it will be difficult to place enough defenders and win the game when we don't have many resources in plants vs zombies we have small suns that spawn periodically and if you click on them you gain some resources we will do something similar i create an array called amounts and i put values 20 30 40 inside i create a class called resource with capital r constructor will contain blueprint this dot x will be random number between zero and canvas width minus cell size i don't want resources to spawn too much to the right and be hidden behind the age of canvas this dot y will be a random integer between 1 and 5. so i wrap it in math.floor to round decimal points down times cell size so if it's 1 it will sit on the first row because 1 times 100 is 100 2 times 100 is 200 so it will sit on the row 2 and so on i could have just made it completely random they don't have to stick to rows but when we add sprites making resources sit on rows will look much cleaner and will prevent some clipping issues this dot width will be cell size times 0.6 this dot height will be also cell size times 0.6 this.amount will be random value from the amounts array from line 227 so it will be either 20 30 or 40. i create a draw method for now resources will be represented by yellow rectangles ctx fill style is yellow ctx fill rect and i pass it this dot x from line 230 this dot y this dot width and this dot height i also want text that displays the amount so fill style black font 20 pixels orbitron fill text will be this dot amount from line 233 this dot x plus 15 and this dot y plus 25 15 and 25 will push the text to the right and down so it doesn't start all the way from the top left corner of this yellow resource rectangle blueprint is complete i create a custom function called handle resources and as we did before i take advantage of a frame variable to introduce periodic events in our game every time frame variable is divisible by 500 with a remainder of 0 i take resources array and push new resource to it up on line 20 i need to create this resources array on line 14 i also create a variable called winning score for now i will just set it to 50 for testing purposes on line 247 inside handle resources i only want to be adding new resource if current score is less than winning score don't forget that players also gain resources by defeating enemies i will also create a for loop here this will as usual cycle through all elements in resources array and for each element in the array it will call its draw method we wrote on line 238 i want to collect resources just by hovering over them with mouse no clicking needed i say if resources i exists and mouse x and mouse y are not undefined i check all of this because if one of them is undefined when passed to collision function the entire game will freeze because we get an error so here i also check if there is collision between resources and mouse again using our custom reusable collision detection function if they do collide i take number of resources and i check for its disk.amount value from that particular resource we collided with and i add it to total resources i will also take resources array and i will remove that resource from it splice i for index and 1 and adjust index i minus minus as always so here we declared handle resources i also have to call it there's an error on line 240. it's just a comma should be a dot when i hover over resource it disappears and its amount gets added to my total available resources i can use these to buy more defenders nice on line 219 i only want to add new enemies if score is less than winning score my defenders are not shooting when they should it is because i made defenders smaller but enemies stayed the same size which means their vertical coordinates now don't match my defenders and the logic is broken let's fix it on line 182 inside nma class constructor i will adjust this.width to cell size minus cell gap times 2. i will do the same for this.height now vertical positions of enemies and defenders match again and projectiles get triggered automatically if defenders detect enemies on their row let's display winning message inside handle game status custom function i check if score is more than winning score and if we defeated all remaining enemies by chicken if enemies array is empty my text will be black font for example 60 pixels orbitron fill text will say level complete add coordinates 130 and 300 then we will have smaller text 30 pixels orbitron that says you win with and i concatenate score variable plus points and i place it at coordinates 134 and 340. just to test it let's set winning score to 10 on line 14. we have the score we need we also need to defeat all active enemies and we won the game that was easy on line 268 i changed this to score more or equal than winning score i set winning score to 50 and let's playtest it i'm placing defenders collecting resources defenders shoot their lasers only if they detect enemies everything seems to be working fine yes we win again there is one small bug that happens in every canvas project when we deal with mouse position when i resize browser window mouse position is no longer correct it is because the distance between left edge of canvas and browser window changes and it's not offsetting correctly anymore fix is very easy i create an event listener for resize event and in callback function i just take canvas position variable from line 30. notice this variable is used on line 32 and 33 to handle top and left canvas offset as we covered in the beginning of this tutorial i just take this line of code without the led part because i'm not declaring a new variable i'm just recalculating existing one i paste it inside resize event listener online 303 and now every time we resize browser window offset gets automatically recalculated and mouse position is always returning correct x and y coordinates there are so many things that can be done at this point i want to add more features sprites animations and boss battle what features would you like to see do you have any interesting ideas this is our game and our code we can do whatever we want with it we have a solid good quality based game here it just needs some graphics now congratulations if you followed this entire video you are determined to learn and build things while you're here you can also check out other projects in my vanilla javascript game development playlist i also do creative canvas animations and particle effects see you there
Info
Channel: Franks laboratory
Views: 33,540
Rating: undefined out of 5
Keywords: javascript game tutorial, javascript game, javascript tutorial, javascript 2d game, tower defense, plants vs zombies, html canvas, vanilla javascript, html5 canvas, html canvas tutorial, vanilla javascript tutorial, vanilla javascript game, create a game with JavaScript, HTML5 Canvas and JavaScript Game Tutorial, JavaScript Tutorial for Beginners, javascript gamedev, javascript game development, javascript, game development, web development, franks laboratory, frankslaboratory
Id: QxYg8-mhhhs
Channel Id: undefined
Length: 74min 15sec (4455 seconds)
Published: Fri Jan 29 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.