JavaScript Game for Beginners: Enemy Types

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
if you want to build games with the javascript all you need is a small set of tools and techniques let's add a couple more of them to our toolkit today and make sure we really understand them in javascript the extends keyword is used to create so called child class that way i can have my main parent class where i define properties and values share between all my enemy types and then i can have child subclasses that define specifics such as different visuals different movement patterns and so on for each enemy type then when i instantiate my ghost object and i call update on it if javascript can't find update method on the ghost class it will go look for it automatically on the parent anime class and it will run the code from there there is another special keyword called super today i will show you exactly when and how to use it let's build a project step by step completely from scratch and in the process let's learn everything we need to know about subclassing in javascript and how to use it to keep our code modular clean and easy to read this tutorial is for beginners i will take you through the code in a way to make sure you understand everything we will build a really fun animation project and i will show you many game development techniques today this video is independent and i will explain the code from scratch but it's part of a larger series where i teach you tools and techniques you will need if you want to be a game developer or if you just want to learn front-end web development and javascript on fun visual animation projects if you go along with me today you will get three free premium sprite sheets so that you can get exactly the same result check out this website for more beautiful game development art assets to start my project i create a basic webpage boilerplate markup a link style css file javascript file and i created html5 canvas element with an id of canvas one i bring my three images into the project by creating three img tags with ids worm ghost and spider you can find these images to download in the video description it might be a good idea to use the same images as me at first and then modify the code and use your own art assets once you get everything working it might save you a lot of time debugging if you follow exactly what i'm doing especially if you are a beginner i give my canvas a border width and height and i position it with css in the middle of the page i will also hide the project images we will draw them and animate them with javascript in a minute by including images as image tags i can make sure they are fully loaded before my javascript code runs by using load event listener it will prevent potential errors i will show exactly what i mean as we go along i set up my canvas using these four lines of code as usual let's outline the entire project structure now so that you can see what functions and classes we need and how they are connected to each other when we have the complete project structure we will be expanding on individual parts to add more functionality the most important thing today will be my game class i will use it as the wrapper that contains and controls all movement and animation logic and basically everything else it's a javascript class so it will need a constructor which is a monitor method for all javascript classes it will also have update and draw methods in game class constructor i create a property i call this dot enemies my game object will hold an array that contains all enemies like this my game class will also have a private method called add new enemy this method will be called every time i need to create and set up a new enemy for my game if method's name starts with a harsh symbol like this it will be a private class method it means it can only be called from within my game class to manage some kind of internal functionality in our case adding a new enemy into this.enemies array from line 8. if you try to call this private method from outside game class you will get an error notice that update and draw methods are public because i will need to call them from my animation loop from the outside a bit later my add new anime private method will need a way to create new enemies for the game so we will write another class i call anime and inside we declare a blueprint whenever i call this anime class from inside add new anime method it will create one new anime object for me i will show you how to make animated ghosts worms and spiders they will all be created by this enemy class but they will have different animations and different behaviors to make our game more interesting and maybe more difficult for the players so again as you know every class needs a constructor to hold the blueprint and we will also have update and draw methods here update and draw method on game object here will be handling updating and drawing my entire game for example all enemies player obstacles backgrounds menus and so on update and draw method here on the enemy class will be handling only each individual enemy such as their position movement pattern sprite animation and so on and finally to complete the skeleton of our project we will need a custom animate function that will call what needs to be called and a loop around to move and animate things in our game here in animation loop i declare what happens in our game frame by frame at first we will call built in clear rectangle method and we will clear old paint from the entire canvas so from coordinates 0 0 to coordinates canvas with canvas height if you are a complete beginner this is horizontal x axis and this is vertical y-axis so point zero zero is here and point x two hundred y two hundred for example is here this is the basic thing you need to understand if you want to draw on canvas so we cleared old paint from canvas to make sure we can only see the current animation frame we write some code here depending on what we want to happen in our game and we call request animation frame built in javascript method which will simply take function we passed to it and it will call it once animate will run its code and call itself again run its code and call itself again over and over this is how you create animation loop i don't want any of this code to run until i'm sure my web page including canvas element and all project images have been loaded so i highlight all the code and i cut it for a second i want to put it inside load event listener document dot add event listener and we will listen for dom content loaded javascript event i actually made a mistake here because the dom content loaded event fires when the initial html document has been loaded and parsed but it doesn't wait for style sheets and images the correct event to listen for here is the load event which only fires when the whole page has been loaded including all dependent resources such as style sheets and images so please replace dom content loaded event with load event here you will not notice any difference if you are working locally because images will load instantly but if you are loading images from an online server somewhere load event is the one you need here so i paste all the code back here inside my load event listener we have canvas setup main game class enemy class and animate function if you want to animate something it's important that it runs at the same speed on slow old computers and on brand new gaming super pcs not every computer is able to surf frames at the same speed also some screens have different refresh rates and request animation frame adjusts when it serves the next frame based on screen refresh rate so what i want to do now is count how many milliseconds happened between frames and only when we reach certain threshold we will serve the next frame since we are passing animate as an argument to request animation frame here request animation frame has another secret feature it will pass an automatic timestamp argument to the function it calls so our animate has access to timestamp argument which was automatically generated by javascript because i called animate using request animation frame here i want to calculate the difference between previous and current animate call in milliseconds difference between frames and animation is also called delta time simply explained as request animation frame runs over and over it is passing animate this automatic timestamp argument i want to know what is the difference in milliseconds between previous timestamp and timestamp from the current loop so called deltatime i will calculate it by saying a timestamp that was passed in this loop minus timestamp that was passed in the previous loop i will call it for example last time after i used last time variable that was holding old timestamp from the previous loop to calculate delta time i can reassign last time to the new timestamp from this loop so that it can be used in the next loop again let's console delta time to see if it works here i declare animate i also need to call it the start the first animation loop oh yeah last time is not declared i declare it right here and i initially set it to 1. this value of 1 will only be used for the very first loop because on the second loop it will get reassigned to the current timestamp in console i can see my delta time is around 16 milliseconds this is very common refresh rate for most computer screens but if you have high refresh screen you can get a lower number here and if you have old computer you can get higher number here especially when you create a bigger project where you animate many moving parts at this point even very slow computers should handle 16 milliseconds delta time time between animation frames since our game doesn't have any graphics yet if i scroll in console all the way up to my first delta time console aux i can see the initial call was none not a number it's because animate only receives automatic timestamp argument here when it gets called by request animation frame on the very first initial call it doesn't have any timestamp argument so i will pass it 0 just for that first loop now we know how to calculate delta time number of milliseconds between frames faster computers will have lower number slower old computers might have higher delta time because it can take them more milliseconds to serve the next frame since we have this delta time value we can adjust animation speed with it to make sure our game runs at the same speed on slow and fast computers i will show you where and how to use it as we build our project and start animate and move game characters around on canvas console lock like this can cause performance issues so don't forget to remove your console logs when you don't need them anymore on line 22 i have my anime class it has a mandatory constructor method which will be used as a blueprint to create one new enemy object whenever i call this class with a new keyword from somewhere in my project let's write that blueprint and inside we declare properties and values of our enemies each anime will need x and y starting coordinates so that javascript knows where we want to draw them on canvas our first hard code coordinates hundred hundred we will change that in a minute i will also give each enemy width of 100 pixels and height of 100 pixels update method on enemy class we'll just move the enemy by one pixel to the left draw method on my custom enemy class we'll call build in a fill rectangle method and it will first draw a black rectangle representing my enemy i pass it this dot x from line 24 this.y this dot width and distort height great we have anime class now we can call it to create enemy object and add it to our game inside add new enemy private method i take this loot enemies from line 9 and i call build in push method push method can be called on arrays and it will take whatever we pass to it and it will push that at the end of the array so i pass it new enemy the new keyword is a special command in javascript it will make javascript look for a class with the name enemy and when it finds it it will take its constructor and use the blueprint inside to create one new blank javascript object and assign it values and properties based on instructions inside the blueprint that new anime object will also automatically have access to update and draw method on anime class when you instantiate object with the javascript it will run its blueprint in site constructor so i will do something which might look a bit strange to you any code put inside the constructor will be executed when you create new instance of that object with the new keyword so here when i create new instance of my game object i will create enemies array and i will call its private add new enemy method automatically which will push new enemy inside enemies array to check if that really happened i will also console lock this dot enemies to execute all this code here all i need to do is to instantiate my game class so let's do it down here on line 39 i create a new constant variable i call for example game and i set it equal to new game like this doing that created one new blank game object and a train code inside constructor here on line 8 so this console log from line 11 is showing that this load enemies from line 9 contains one new enemy object perfect you can see that enemy object has x and y width and height as we declared here on line 25. now inside update method on game class i will cycle through all anime objects inside this dot enemies array and i will call their update method for line 31. i hope you know syntax of for each method i'm simply saying here take this.enemies array from line 9 and for each object in this array run their associated update method this is es6 syntax so called arrow function i will also take the same enemies array from line 9 and i will call draw method from line 34 on all these enemy objects currently there is only one inside but there could be many more down here inside my animation loop i take instance of my game class from line 39 i call update method we just wrote and i also call draw method and here we are animating one enemy in our game i hope you can see advantages of code structure like this let's say i also have array of obstacles background layers power ups players and so on i can call all of these from update and draw method on my game class to get everything moving with very little code from a single wrapper function keeping my code clean and organized this way it's a good practice not to use global variables inside our classes so i will take context with and hide and i will put them inside my game class these arguments will be passed here on line 39 when i create an instance of game class so i pass it ctx from line 3 to tell javascript which canvas i want to draw on i will define width of my game by passing it canvas width from line 4 and canvas height from line 5. these three variables just as my game class constructor expects here on line 8. then i take these arguments and convert them into class properties this dot ctx is ctx passed on line 8 this.width is width and distort height is height now i converted public variables into class properties and i can pass them around when i need them by passing game object as an argument i will show you let's say i need width and height of my game inside the enemy class because i want my enemies to appear behind the edges of game area on line 23 when i create new anime object i will pass it this keyword which here refers to the game object we are currently inside that game object will carry all references to its width height and other properties with it let me show you so i'm passing this keyword and inside my enemy constructor i will call it game here i created this.game property and i set it to game that was passed as an argument now i have access to game object from inside my anime class i can console log it and you can see all its properties including width and height so now i have access to width of my game from inside enemy class by saying this.game.with the initial starting x horizontal coordinate for enemies will be just behind the right edge of canvas initial starting y-coordinate will be a random number between 0 and this dot game height so in this case between 0 and 800 pixels because height of my game is 800 now whenever i reload page new enemy is generated with random vertical coordinate i achieve that by passing reference to my game class to enemy class constructor on line 23 which will take all these properties and values with it i call it game and convert it into class property on my enemy class making them available when i need to position newly created enemies this is one way how you can pass class variables around if you want to avoid using global variables in your projects i don't really want to add just one anime when i create instance of game class i want to create new enemies over and over in a certain interval if i just call this.add new anime from inside update method it will create new anime for every animation frame that is too many and it would freeze my game very quickly i commented out game class is responsible for adding new enemies to the game so i will give it two new properties this dot enemy interval will define number of milliseconds between adding each new enemy into the game let's try 400 milliseconds this dot enemy timer will count milliseconds from 0 to 400 over and over in update method i can say if this dot enemy timer is more than enemy interval call add new anime private class method also at that point we want to reset enemy timer back to zero so it can start counting again else meaning if enemy timer is less than 400 milliseconds increase enemy timer by one every time we add a new enemy i will counterlog this.enemies array from line 12 just so we can check if it's adding objects or not i decrease enemy interval on line 13. awesome we are adding enemies but you know how we said that each computer could run at a different speed that could mean that enemies are being added faster or slower on some computers i want my game to run at the same speed on every computer and that's why down here on line 56 in animation loop we calculated the delta time time between frames and milliseconds i will use it now i pass delta time to game.update method on line 58. from my previous console log i know my delta time is 16 milliseconds my computer can serve new frame every 16 milliseconds value of delta time is dependent by the power of your computer but also on your screen refresh rate because request animation frame method adjusts to maximum screen refresh rate if the computer can handle it now i have access to delta time value here in my update method so instead of adding hardcoded value plus 1 i will add value of delta time to my enemy timer on line 13 i set an m interval to 400 milliseconds or maybe 1 000 milliseconds here i'm increasing enemy timer by 16 milliseconds per frame when we accumulate enough to reach 1000 milliseconds one second we will add new enemy to the game if somebody has a faster computer their delta time will have smaller value on slow computers delta time will have higher value and will be getting near to the target 1000 less often but in larger steps so in the end my code will run at the same or very similar speed on slow and fast computers regardless of their power or monitor refresh rate and this is how you use the delta time to trigger periodic events in your games or animation projects i also need to make sure i'm passing the reference to my canvas properly and not using global variable let's follow the logic we declare ctx here on line 3. i pass that global variable to my game class when i instantiate it on line 52. on line 9 i take that argument and i convert it to class property then i pass ctx from line 9 to draw method on my enemies and on line 47 i need to make sure draw method on enemy class expects that argument and this way line 48 is using this.ctx that was passed around from gameobject rather than using globalctx variable i'm going to log in this.enemiesarray online20 and i can see it keeps growing i need a way to remove enemies that have left the screen we could potentially also use something called object pooling here but i will leave that for another episode first i will need a flag on my enemy objects that will mark them as ready to be deleted and then we will simply filter these objects out i will call that property this dot marked for deletion and i initially set it to false then inside update method on enemy class i will check if this.x is less than 0 minus this dot width meaning the enemy has moved across the screen and behind the left edge of canvas if it has i will set its marked for deletion property to true now i can go inside update method on game class and here i will take this.enemies array from line 12 which currently has around 20 objects and it keeps growing and i will filter it filter is a built-in javascript method that manipulates arrays it simply creates a new array with all elements that pass the test implemented by the provided function so here on line 17 i'm saying take this.enemies from line 17 and reassign it to the same array but only include elements that have this dot marked for deletion property set to false exclamation mark here means false let's run the code and see if it works yeah we stop at 10 enemies and it's not growing anymore because we are removing old ones as we are adding new ones might be one good performance idea here you don't have to check and filter enemies for every frame of animation we could for example do it in a certain interval i could even take this line of code and put it inside this if statement it would still filter old enemies but it would run only periodically whenever i add a new enemy to the game we are making nice progress we have already implemented so many features here between lines 35 and 53 i have my enemy class it's a blueprint with properties and methods that determine what my enemies look like how they move and how they behave let's expand on it and make them more interesting rather than simple black rectangles i will create a so called sub class or child class by saying class worm extends enemy doing that will make anime a parent class of worm and for example if i call update or draw and javascript can't find that method on my worm class it will automatically go looking for it on the parent class so it will look for that method on anime class same goes for properties declared in constructor if there is no constructor on my worm class it will automatically use the constructor from anime when i do this i can have shared enemy class that declares all logic properties methods and features that all my enemies have in common for example their sprite animation logic collision checks with player and so on while at the same time i can declare child classes where i can override improve and change some of these properties and methods to create unique and very different behaviors for different enemy types and that's exactly what we will do now let me show you how much visual and behavior variety we can create without repeating the code all enemies have in common doing this will keep our code clean and modular and you can go crazy with different enemy types while still keeping your logic organized and easy to read on my worm class i don't have to declare constructor at all and javascript will automatically use constructor from line 36 on enemy class but in most cases you want each enemy to have different width height x and y position and so on so i kind of want to use both i want to take all properties from enemy constructor and i want to add some additional properties on top of it that will be specific only for my worm objects i declare constructor and i know worms will have unique value for image properties so this dot image equals worm this line of code is a weird syntax and i will explain what's going on and how comment works when i bring image to my project this way in a minute let's first finish constructor method on worm class same as on line 36 worm class constructor will expect the game as an argument now i want to say take all the code between lines 37 and 43 on enemy class constructor and use it here to do that we use special super keyword the super keyword is a special command in javascript and it's used to access and call function on an object's parent so here i call super and i pass it game to literally run the constructor on its parent class after that i can add more properties on my worm class constructor like this important thing to note here is that you have to first call super and run parent constructor like this before you declare any additional properties using this keyword otherwise you will get a reference error in derived classes like this super must be called before you can use this keyword so now my worm constructor contains all the code from between lines 37 and 43 and additional distort image property set to worm each enemy type will have different starting x and y coordinates and different width and height so i will actually take all these and put them on my worm i want enemy class constructor to have only properties and values that are shared for all my enemy types up here on line 31 inside private add new anime method rather than instantiating parent enemy class i will instantiate child worm class again i'm passing it this as an argument and because we are inside game class this reference is the entire game object so all these properties we travel down to worm class and inside constructor we are taking that game and passing it to super instantiating constructor of its parent anime class which is here anime class also has update and draw methods and when i call update and draw on my worm and javascript can't find it it will automatically go looking for them on the parent anime class i hope it's becoming a little bit more clear how this works and how parent and child classes relate and work together if i run the code we have our worms coming again just to prove that draw method that is called from 946 is taking properties from worm class constructor let's change this.with online 56 as you can see now my worms are 200 pixels wide so what is this weird syntax online 58 this is not how we usually bring images into a project usually i would use getelementbyid to target my image element with an id of worm that i created in index html in the beginning i wasn't aware of this so all the credit goes to dany who says a little known fact is that any elements created in the dom with an id attribute are automatically added to the javascript execution environment as a global variable this means that you can access any html element with a javascript using its id no need for document.getelementbyid or query selector i'm just doing this as an experiment and you will see in a minute that it actually works if you're having any issues you can still bring the image into the project the usual way and also ideally pass it to the worm class constructor as an argument so that we are not pulling global variables directly from the outside i'm just experimenting here a bit i really like tips like this thanks danny nowhere in my code have i declared this worm variable the only place you can find this reference is id on line 12 inside index.html but this image will still work you can test it by console logging this.image i have too many console logs let's comment outline 38. yes you can see that the javascript is actually finding the image with an id of worm automatically without us having to use getelementbyid like we usually do so let's use that image and make our worms look a bit better than black rectangles you can download these sprite sheets for free in the description and you can use them to follow along with this video for educational purposes to use them in any commercial project you would have to buy a full license from the artist's website i will link his site as well he has a lot of great game development art assets there check it out i feel like i explained built in canvas draw image method 100 times on this channel this method is so important at first i will just use the version with 5 arguments image we want to draw x y width and height where to draw it on canvas this way we will just squeeze the entire sprite sheet with all its six frames into the predefined area of 200 times 100 pixels notice that i'm calling draw method on my worm class it's using these properties but since javascript can't find draw method on worm it will look for it on parent anime class and it will use that one let's remove this console log and this fill rectangle as well i can change width and height but now i would actually like to draw just the first frame from the worm sprite sheet i remove all these lines i commented out earlier i create a new property on worm class called this.sprite width and it will be a width of a single frame in my sprite sheet if i take width of my sprite sheet and divide it by 6 the number of frames i will get 229 pixels height of a single frame is 171 pixels i take width and height and move them up here now i need to use draw method to cut out just single frame from that sprite sheet and place it on canvas for that i will need to give it nine arguments so additional four to tell javascript what area i want to crop out from the source image i need to give it source x source y source with and source height arguments source x and source y will be 0 0 since i'm just cropping out the first frame for now source width and source height will be distort sprite width from mine 52 and this.sprite height from line 53 so to crop out a single frame from an image with draw image method i need to give it nine arguments image i want to draw x y width and height of what area i want to crop out and x y with an height of where on canvas we want to place that cropped out piece of image onto now we are drawing just one frame perfect you probably noticed that it's a bit stretched that's because this width and height which is coming from here has no relation to the actual size of a single frame so the ratio is different let's make width and height the same aspect ratio as our sprite sheet i do that by making width half of sprite width and height will be half of sprite height i need it to do half because the original size would be too large as long as i use the same modifier on both width and height in this case i divide both of them by two it will preserve the aspect ratio of the original sprite frame all my worms are moving to the left by one pixel per frame what if i want each worm to have different speed for example a random value between 0.1 and 0.2 and up here inside update method on enemy class i say this dot x minus equals distort speed that will be very very slow let's make it faster you might have realized that i'm not using delta time in this movement calculation so let's use it to make sure the worms move at the same speed on all machines i'm calling this update method here on line 25 so i pass it delta time which we made available here before now that i have access to delta time in update method on enemy class i will pass it here as an argument and to factor in time difference between animation frames when moving something around we have to multiply it that way faster refresh rate with lower delta time will move often but by smaller steps slower pcs will have higher delta time they will not draw frames so often but they will have higher delta time so they will make larger steps to compensate for the difference now the base speed is too much so i amend it on line 59. i will actually rename it vx velocity on horizontal x-axis because maybe some other enemies will have a vertical movement also i need to rename it on line 41. if i go up on line 13 and reduce enemy interval to 100 milliseconds we will get a lot of worms and something will become apparent they are layered randomly in order as they are added to the game for example this worm should be probably behind this one and this one behind this one if we pretend this is 3d game space and worms that are higher should be behind worms that are lower it would look better how do we achieve that that index or whether worm is in front or behind at worms depends on order at which they are added to the array on their index in that array because as i push them into enemies array one by one we cycle through them from index 0 to the maximum index in that array and we draw worm with index 0 then worm with index 1 on top of it if they are in the same area then worm with index 2 will be on top of them and so on i want to reorganize my array so that index in the array is tied to their vertical position so that we first draw worms in this area and then we draw the ones in this area on top of them as we go down on the page vertically in plus direction on vertical y-axis that will cause worms to look like they are properly behind each other and not messy like this look at this this is a mess this one is sliding over the other one's eye so every time i push new worm into the array i will just build javascript sort method you can simply just call sort on the array and it will sort them in ascending order by default if you want more control you can give it a callback function sort simply takes every element in the array and it performs a check comparing values between these two elements pushing them up and down in the array depending on the condition in the callback i want worms with lower vertical y coordinates to have lower index so that they are drawn first so my condition will be a dot y minus b dot y like this sort is a built in javascript method it abstracts away the actual algorithm that does the sorting work so let's not spend more time on it now all we need to know for now is how to use it when i run the code you can see that worms that are higher are drawn behind the worms that are lower making it look like they are moving in actual 3d or maybe in two and a half day cartoon space well it doesn't look messy anymore you can swap between ascending and descending order by swapping these values in our case we want a minus b also notice i'm not sorting the array for every animation frame only when i add new anime into the array so this is our worm class it's a child of anime and it doesn't have update and draw methods because as we said if javascript doesn't find them on the worm class it will go looking for these methods on the parent class but what if i want to use the base code from draw method on enemy class and i want to add some extra code to it to create some additional visuals for example as boris suggested ghosts need to be semi-transparent but i don't want reduced opacity on worms only on ghosts so how do we do something like that i'm sure you can come up with some clever if statement but let's keep it clean and use child classes for that i will copy my room class and rename it to ghost image online 74 will be ghost referencing the id here on line 13 in index html i look at my sprite sheet width divided by number of frames so sprite width of single frame is 261 pixels height is 209 pixels in this case horizontal speed modifier will be a little bit higher than worms let's say a random number between 0.1 and 0.3 so that's the base ghost child class how do i randomly push worms or ghosts into my game one way to do that is to create a property on my main game object called distort enemy types it will be an array with two elements for now worm and ghost inside add new anime private method i will randomly pick one of the options in the array i create a constant variable called random anime and i set it equal to these dot anime types and square brackets so if index here is 0 we get worm and if index here is 1 we get ghost how do i randomize this index and let it choose one of these randomly every time we run add new anime method i can just put math at random statement and it will be a random number between zero and length of distort enemy types array that way i can keep adding more and more enemy types to the array and this code will still work maybe my game will have 20 different enemy types it doesn't matter it will still work the only problem is that math at random returns numbers with decimal points and there is no index 1.5 in this array it's easy to fix i will wrap it inside method floor which will round it down to the closest integer now i can check if random enemy variable is worm we will push new worm into this dot enemies array else if random anime is ghost we will push new ghost perfect so we have this.enemy types array on the main game class this can contain as many enemy types as you want inside add new anime we randomly choose one of them every time we are about to create new anime object and based on that random choice we create either worm or ghost so now both worm and ghost are child classes that extend my main enemy class let's switch to kind of flat 2d view i want worms to move only on the ground so i change their vertical y position to this.game.height minus distort height worms are sliding on the floor ghosts are flying in the air i want ghosts to take up only the top 80 of the game area not to be so close to the ground their vertical y position will be a random number between 0 and game height times 0.8 or maybe 0.6 yeah since we switched to this new view i don't really need to be sorting my enemies by their vertical position anymore so i comment out this code on line 35. with both worm and ghost we are only modifying properties on the constructor they both inherited the same identical update and draw method from the main enemy class with no modifications at the moment we said we wanted to make the ghost semi-transparent so let's do it i declared draw method on ghost class and now two things can happen we can override the draw method with a completely different code here because when we create a new worm javascript will not find the draw method and we'll go looking for it on enemy parent class but with ghost it will find the draw method right here and it will ignore the original draw method on enemy class the second option is that i want the code from draw method on enemy class to run plus i want to write some additional code on top of it that will be unique only for ghosts i can do that by calling super the super keyword here simply represents parent class so imagine i'm saying enemy dot draw because i want to call it's draw method first super.draw means animate.draw basically super represents the super class the parent class so here inside the draw method on ghost class first i call draw method on enemy class and i know it expects ctx as an argument so that it knows what canvas we want to draw on that ctx property is coming from here on line 29 by the way where i'm calling draw on all objects inside this dot enemies array i'm calling draw on all ghosts and all worms at the same time so first we call draw method on parent enemy class from 950 we will run all the code in here but we will also add some additional code that will be specific only for ghost objects in this case i will set global alpha property to 0.5 this will set opacity to 50 you can see that it affects all ghosts and all worms as well it's because global alpha canvas property works the same as fill style for example once you set it you are setting it for the entire canvas it will stay that value for everything drawn on the same canvas unless you redeclare it to something else since i want global alpha to only affect ghosts and nothing else on canvas i set it to 0.5 i draw my ghost and i set it back to 1. the alternative way to do this would be to call ctx save built-in method which will take a snapshot of all canvas settings at that point then we can change global alpha and anything else we need to change we draw the ghost and we call ctx restore which will automatically restore canvas properties including global alpha back to what they were at this point when we called safe for the first time this technique is especially useful if i was changing multiple canvas properties at the same time not just global alpha so this is how you extend a method from parent class with the help of super keyword now that we know how to do it we can also extend update method and make ghosts move in wavy patterns let's try i redeclare update method on ghost class and i pass it delta time i call super dot update and i pass the delta time this will run all the code in update method on enemy class from line 46 here i add a new property called this.angle and initially i set it to 0. then in update method i will say this dot y vertical position of my ghosts plus equals mastered sign built in javascript trigonometry function and i pass it this dot angle as an argument i covered this in my enemy movement patterns episode as part of this series before so now just quickly moderate sign is a built-in javascript function that returns a sign of a number we pass to it it expects angle value in radians and it will return a value between -1 and plus 1. if you keep calling it and passing it every increase in angle value as we are about to do here it will endlessly cycle by small increments between -1 and plus 1 creating wavy movement pattern so called sine wave so every time update method runs i will increase this.angle from line 79 by 1. that angle value is being passed to math.sine function here at the same time you can see it makes the ghost shake up and down we can also multiply it times a certain radius value this will make the curve larger like this we need to be increasing angle much slower let's try 0.1 per frame 0.05 0.02 you can see how that affects the movement curve i change radius value and angle value let's randomize the wave size for each ghost i create a property i call for example this dot curve it will be random number between 0 and 3 and i use it here now some ghosts move in smaller some in larger waves i reduce the number of enemies by increasing enemy interval on line 13. let's add a new anime every 500 milliseconds for example so with ghost class we are extending draw method to make it semi-transparent and update method to give it wavy vertical movement worm class is just using the base draw and update methods from its parent anime class let's create another child class and let me show you a completely different type of movement for the third enemy type i just copy worm class here so that i don't have to write all of that again i will call it spider i check my sprite sheet sprite width width of a single frame is 310 pixels sprite height is 175 i want spiders to start from just behind the top edge of game area so zero minus distorted height their horizontal speed will be zero i only want them to move up and down image will be spider referencing this id from line 14 in index html i give them v y velocity y property and set it to 1. now i create update method i pass it delta time first i call update method from parent anime class from line 46 and then i add some additional code at first let's just increase vertical speed by one on line 15 i add spider to my enemy types and here i say else if random enemy is spider push new spider object into enemies array my spiders are not showing and that's because yes they just move down and their horizontal coordinate is off canvas so we can't see them i set their horizontal coordinates to be a random number between zero and game with nice we have spiders here let's make them move up and down let's say if this dot y is more than 200 pixels from the top revert this v y to a negative number since we are adding this dot v y to vertical position here on line 110 if it becomes a negative number it will make the spider go up awesome we have spiders going up and down what about instead of hardcoding 200 here each spider will have a different maximum movement range this.max length is a random number between 100 and 300 for example actually between zero and game height might be better now i just replace hardcoded 200 with this.max length here i also want each spider to move up and down at different speed so this dot v y is a random number between 0.1 and 0.2 i should also multiply it times delta time so we get consistency across different machines i want the spiders to swing up and down from a spider web so let's extend draw method as well and draw it first i will call draw method from parent class from line 51. here on line 88 i need to pass ctx as an argument i forgot to do that we don't want to use global variables inside our classes so on line 114 i pass draw method ctx i call draw from its superclass from enemy parent on line 51 to run all the code in there and also i will draw a spider web here so begin path to start a new shape build in canvas move to method to set starting coordinates of the line let's pass it coordinates 0 0 for now line 2 will be the ending coordinates of my line so this dot x this dot y i want it to be where my spider is at the moment i stroke the lines and here they are we have spider webs coming from coordinates 0 0 2 coordinates of my spiders let's change the starting coordinates to be exactly on top of the spider horizontally so this dot x horizontal and zero vertical this dot x position is top left corner of my enemy rectangle so if i want the web to come from the middle i need to take this dot x position and add half of enemy width to it i also do that for the starting coordinates i can see there is a small vertical gap between spiders and the ends of their webs so let's do this.y plus 10 pixels to make the lines a bit longer notice that i'm not actually animating the sprite sheets we are just showing the first frame for each anime type the best thing about our code structure now is that i can just write sprite animation logic once and it will automatically be applied to all spiders worms and ghosts i go up to line 46 inside constructor on enemy class where i declared properties and values that are shared between all enemy types in my game we will need frame x property which will cycle between 0 and 5 to specify which horizontal frame from the spreadsheet we want to show i also declare this.max frame which could be specific to each enemy but in this case all my enemy sprite sheets have 6 frames we count from frame 0 so max frame is 5. i also need to account for delta time when animating frames to make sure it runs at the same speed on slow and fast computers we already done it once today i will have a frame interval variable so let's say 100 milliseconds and then i will have one accumulation variable that will be going from 0 accumulating delta time until it reaches my frame interval value i will call it for example this.frame timer i set it to 0 initially i check if this dot frame timer from line 49 is more than frame interval from 948 if it is i do something else i increase frame timer variable by delta time now i just realized we need access to delta time here so all this code actually needs to be inside update method here now i can say frame timer plus equals delta time remember that delta time is milliseconds between frames so fast computers will surf frames of our game animation very fast but will have smaller delta time on the other hand slow computers can't surf frame so often but their delta time is larger so as a result it evens out and both fast and slow computers will reach frame interval of 100 milliseconds at the same time this will ensure all the movement and animation in our games has the same timing regardless of speed of the machine we run the code on frame timer will start at 0 it will be increasing by delta time and when it reaches frame interval we can move to the next frame in sprite sheet i have to check if current frame x from line 46 is less than max frame from line 47. if it is slice we increase frame x by 1. else we set frame x back to 0. at the same time i set frame timer back to 0 so it can start accumulating delta time again on line 46 i set frame x initially to 0. now i'm cycling between frame x 0 and frame x 5. to actually display that in game i have to replace hard-coded source x and source y properties in draw image method which i set to 0 0. i have to replace them with my new variables source y can actually stay hard-coded to 0 because our sprite sheet has only one row there will be no vertical navigation within this sprite sheet source x argument will be frame x from line 47 times sprite width from line 70. that way when frame x is 1 we cut out this frame frame x2 is this frame and so on i have entire episode on this as part of this series if you want more details about how to animate sprites properly and in that episode i also show alternative techniques how to do this i can also add more functionality here in update method on enemy class and it will be shared across all my enemy types it could be for example collagen checks with player some javascript generated sounds enemies can have particle effects and so on being able to write code here in one place and share it for all my anime subclasses will save a lot of code repetition that would happen otherwise on line 53 i'm checking if enemies have moved behind the left edge of screen and i'm marking them for deletion this check works for both ghosts and worms as they move from right to left the problem is that spiders never move past that point they have no horizontal movement so as you can see in the console if i let my game run it will just keep accumulating more and more spiders i need a different check here to mark spiders as ready to be deleted i go inside update method on spider class and i say if this dot y is less than 0 minus this dot height times 2 then mark it as ready for deletion spiders start at 0 minus distal height as declared here on line 114 they will move down and then they will move back up when they reach point 0 minus this dot height times 2 we delete them i will be extending classes a lot in my games so i hope you learned something new today creating child classes is not only useful for animations and game development but it's a very important javascript technique with many different applications across all front and web development i think sometimes it's better to learn these concepts on fun projects like this to learn more fundamental javascript techniques on creative coding projects check out related videos in the video description great job on completing today's course say hi in the comments
Info
Channel: Franks laboratory
Views: 10,147
Rating: undefined out of 5
Keywords: JavaScript games, monster variety, enemies in javascript games, npc animation, JavaScript game, javascript game development, javascript crash course, franks laboratory, game development, intro to game development, game development tutorial, game development course, game dev, game development in javascript, javascript games, make javascript games, javascript tutorial, game development for beginners, javascript course, javascript tutorial for beginners, html, html canvas
Id: aEDADLtLEbk
Channel Id: undefined
Length: 55min 5sec (3305 seconds)
Published: Thu Sep 23 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.