JavaScript GameDev Tutorial – Code an Animated Physics Game [Full Course]
Skills:
JavaScript Fundamentals90%
Key Takeaways
This video tutorial demonstrates how to code an animated physics game using JavaScript, covering topics such as game development, web animation, and front-end web development. It utilizes tools like JavaScript, HTML, CSS, and Canvas to create a polished and responsive browser-based game.
Full Transcript
Frank dwark is back with a new JavaScript game tutorial featuring interesting physics and AI mechanics he's included custom-made royalty-free assets for you to use and they'll guide you step by step to create a polished and responsive browser-based game [Music] what makes a perfect game detailed handcrafted visuals fantasy environments and a wide variety of animated characters or is it more about the game mechanics physics for interactions and AI to make the creatures feel alive what is this special ingredient in game development recipe in this class we will dive deep into the secrets of JavaScript web animation and front-end web development let's try to discover what makes a great game and how we can build it from start to finish using just our own JavaScript code with no Frameworks and no libraries this class is for beginners but some basic knowledge of front-end web development is required to get them maximum value let's go I'm giving Olay a ton of free game art with this class I hope you like it we will control the blue ball its job is to protect hatching eggs from waves of hungry enemies player can position all the game objects by pushing them around we can push the ax and hatchlings to safety or we can push the enemies out of the way while building this project I will show you how to use HTML CSS and plain vanilla JavaScript to implement many important web animation and game development techniques we will apply physics to make game objects interact with each other we will learn how to restart our game by pressing a button how to control FPS of the whole game how to trigger periodic events we will apply Mouse controls learn how to manage and animate 8-directional Sprite sheets we will trigger and animate particles when a certain event happens and much more let's take it step by step to make sure we really understand the code and by the end of this class you will have all the skills you need to build your own games and animation projects I create an IMG element with an ID of overlay The Source will be overlay.png you can download all project art Assets in the resources section below there are individual images and Sprites as I'm using them in this class as well as a bonus folder with source files where each game object comes split into high resolution pieces you can edit and animate so if you want you can mix and match combine them with art I gave you for other classes where we are using this mushroom Forest theme and you can create your own unique game environments all the art in this class is copyright free feel free to download them modify them and reuse them in your own projects in any way you want I'm already giving you all art you will need to follow this course but for those of you who want to take it further if you have a graphics editor like Photoshop you can for example color shift the images to create even more visual variety character source files can also be raked and animated in 2D Sprite tools like dragon bones or spine check out the source files and use them however you want it's my gift to you as a thank you for spending your time with me so we have a basic setup in index.html and style CSS canvas defaults to this small size of 300 X 150 pixels if I set its size with CSS I would be setting only its element size and that would stretch my drawings HTML canvas has actually two sizes element size and drawing surface size that can be set independently I want both sizes to be the same to prevent any distortions so I will size my canvas with the JavaScript here I wrap everything inside load event listener because we will use a lot of art assets and I wanted to make sure all my images are fully loaded and available before any JavaScript code runs first we need to point JavaScript towards our canvas element using get element by ID we save that reference in this custom variable I call for example canvas then I take that variable and from it I call build in get context method passing it 2D as context type argument this will initialize a built-in object that holds all canvas properties and drawing methods we can now call them from this CTX variable so as I said before I want to set canvas size both element size and drawing surface size to the same value we can do it like this canvasa dot width is 1280 pixels and canvas height is 720 pixels now the full background artwork I prepared for you is revealed perfect [Music] I wanted to ride this game as object-oriented code base to make it more modular we will have a class for player and another class for game to manage all the game logic the main brain of this code base we will also need animation Loop to draw and update our game over and over to create an illusion of movement game class Constructor will expect a reference to Canvas element as an argument like this inside we convert it into a Class Property and we will need with property and the value will be this dot canvas from line 15 DOT with like this this will give us 1280 pixels as we set it up on line four we do the same thing for this dot height we are taking a reference to Canvas element and we are setting the width and height of our game to be the same as the width and height of canvas we will finish this setup by connecting this canvas argument to this canvas variable a little bit later when we create an instance of our game class using the new keyword we will get there soon I'll show you before we do that I also need the player to have access to width and height properties of our game because the player needs to know for example when it moves outside the game area and so on I will give it access to the entire game class and all its properties and methods by passing it a reference to this game class as an argument like this inside we convert it to A Class Property keep in mind that I'm not creating a copy of game object when I create player objects in JavaScript are so called reference data types so this dot game here on line 9 doesn't create a copy it just points to a space in the memory where our main game object is stored all the code inside a class Constructor gets triggered when we create an instance of a class using the new keyword we will do that in a minute I want our code base to automatically create player when we create an instance of our main game object so I can do this inside the class Constructor I create a property called this dot player and I set it to new player like this I can see that player class Constructor on line 8 expects game as an argument so I pass it this keyword since here we are inside that game class this keyword here refers to the entire game object here on line 18 we are creating an instance of player class and we are saving it as a desktop player property on the game class structuring our code like this will automatically create player when we create game we create an instance of game object like this custom variable I call for example game and I set it equal to new game on line 14 I can see that the game class Constructor expects canvas as an argument so I pass it canvas variable from Line 2. This variable will get converted to a class property and the width and height of the game area will be extracted from it as we plant let's check if everything worked by console login this game variable nice I can see the correct width and height properties and we have an instance of player class in there as well this is one of the ways how you can organize and connect your objects in an object-oriented JavaScript code base keep in mind that the order in which we Define the classes matters Javascript file is read line by line from top to bottom JavaScript classes are hosted but they are not initialized until that particular line is right so player class needs to be defined before it's used a very good idea would be to split our JavaScript into individual modules and Import and Export our classes between files as needed for this project I will write all the code in a single Javascript file to keep it as beginner friendly as possible using JavaScript modules would require us to run this code through a local server it wouldn't run The Code by simply opening index HTML file in a web browser anymore if you are more experienced it will be very easy for you to finish the project with me and then if you want you can split individual classes into separate modules yourself this class is for beginners so let's focus on object oriented principles and animation techniques [Music] a function that sits on an object is called a method player will need a draw method to draw and animate it it will expect context as an argument to specify which canvas we want to draw on we will connect this context argument to our CTX variable from line 3 when we call this draw method a little bit later I will show you I wanted to draw a simple Circle at first representing our player to draw a circle on canvas we take context and we call begin path to tell JavaScript we want to start drawing a new shape and we want to close previous shape if there are any then we call built-in Arc method which expects at least five arguments it expects X and Y coordinates of the center point of the circle its radius start angle in radians measured from the positive x-axis and end angle where the arc ends again in radians measured from the positive x-axis there is an optional sixth argument for counterclockwise if we don't Define it it will default to false which means that the arc will be drawn clockwise so start angle is zero radians and end angle is mastered pi times two it's a full circle now we can choose to call fill to fill the shape with color or stroke just to outline the shape or we could use both we will do that soon how do we actually draw the player on canvas now on the game class I create a method I call for example render this method will draw and update all objects in our game it expects context as an argument inside I take this dot player from line 23 and through this reference we access the draw method on player class we just defined from Line 11. This method contains the code to draw a circle representing the player I can see it expects context as an argument so I pass it along this context we passed to the render method now I can take this game variable that holds an instance of the entire game class and from there I call render and I pass it to CTX from line three that CTX will be assigned a variable name context here and it will be passed along to draw method on player class we are drawing a black circle representing the player it's here maybe you can't see it so let's give it different X and Y coordinates to move it instead of hard coding all these values I want to create properties on the player class and then use those here we will need X and Y coordinates for player position but because in this class we are learning about position and hitboxes and character images that can have very different shapes and sizes I will have to create property for X and Y position of player hitbox and I will need to have a different X and Y property for player Sprite sheet image it will make more sense as we build it I want to be very explicit with my variable names to make it absolutely clear which value is the position of the Collision box and which value is the position of a Sprite sheet so instead naming these properties just X and Y I will name them Collision X and collision Y X and Y position of the Collision hitbox of the center point of the Collision Circle all objects in our game today will have circular hitboxes because I wanted to show you how to make them push and slide along each other I want the starting position of the player to be exactly in the middle of the game area so Collision X will be this dot game from line 9 and from that property I will extract with from line 23. and in the middle so terms of 0.5 Collision y will be the same this Dot Game dot height times 0.5 now I can use these values as X and Y arguments but to Canvas Arc method like this now we can move the player around by changing values of collision X and collision y properties we will also need a property called Collision radius which will Define the size of this circular player hitbox I use it here inside the arc method as well default fill color is always black I can override it here by setting canvas fill style property to White like this instead of filling the shape with color we could also just stroke it Again by default line width of stroke is one pixel and the color is black I set the line with 2 3 pixels and I set stroke style to White notice I'm defining these canvas properties outside of any class or method I do that on purpose because this code will only run once on the initial page load you can't always do that if you have multiple objects with different fill Styles and stroke colors in that case you would have to Define these properties inside the draw method and switch between them over and over the problem with that is that the draw method will be called 60 times per second and change in kind of a state like this could get performance expensive it's a good idea to structure your code in a way where you change kind of a state as little as possible and when I say canvas State I mean anything from transforms to changing colors of fill style and stroke style that's why I put this code here instead of placing it directly inside the draw method to make it run as little as possible while still applying the colors and settings as I need them I can also call Phil here so now we are filling and stroking the same path defined by Arc method I want the fill to be white but I want it to be slightly transparent canvas has a global Alpha property to set opacity of the shapes we are drawing the problem is that when I set Global Alpha to a different value everything drawn after that will be semi-transparent I want the transparency to only apply to the fill color of player Collision Circle to limit certain kind of a settings only two specific draw calls we can wrap that drawing code between save and restore built-in canvas methods then if I set Global Alpha to 0.5 it will affect only that specific draw in action in our case it will only affect the fill of this circle so save method creates a snapshot of the current canvas State including its fill style line width opacity as well as Transformations and scaling if we are doing that then I can do any changes to that kind of a state I want in this case I just set opacity to 0.5 this fill call will be affected by that changed opacity and then we call Restore restoring all canvas settings to what they were when we first called its Associated save method for that reason this stroke will not be affected by reduced opacity save and restore methods allow us to apply specific drawing settings only to selected shapes without affecting the rest of our canvas drawings [Music] I want to move the player around using Mouse we already know that the code inside the game class Constructor will be executed at the point where we create an instance of this class using the new keyword we are taking advantage of that by automatically creating an instance of player class here we can actually run any JavaScript code in here I can even put event listeners here to make sure they are automatically applied when I create an instance of game class I create an event listener for Mouse down event when Mouse button is clicked the code inside this callback function will run I test it by just console login the word Mouse down if I save changes because I'm already instantiating this class online 46 this event listener is automatically applied now when I click on canvas control lock is triggering nice callback function on event listener also generates an event object that contains all kind of information about the event that just happened to get access to that object we just need to give it a variable name you can name it whatever you want but the convention is usually event or E let's consolock this event I click on canvas and I see it here I inspect it you can see it contains a lot of information about that Mouse click for example we see X and Y coordinates of that click here there are many other properties that tell us which Mouse button was pressed and many other things I want to take the coordinates of the click and save them as properties on the main game object and from there we will be able to access them from our player object as well I create a new property on game class called this.mouse it will be an object with X property with default value of this dot width times 0.5 and Y will be distal height times 0.5 so the middle of canvas horizontally and vertically we will also want to monitor when the mouse button is pressed down initially it will be set to false if I consolok e.x and e.y you can see we are getting X and Y coordinates as we click around canvas the problem is that the coordinates are from the top left edges of the browser window I would like to measure the click coordinates from the top left corner of canvas instead so when we click here in the top left corner of canvas we get X and Y 0 0. for that we can use a different property on this auto-generated event object called offset X which will give us horizontal coordinate of the click on the target node in our case the target node the target of the click is canvas element now you can see the values get very close to zero as I click close to the edge of canvas I could also add the event listener just to the canvas element itself rather than the entire browser window object if I click closer to the top left corner we are getting values close to zero zero perfect probably it would make sense if I use this dot canvas property here from line 31 instead since we are inside a class and we have the reference to Canvas available here so now we are getting coordinates of the click measured in pixel distance from the top left corner of canvas we resize the browser window this is working well I want to save these click coordinates inside our custom Mouse property so that they are available to other objects in our code base such as the player inside the mouse down event listener I take that this.mouse.x property from line 36 and I set it equal to e dot offset X this.mouse.y will be e dot offset y I create another console log and I will log these newly updated Mouse properties when I click on canvas we get an error that says cannot set properties on undefined setting X on line 43. it's telling me that I can't set X property on something that is undefined for some reason this.mouse is undefined when accessed from inside event listener it is because when this callback function on event listener runs it forgot it was originally defined inside this game class Constructor it forgets what this keyword stands for this is expected to make the event listener remember where it was first defined where it sits in the lexical scope of our code base we can simply use es6 Ro function here instead one of the special features of es6 Ro functions is that they automatically inherit the reference to this keyword from the parent scope Arrow functions remember where in the code base they were originally declared lexically and they adjust their disk keyword to point to the correct object to the parent object now this.mouse.x and this.mouse.y are correctly updating to the new values making the current Mouse coordinates available all over our code base whenever they might be needed later I delete the console logs when Mouse down event happens I set pressed from line 38 to true I copied this event listener this one will be for Mouse up event when the mouse button is released everything here will stay the same and reset pressed to false I also create an event listener for Mouse move event let's console log it to check yeah that's working let's make the player move [Music] I create a custom method I call update inside I set Collision X from line 14 to the current Mouse exposition and collision y will be the current Mouse y position like this to run this code we actually need to call the update method I will call it from inside render down here I delete this console log if we want to see any Movement we need to be calling render over and over so let's put it inside the animation Loop here I call built-in request animation frame a method which sits on the browser window object but we can also call it directly like this if we want I pass it animate the name of its parent function to create an endless animation Loop now I need to call animate to actually start the animation when I move Mouse over canvas we get Trails I only want to see the current animation frame so between every loop I use built-in clear rectangle method to clear the old paint I want to clear the entire canvas area from coordinates00 to canvas with canvas height now the player sticks to Mouse as we move it around canvas perfect I want to create a line between Mouse and the player to clearly show the direction in which the player will move inside the draw method on player class we start a new shape by calling begin path move 2 method will Define starting X and Y coordinates of the line in this case I wanted the line to start from the coordinates of the player object line 2 method will set the end in X and Y coordinates of the line in this case it would be X and Y coordinates of the mouse then we call stroke to actually draw the line this works but since the player is always able to catch up with mouse cursor so fast we can barely see the line let's give player speed speed X horizontal speed initially I set it to zero speed y vertical speed also initially sets to zero inside update method we will calculate speed X first I set it to hard-coded one pixel per animation frame and I increase player Exposition by horizontal speed that worked I also do it for vertical position there are two ways we can make player follow the mouse one way would be to Simply take the difference between the current Mouse position and the player position on horizontal x-axis and set that difference as horizontal speed and we also do that for vertical movement now the player position is correcting for the difference by the entire amount of that distance so it makes the movement instant what if I make it move only by the 120th of the difference between player and mouse position per animation frame horizontally and also vertically I create class properties for DX this stands between Mouse and player horizontally and Dy vertical distance I replace those values here it's easier to read this way I don't want the player to follow all the time as we move Mouse over canvas I won't only when we click somewhere or when we hold Mouse button down and move around inside Mouse move event listener I say only update X and Y Mouse position if Mouse is pressed now I can click around to make the player move to that location or I can drag that point around while holding Mouse down perfect the problem with this technique is that the speed is not constant player moves very fast at first because 1 20th of the distance is at first a big chunk when they are far apart but as they get closer 1 20th of that distance becomes smaller and smaller amount of pixels to be traveled per animation frame you might want this particular motion for your project but for the game we are building today I wanted the player to move at a constant speed we will have to use the second technique for that inside update method I calculated the distance we already have DX the distance between Mouse and the player horizontally we also have DUI the distance between Mouse and the player vertically we want to calculate the distance between these two points we can do that by calculating hypotenuse the longest side of this imaginary right triangle we can use Pythagoras Theorem formula or in JavaScript we have this built-in mastered hypotenuse method this method will calculate the length of the longest side for us if we pass it to other sides of the triangle as arguments keep in mind it expects DUI first and DX second which might be a bit unexpected if you never saw this before horizontal speed is the ratio between DX horizontal distance between Mouse and the player and the actual distance same with speed y it will be the ratio between the distance on vertical y-axis and the actual distance between the two points as a backup when some of these values are undefined at first we say or zero like this we are dividing horizontal and vertical distance these sides by the actual distance represented by the longest side of a right triangle DX and d y is always a smaller number than the distance because the distance is hypotenuse the longest side for that reason the values we get as speed X and speed y will be somewhere between 0 and 1. that will give us the correct direction of movement at a constant speed there is much more to be said about this technique but for now this is all we need to know I'll get back to this now the player is moving at a constant speed towards the mouse I can have a speed modifier I set it to 5 for example I use it down here and I multiply speed X and speed y by that modifier after we add the speed modifier the player Circle will actually never stay still anymore it will be swinging back and forth in this case by 50 pixels because the speed modifier pushes it too far in both directions I can fix it by saying only move the player when the distance between Mouse and the player is more than speed modifier else set speed X to Zero and speed y to 0 as well this works perfect so we covered one simple and one more advanced technique to make the player move towards the mouse now it's time to add solid randomized non-overlapping obstacles [Music] I create a class I call obstacle [Music] Constructor will expect the game as an argument and inside I convert that reference to a class property same as before it will be pointing towards the main game object and we need it here because through this reference we have access to game width and height Mouse positions and some other properties we will be adding later we will have access to all these values from inside obstacle class through this.game reference from line 54. as I explained before all the objects in our game will have a circular collision hitbox and a separate rectangular Sprite sheet for that reason I will be calling these properties with a very descriptive names to make sure it's very clear what's happening when we are moving and animating everything later Collision X the center point of collision circle of each obstacle will be a random value between 0 and the width of the game that width is coming from line 62 here and we are accessing it through this Dot Game reference we created online 54. we will also need Collision y vertical center point of collision area Circle it will be a random value between 0 and game height this value Collision radius will be 60. we will also need a draw method that expects context as an argument I want that Circle that represents hitbox area of each obstacle to look the same as the circle representing the player so I take the drawing code from up here just for the circle so this code block I copied and I paste it down here this code will work here because the same as with the player we gave our obstacles properties called Collision X Collision Y and collision radius we will also need the same naming on all these properties between different object types in case we want to have a reusable Collision detection function I will show you how to use that one later it's simple anyway here we have a code to draw a circle with a radius of 60 pixels with 50 opacity wide fill and white fully visible full opacity stroke this obstacle class here is a blueprint we will use it to create individual obstacle objects the actual logic to create and manage these objects will be down here inside the main game class which is the main brain of our code base I create a property called this dot obstacles it will be an array that holds all currently active obstacle objects it will start as an empty array at first the number of obstacles will be for example 5. I create a custom method on our game class I call for example init initialize its job for now will be to create five randomized obstacle objects and put them inside obstacles array we just defined inside I create a for Loop it will run five times because we set number of obstacles to 5 up on line 76. each time it runs it will take this dot obstacles array from line 77 and on it it will call built-in array push method push method adds one or more elements to the end of an array and it Returns the new length of the array I will pass it new obstacle like this the new keyword will look for a class with the name obstacle and it will trigger its class Constructor up online 53 I can see that obstacle class Constructor expects the game as an argument down here init method sits inside that game class so I pass it this keyword which here represents the entire game object with all its properties and Associated methods making all of these available from inside obstacle class now icons look a game and I can see obstacles array is completely empty to fill it all I have to do is call init method we just wrote like this now I can see the array contains five obstacle objects I double check to make sure all properties have values if you see undefined in any of these it means there is a problem in your code base all is good here same as I'm drawing and updating the player from inside game render method here I would like to draw all five obstacle objects on canvas I take obstacles array from -77 we already know that it contains five objects and that each of these objects was created using our custom obstacle class from ion 52 so they all have access to this draw method we defined on line 59 so here inside render I take that obstacles array and I call built-in array for each method the for each method executes a provided function once for each array element first we need to Define a variable name which will be used within this for each method to refer to individual objects in that array I will call each object obstacle so for each obstacle object in obstacles array I call their Associated draw method from line 59. on line 59 I can see that it expects a reference to context as an argument to specify which canvas element we want to draw on it's along this context that was passed to the parent render method nice we are drawing one player and one two three four five randomly positioned obstacles I go up here and I make the obstacles a bit larger every time I refresh browse the window they get positioned randomly somewhere within the canvas area because that's how we Define their position on lines 55 and 56. what if I want to make sure that the obstacles never overlap like this and maybe to take it even further since these will be solid obstacles that player can't move through and has to walk around them I would also like there to be a minimum spacing between them and also between the edges of the game area just to make sure all the creatures that will soon be crawling here don't get stuck and can eventually find their way automatically around each obstacle it's actually easier to implement all of that than you might think but we have to take it step by step and explain a couple of Tricks we can use here to achieve that [Music] inside init method we are simply adding five randomly positioned obstacles right now I have to delete this we will need to structure this code a bit differently here so first I want to make sure the obstacles don't touch that they don't overlap like this we could also adjust the number of obstacles to be the maximum number of circles possible that can fit into a certain area without any two of them overlapping sometimes we call this circle back in so let's write a very simple Circle backing algorithm here I will use the basic technique where you just try to place circles at random positions many times and only those that don't collide with already existing circles will actually be turned into obstacle objects and drawn this is also called a Brute Force algorithm it's not very smart it just tries over and over many many times I will create a lead variable called attempts it will be my safety measure we will count how many times we tried to draw a circle and we will give up after a certain number of attempts the Assumption being that there must have already been enough opportunity entities to place the obstacles I will use a while loop you have to be careful with this one if you create an infinite while loop you will slow down your browser and you will need to restart it very old computers might even freeze if you use while a loop wrong new browsers can usually deal with it my goal here is to randomly Place circles over and over and before we actually turn that circle into obstacle object we check if it overlaps with existing circles only if it doesn't overlap we add it into the obstacles array I want this while loop to run as long as optical's array length is less than number of obstacles less than five we defined that array here and number of obstacles was defined here as a backup I also set a secondary condition only continue running this while loop as long as attempts is less than 500. this is important because if I set radius of an obstacle to be a very large number or I set number of obstacles to be so large that they can't physically fit into the available area we would get an endless while loop but with this secondary condition JavaScript will just try 500 times and if by that time they couldn't find placement for all the obstacles it will give up think 500 attempts is more than enough every time the loop runs we have to increase attempts by one for our safety backup plan to work every time this while loop runs we create a temporary object I call for example test obstacle it will be equal to the new obstacle and I pass it again this keyword as an argument as we did before let's consolock this test obstacle nice we have 500 test obstacles in console now you can see they have Collision X Collision Y and collision radius properties as they should my goal now is to take this temporary test obstacle object and compare it against every other obstacle in obstacles array of course at first this array is empty so the first obstacle should always be placed without issues the second test obstacle will compare itself with the first one that's already in the array and so on so for each obstacle in obstacles array I will run a circle Collision detection formula Circle Collision detection in JavaScript is quite simple we basically need to calculate the distance between the two Center points of those two circles then we compare the distance between two Center points with the sum of the radii if the distance is less than radius of circle 1 Plus radius of circle 2 they overlap if it's exactly the same the circles are touching if the distance is more then the sum of radii there is no Collision we already did this when measuring the distance between player and mouse this time the two points we want to measure the distance in between is the center point of obstacle Circle one and center point of obstacle Circle 2. so again we are creating this imaginary right triangle where DX is the difference between two points horizontally Dy is the difference between the two points vertically and the actual distance is the hypotenuse of that triangle so here we use Pythagoras Theorem formula or a built-in method hypotenuse method passing it to DUI first and DX second so now we know what is the distance between the two Center points sum of radii is a radius of circle one in this case radius of test obstacle and the second one is the radius of whatever obstacle object inside obstacles array we are currently cycling over as we said if the distance is less than sum of radii how will I do this outside the for each method I create a flag a lead variable I call overlap and initially I set it to false if the distance is less than sum of radii we set overlap to true because Collision was detected outside the for each method if overlap is still false after we created test obstacle and after we compared it using Collision detection formula with every other existing obstacle in the array if it doesn't collide with any of them and overlap a variable is still false after all these checks only then we take obstacles array and we will push this test obstacle that passed our checks into the array now when I refresh the game five obstacles will be randomly positioned and they will not be overlapping because those that do overlap are discarded and only non-overlapping circles are used because I have my safety measure here on line 108 and we always stop this while loop when we reach 500 attempts I can actually go up here and I can set the number of obstacles to a large number that I know will never fit our code will just place as many obstacles as possible and then it will stop trying we know this is working because if I keep refreshing my project over and over we never see overlap in circles I mean no obstacles overlap with each other player can overlap at this point with obstacles we don't care about that right now I set the number of obstacles to 10. [Music] now let me show you how we will be attaching images to the circular collision hitboxes and how to position the image in relation to the hitbox so that it makes Visual sense and creates an illusion that this is not flat canvas but a three-dimensional environment where the player can actually walk around these obstacles you can download all project art Assets in the resources section Below in index.html I create another image element with an ID of obstacles and Source will be obstacles.png that image is a Sprite sheet we will randomly cut out one of these frames for each obstacle object I don't really want to draw the actual image element so I hide it with CSS inside obstacle class Constructor I create a new property I call this dot image I point it towards that obstacle Sprite sheet using get element by ID like this let's set the number of obstacles to one for now inside the draw method on obstacle class I call built-in canvas draw image method this method needs at least three arguments the image we want to draw so this dot image from line 58 and X and Y coordinates where to draw it I will draw it at this dot Collision X and this dot Collision Y at first doing this will simply draw the entire image Sprite sheet and the top left corner of this Sprite sheet will be starting from the center point of obstacle Circle because this is how by default images and circles are drawn on HTML canvas if I refresh the project our new obstacle is positioned randomly somewhere on canvas I created this Sprite sheet for you so I know that individual frames are 250 pixels wide I save that value as Sprite width variable Sprite height will also be 250 pixels if you are using a different spreadsheet you can get the width by dividing the width of the entire Sprite sheet by the number of columns and the height is height of the Sprite sheet divided by the number of rows in case we want to add scaling later I will also create independent width and height properties for now they will be equal to Sprite width and Sprite height because I sized the Sprite frames to the exactly same size as I want them to be drawn in the game draw image can also accept optional fourth and fifth arguments defined in the width and height the entire image will be squeezed or stretched to the area we defined by these values it will look like this what I actually want to do is to crop out one of these 12 obstacles and draw only that one at the size of 250 times 250 pixels for that I need to use the longest version of draw image method that expects 9 arguments those nine arguments are the image we want to draw Source X Source y Source width and Source height of the area we want to crop out from the source image and destination X destination y destination with and destination height to Define where on destination canvas I want to place that cropped out piece of image onto so if I pass it 0 as a source X and 0 as Source height and Sprite with Sprite height like this as Source width and Source height I need to spell width correctly so now we are drawing the top left frame in our Sprite sheet as I said before I will set separate X and Y position for the Sprite sheet there are multiple different ways to do this I can just simply position the image directly on top of collision X which is the center point of collision Circle minus the width of the image times 0.5 this will Center the image horizontally exactly over the Collision Circle to actually apply this I need to use Sprite X as destination X property passed to draw image method here be careful when passing arguments to draw image method the order in which you pass these arguments is very important okay if I refresh the page I can see it's been correctly centered horizontally I do the same thing for sprite y and I use it as destination y property passed to draw image method now the spreadsheet is directly on top of the Collision Circle I set Collision radius to a smaller value I want this small Collision area to be positioned at the base of the plant where the stone is because that will be the solid area that's touching the ground that our game characters will have to walk around since our Sprites are set size of 250 times 250 pixels I can actually use a hard-coded value here plus 40 we'll move it up minus 40. minus 50. minus 60 minus 70. yes this seems all right I said that the number of obstacles to 10. [Music] what if I want to make sure that not obstacles so don't overlap but also that there is a additional minimum 100 pixel space in between so that they are more evenly spaced out around the available game area as well as allowing enough space in between the obstacles so the game characters can easily walk around them I create a helper variable I call for example distance buffer and I set it to 100 pixels like this then I include the distance buffer here in sum of radii to apply this buffer in between obstacles when we are placing them nice to make sure this is working I increase distance buffer to 150 pixels that should make it even more apparent yeah so this is how we can easily control obstacle spacing I also want to make sure that obstacle Sprite images are entirely drawn within the game area and not partially hidden behind the edges I could have done this inside obstacle class Constructor when defining these values initially or I can also just do it here since we are not drawing that many obstacles and their positions are calculated only once on the first page load anyway I make sure the left edge of obstacle Sprite sheet is more than zero so it's not hidden behind the left edge of canvas at the same time I make sure that the right Edge is not hidden so Sprite X must be less than the width of the game area minus the width of the obstacle nice when I refresh the page I can see horizontally obstacles are always fully visible for vertical position I want to check the image but at the center point of collision a circle more than zero vertically will not be enough I want to Define an area that is reserved for this background artwork I don't want to Grant obstacles to appear over this area I create a property called top margin I guess a desktop area is around 260 pixels of height let's check here yes 260 looks alright because I want to make sure the base of the obstacles doesn't overlap with this top area but I don't mind if the top of the obstacle Sprite sheets overlaps like this because this looks like the obstacle plant is standing in front of the background Forest View so this is fine I will also check if the center point of obstacle Collision area circle is less than the height of the game area I want some margins I can for example create a helper variable that's equal to Collision radius of the test obstacle times two I replaced this hard-coded value with this.top margin property we defined plus I want to give it some additional top margin so that characters and special enemies can squeeze in between obstacles and game boundaries when walking across the game field horizontally from right to left I will also account for the margin from the bottom of the game area to create some space there we wrote code that automatically places obstacles in our game World these obstacles never overlap and their correlation areas are placed to allow enough space in between them this will make the next steps much easier because we need enemies and friendly NPCs to be able to automatically walk around them using very simple artificial intelligence we have different images for obstacles but right now we are drawing only the first top left frame at coordinate 0 0. we can crop out different areas from the obstacles Sprite sheet horizontal crop area will start from the position we pass as Source X argument to draw image method here zero times Sprite width is this Frame one times Sprite width will be this Frame two is this one three is this one back to zero to select from which row we are cropping we use Source y argument here again we multiply row number by the actual height of individual Sprite frames so zero times Sprite height is this one times Sprite height is this now we are on Row 2 and there is no Row 3 because we start from row zero images are drawn and cropped from the top so instead of hard coding these values that are currently set to 0 0 let's turn them into class properties for cloud Authority and easy control this.frame X will determine which column we are on in our obstacle Sprite sheet if I do a random number between 0 and 4 this will not work there is no column 1.74 for example we need integers numbers without decimal points so I wrap it in my third floor to round the random value generated by Mazda random to the closest lower integer this code will give me either 0 or 1 or 2 or 3. so one of our Sprite columns when we multiply these integers by the width of a single Sprite frame and we pass that value as a source X argument to draw image method we are defining horizontal cropping coordinate that worked perfect I will do the same for frame Y which will determine Sprite row we have only three rows this line of code will give me integers either 0 or 1 or 2 corresponding to the number of rows we have available in our obstacle Sprite sheet now we can replace this hard-coded 0 with this dot frame y so Source y argument passed the draw image method will be this dot frame y times this dot Sprite height I will do that in a second random values in frame X and frame y combined will give us random image out of these 12 available obstacles each obstacle object will have random frame from this Sprite sheet assigned to it I will finish this a bit later [Music] on the main game object I create a method I call check Collision I want this to be a reusable utility method that takes object a and object B and it will compare them and check if they are collided or not we will be able to use this all over the code base wherever Collision detection between two circles is needed the way I'm building my game all characters and objects will have a circular Collision area which will be a solid base that nothing can walk through and everything will react and walk around everything using this we can also push things around I will show you to check collision between two circles we have Circle A and A Circle B here we need to check DX first the distance between the center point of Circle A and the center point of Circle B on horizontal x-axis this reusable method will work only if all objects involved have properties with same name and conventions so we will make sure we name X and Y positions on each object as Collision X and collision y I'm using this overly descriptive property names so that it's very clear when X and Y coordinates relate to Collision area Circle and when they relate to image Sprite sheet positions this is a tutorial so I want things to be very clear and easy to understand [Music] we will also need a DUI the difference between the center point of Circle A and the center point of Circle B on the vertical y-axis then we want to know the distance between these two Center points so hypotenuse the longest side of this imaginary right triangle right angle 90 degrees is here and this is the distance Pythagoras Theorem formula or alternatively built-in method hypotenuse method and we pass it Dy first and the X as the second argument to determine whether or not there is a collision We compare distance between these two Center points with radius of Circle A Plus radius of Circle B I will save this value as a custom variable I call for example sum of radii so if this tense is less then sum of radii we know the circles Collide if the distance is the same as the sum of radii circles are touching if the distance is more than the sum of radii we know there is no Collision this function will simply return true if there is collision and false if there is no Collision let's use our custom check Collision function up here inside update method on player class we check for collision between player and obstacles we have a one player object and multiple obstacle objects so to compare all we will call for each on obstacles array which holds all currently active obstacle objects I will call each object in the array with a helper variable name obstacle and I will console log check Collision method we just defined we know it expects circular object a and circle object b as arguments to compare the distance of their Center points to the sum of their radii so I pass it this which means this player object as Circle 1 and obstacle we are currently cycling over with this for each method as Circle B keep in mind that this reusable check Collision method can only
Original Description
Learn how to code a JavaScript Game featuring interesting physics and AI mechanics.
What makes a great game? Is it about beautiful, polished visuals or about gameplay that feels good and responsive? Is it about unique ideas, or maybe it's the little details, special secrets and Easter eggs? What are the ingredients in a perfect game development recipe? Let's explore fantasy mushroom forest theme and learn all the techniques you need to make your own 2D games with vanilla JavaScript, HTML5, CSS3 and HTML Canvas.
✏️ Course created by @Frankslaboratory
⭐️ Assets ⭐️
All project images: https://www.frankslaboratory.co.uk/downloads/109/all_project_images.zip
Background: https://www.frankslaboratory.co.uk/downloads/109/background.png
Overlay: https://www.frankslaboratory.co.uk/downloads/109/overlay.png
Player: https://www.frankslaboratory.co.uk/downloads/109/bull.png
Egg: https://www.frankslaboratory.co.uk/downloads/109/egg.png
Toad single: https://www.frankslaboratory.co.uk/downloads/109/toad.png
Larva: https://www.frankslaboratory.co.uk/downloads/109/larva.png
Toads: https://www.frankslaboratory.co.uk/downloads/109/toads.png
Bonus images, environmental art, HQ separate pieces in PSD format etc: https://www.frankslaboratory.co.uk/downloads/109/bonus_image_source_files.zip
You will learn:
- How to implement a very simple AI to make the creatures feel alive
- How to control the FPS of our game and how to measure time to trigger periodic events
- How to restart the game by pressing a button
- How to apply collision detection, resolve collisions and use that to simulate physics
- How to use the built-in drawImage method to draw randomized game environments and animated characters from a sprite sheet
- How to capture mouse position and animate an 8 directional sprite sheet based on the relative position between the mouse and the player character
- How to use HTML5, CSS3 and plain vanilla JavaScript to build a game from scratch. We will write and understand every line of
Watch on YouTube ↗
(saves to browser)
Sign in to unlock AI tutor explanation · ⚡30
Playlist
Uploads from freeCodeCamp.org · freeCodeCamp.org · 0 of 60
← Previous
Next →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
React: Production Server Setup Part 2 - Live Coding with Jesse
freeCodeCamp.org
cookies vs localStorage vs sessionStorage - Beau teaches JavaScript
freeCodeCamp.org
Browser history tutorial - Beau teaches JavaScript
freeCodeCamp.org
Graph Data Structure Intro (inc. adjacency list, adjacency matrix, incidence matrix)
freeCodeCamp.org
React: Parameterized Routing with Next.js - Live Coding with Jesse
freeCodeCamp.org
React: Dealing with jQuery Issues - Live Coding with Jesse
freeCodeCamp.org
setInterval and setTimeout: timing events - Beau teaches JavaScript
freeCodeCamp.org
Browser and Device Testing - Live Coding with Jesse
freeCodeCamp.org
Last Minute Updates - Live Coding with Jesse
freeCodeCamp.org
Post Launch Updates - Live Coding with Jesse
freeCodeCamp.org
React: Setting Up Google Analytics - Live Coding with Jesse
freeCodeCamp.org
React: Masonry Layout - Live Coding with Jesse
freeCodeCamp.org
Load Balancing Digital Ocean Droplets - Live Coding with Jesse
freeCodeCamp.org
try, catch, finally, throw - error handling in JavaScript
freeCodeCamp.org
Load Balancing: SSL Passthrough Setup - Live Coding with Jesse
freeCodeCamp.org
Graphs: breadth-first search - Beau teaches JavaScript
freeCodeCamp.org
React: Masonry Layout Part 2 - Live Coding with Jesse
freeCodeCamp.org
React: WordPress API Live Search - Live Coding with Jesse
freeCodeCamp.org
Creating WordPress Custom Post Types - Live Coding With Jesse
freeCodeCamp.org
Dates - Beau teaches JavaScript
freeCodeCamp.org
Miscellaneous Front End Updates - Live Coding with Jesse
freeCodeCamp.org
Merging a Pull Request from GitHub - Live Coding with Jesse
freeCodeCamp.org
React + Prettier + Standard JS - Live Coding with Jesse
freeCodeCamp.org
React: Sortable Responsive Table - Live Coding with Jesse
freeCodeCamp.org
Geolocation Sorting by Distance - Live Coding with Jesse
freeCodeCamp.org
Tradeoff Matrix - Agile Software Development
freeCodeCamp.org
The Definition of Ready - Agile Software Development
freeCodeCamp.org
Getting first React job without experience - Ask Preethi
freeCodeCamp.org
React: Google Analytics Click Tracking - Live Coding with Jesse
freeCodeCamp.org
Submitting a PR to an Open Source Project - Live Coding with Jesse
freeCodeCamp.org
Should I go back to school to get CS degree? - Ask Preethi
freeCodeCamp.org
Hero Section CSS Changes - Live Coding with Jesse
freeCodeCamp.org
Working Agreement - Agile Software Development
freeCodeCamp.org
A day at Pennybox with Co-Founder Reji Eapen
freeCodeCamp.org
React: Sorting and Filtering Data - Live Coding with Jesse
freeCodeCamp.org
React: Sorting and Filtering Data Part 2 - Live Coding with Jesse
freeCodeCamp.org
React: Building a New UI - Live Coding with Jesse
freeCodeCamp.org
Definition of Done - Agile Software Development
freeCodeCamp.org
Getting started with jQuery (tutorial) - Beau teaches JavaScript
freeCodeCamp.org
Making a React Blog with WordPress Content - Live Coding with Jesse
freeCodeCamp.org
React, NextJS, CSS - Live Coding with Jesse
freeCodeCamp.org
jQuery events - Beau teaches JavaScript
freeCodeCamp.org
React/NextJS Routing and WordPress API Custom Types - Live Coding with Jesse
freeCodeCamp.org
React: Working with API Data - Live Coding with Jesse
freeCodeCamp.org
React: Refactoring Components - Live Streaming with Jesse
freeCodeCamp.org
jQuery effects - Beau teaches JavaScript
freeCodeCamp.org
More React Refactoring - Live Coding with Jesse
freeCodeCamp.org
animate in jQuery - Beau teaches JavaScript
freeCodeCamp.org
"Finishing" My React Site - Live Coding with Jesse
freeCodeCamp.org
Starting a New React Project (P2D1) - Live Coding with Jesse
freeCodeCamp.org
React Project 2 Day 2: Learning Material UI - Live Coding with Jesse
freeCodeCamp.org
The Agile Manifesto - Agile Software Development
freeCodeCamp.org
jQuery: get and set with http, text, val, and attr - Beau teaches JavaScript
freeCodeCamp.org
React Project 2 Day 3 - Live Coding with Jesse
freeCodeCamp.org
The INVEST approach to product backlog items
freeCodeCamp.org
React Project 2 Day 4 - Live Coding with Jesse
freeCodeCamp.org
Chickens and Pigs - Agile Software Development
freeCodeCamp.org
React Project 2 Day 5 - Live Coding with Jesse
freeCodeCamp.org
jQuery: add and remove DOM elements - Beau teaches JavaScript
freeCodeCamp.org
React Project 2 Day 6 - Live Coding with Jesse
freeCodeCamp.org
More on: JavaScript Fundamentals
View skill →Related Reads
🎓
Tutor Explanation
DeepCamp AI