How To Create Your First Game - JavaScript

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video i'm going to show you how to create an exact clone of the chrome dinosaur game and it's actually quite a bit easier than you'd think [Music] welcome back to web dev simplified my name is kyle and my job is to simplify the web for you so you can start building your dream project sooner and in this video we're going to create an exact replica of the chrome dinosaur game as you can see i actually already have the images that we're going to be using for this project downloaded you can get these from the github that's going to be linked in the description below or you can use your own images if you want these are all the images right here we're going to need and the first thing that i want to do to get started is i'm going to create an index.html file and this is going to be where all the index code for our html goes and here let's just put exclamation point enter to start a boilerplate code and then inside the body we need to actually put all the html and the first thing i want to do is i want to create this html object and it's going to be called world the reason i'm doing this is because i want to put everything inside this world then i'm going to scale everything using percentages and the reason i'm doing that is as you can see as this screen increases you can see every single thing in the game increases in size or decreases in size so that it fits exactly the same so no matter what screen size you're playing on whether it's a horizontal screen a vertical screen so on it's always going to give you the perfect play experience and that's with this world class right here everything is scaled off the size of this world then we can put everything inside of here for example we can put our score which goes in the top right hand corner and by default we're going to start that out as zero we can also put the text that you see on the screen for press to start so we can just say start screen and inside here it'll say press any key to start there we go and then we can actually put some of the game pieces in so the first thing we're going to have is an image this image is going to have the class of ground and inside here for the source we're just going to say images ground dot png and we don't really need to worry about an alt for this and we're also going to make a second copy of this ground and the reason for that is if i click play over here you can see the game is working and the ground is moving along and if i don't lose immediately you'll notice that this ground just essentially repeats every single time that the ground is done it just repeats itself so if we have two ground images we can make sure we stitch them up so that essentially every single time one of the ground pieces would go off the edge of the screen the next one is going to take the place of that ground piece that's where we're going to have two and you'll see in depth a little bit more why we do that when we get to the code for it now the next thing i want to do is actually put the dinosaur on the screen so it's again going to be an image i'm going to have the class of dyno and for the source we want to say images slash dyno stationary because this is going to be the stationary position for our dinosaur now if we save we can open this up with live server and see what we got so let's just open that up and wait for it to load for a second and now as you can see we have our text up here it says zero press any key to start we have both of our ground images and then we have our dinosaur right here obviously it doesn't quite look like a game so let's actually work on some styles up here i'm going to add a link for a css sheet called styles.css then i'm going to create that styles.css where we can put all the styles to make this look a little bit more like the example we have over here the very first thing that i want to do is i want to select essentially everything so we're going to get star before whoops before and after i just want to set the box sizing here to border box the reason i do this is because it just makes it easier to size everything and then i'm going to set user select to none that just makes it so you can't highlight any of the text on the screen because right now i can highlight this text here when i save this you can see i now no longer can highlight that text that's just nice because in the game we don't want to highlight anything by accident then for the body i want to just get rid of all the margins so we'll say margin zero and i want to make sure everything is centered i'm going to put a display of flex with a justify content of center i want to have an align items that's also going to be in the center and then i want to put a min height of 100 vh on my body and that's just going to make sure everything is dead center in our screen so when we save everything is in the center right now it looks a little bit weird because our ground object is so long but we're going to get to that in just a second so the next thing i want to focus on is our world so we're going to say worlds here and all i want to do is i want to set the overflow to hidden i want to set the position to be relative because everything is going to be absolutely positioned inside this world in this world object we're going to resize in our javascript to make sure no matter what size our window is the world is always going to be scaled like we want it to now if we save here you can see things look a little bit better already because we have that overflow of hidden on here now the next thing that i want to work on is going to be our score so we can come into the score section here i'm going to set a position of absolute on our score and then i want to take our font size i'm going to make it 3v min just so it's going to scale again with the size of our screen so as our screen shrinks and grows in size the font is also going to scale i'm going to set the right to one v min and what i want to do is i want to set the top to one v min as well now if i just say this real quick you can see our scores over there in the top right hand corner of our world and that looks really good next thing we can focus on is going to be our start screen so we'll say start screen here it's going to be pretty similar to our score again everything is going to be absolutely positioned so we're going to absolutely position that our font size here we want to be larger so we'll use 5 v min on this one we want it's essentially be in the center of our screen so we're going to say top 50 left 50 and if we save just that you can see it doesn't actually look like it's in the center and that's because it's aligning the top left corner of our text in that center what we need to do is take our transform and translate it backwards negative 50 in both the x and the y direction and that is going to allow it to be dead centered inside of our screen so right now it's essentially in the dead center of our screen and the next thing that we want to focus on is going to be a class called hide i'm just going to come in here with a class called hi set the display to none and that's because when we start the game we're going to add a hide class to our start screen so that it doesn't show up anymore because obviously you don't want that text there while you're trying to play the game now the next thing i want to do is our ground so for our ground in order to make it look correct about the right scaling what we want to do is we want to set well first of all position absolute we want to set the width to a value that's going to make the ground look the correct height in our case the width of 300 percent is going to look pretty good when i save right now it looks terrible but if we add a few more classes it's going to look a lot better for example we can come in here and we can set a bottom because we want to be on the bottom of zero we can also set our left property in our case this left property is going to come from our javascript what i want to do inside of here is type a calculation where we're going to use a variable which is our left variable i'm going to multiply that by one percent and by default this left is going to be zero so i'm just going to come up here and say left is zero now this obviously still doesn't look like how we want it to so what i'm going to do is come up here to our world i'm going to set the width to 100 so it spans the full width we're going to take care of this in javascript in a bit but for now this is just going to be a placeholder now the next thing we'll work on is our dinosaur so we're going to say our dino is going to be positioned absolute just like everything else the left is one percent since we essentially want it to be close to the left side but not quite touching it we'll give it a height here of 30 percent and again the bottom position is going to be taken care of in javascript so we're going to make sure that's a calculation using a variable called bottom by default we're going to keep that at zero so we'll just say times one percent here and up at the top we'll say bottom is zero by default now when we save you notice that pretty much everything disappears and that's because everything's positioned absolute so we come in here and we just give this a height of like 300 pixels let's see what that looks like there we go we'll actually make that little smaller let's do a 200. there we go we have our game it's kind of showing up on the screen obviously these hard-coded values are not what we want we're going to determine these in javascript but as you can see some of the sizing and proportions look pretty good and as we change our height for example if i change it to 100 you notice everything shrinks down when i change it to 300 for example everything grows so it looks good and everything is scaling like we want it to let's just put this to 200 for now now the final thing that i want to add a class for is something we actually don't have on our screen yet which is just going to be a cactus so for our cactuses they're going to be really simple they're actually almost exactly the same as our dinosaur i'm going to copy it all down but our left position here is the one that we calculate so i'm going to copy this calc code paste it into left here and make sure this is a variable called left and up here we're going to call this left and we don't even need a default so we can actually just get rid of that default completely and then our bottom position here we want to be zero because we always want it to be touching the ground and we want it to be the same height as our dinosaur so if we add a cactus to our screen it's essentially going to be left positioned based on our javascript code it's gonna be the same height as our you know dinosaur here and it's gonna be touching the bottom of the screen now that right there is all of our styles taken care of all of our html taken care of the next thing to work on is going to be our javascript which is obviously the bulk of this video so let's come in here with the script.js we're going to set the type here equal to module because we're going to be using es6 modules if you're unfamiliar with module syntax inside of javascript i have a full video on it linked in the cards and description let's create that script oops script.js file and all we need to do is add in our code here and the very first part of code i want to work on is going to be for our world so when we get rid of these hard-coded widths and heights you can see everything disappears i want to make it so that our world scales on our screen size so we're going to write some code to do that first i'm going to need to select our world we're just going to give it a data attribute called data world if you're curious why i use data attributes for javascript selectors i have a blog article on it i'll link in the description that explains exactly why now inside of our script tag you can just close out of the stylus here what we can do is we can select that world element by saying document dot query selector and we want a query selector based on data world right there then we can create a function we're going to call it set pixel to world scale and this is essentially going to scale the world that we have based on how big our screen is and then we want to make sure we call that function and we also want to make sure that any time our window gets resized if we add event listener for resize we also call that same function so set pixel to world scale so every time the pixel size of our screen changes we want to re-update our world to make sure it's the exact size that we want and it scales properly now in order to write this function we need to create a few constant variables so we can come up here we can just say const world size we'll actually call this world width we're also going to need a constant called world height you can play around with these variables but essentially i'm going to say that our world is 100 units tall i'm sorry 100 units wide and it's going to be 30 units tall this is going to be the ratio of our world then inside that function set pixel to world scale what we can do is we can essentially use those values to calculate how big our world needs to be based on our screen size i'm going to create a world to pixel scale variable and then this is going to be set depending on if our screen is wide if our screen is wide then we're going to be able to have to limit this based on the height and if it's a short screen for example a narrow screen we're going to need to be able to limit this based on the width so what we can do is we say hey if our window.inner width here divided by our window.inner height like this is less than our world width divided by our world height height then what we can do is we can say hey our world pixel scale is going to be equal to window.enter with divided by world width what this is saying is hey you know what if our window right here is wider than our world essentially if our window is wider than our world ratio then we know that we need to pixel scale based on our width because that's the limiting factor otherwise what we're going to do is we're going to make our scale based on the height so we can come in here world to pixel scale is going to be window dot inner height divided by world height just like that and then finally we can take our world element we can take the style we can take the width property oops style dot width we can set that equal to all we want to do is we just want to take our world width we want to multiply it by that scale it's going to be a pixel value do the exact same thing with our height and this is going to be our height now if we save this and make sure i spell height correctly there we go you can now see that our game is scaled and as we increase our browser size you can see that it gets larger and as we decrease our browser size it gets smaller and the reason that we have these two if statements is because for some reason if our browser is really wide but it's really short you'll see here that now our game is scaling so it's cut off on the width and that's because in order to fit the correct ratios that we have we need to make sure that our first screen is really short but really wide it's going to be scaled this direction while if our screen is all but narrow for example like this you can see that our limiting factors are width so we're going to scale our gain based on that width size now with our pixel the world scale done we essentially have our scaling issue done the next step is going to be creating an update loop an update loop is essentially something that runs every single frame and it updates all the positions of all the things on our screen so it's going to update our dinosaur it's up and update our cactuses our score and so on so what we can do is we can just create a function called update this function is going to take in the time since we started our program in order to call this function we're going to call window.requestanimationframe and pass it in that function this requestanimation frame is a really smart thing in javascript that's only going to call our update function the next time that we actually can change the content on our screen so on a really high refresh monitor for example this is going to be called quickly while on a low refresh monitor it's going to be called slower because you can only update the screen so often based on the refresh rate of the browser and the monitor so now the next thing that we can do inside of our update is actually write out the code for our update loop because in order to make it keep looping we need to call the function a second time so inside of the update function we're calling the update function again so it's going to be constantly calling itself over and over and over again every single time that we can update our screen and then the other important thing we need to do is get the time between updates because sometimes your computer might be going through a large workload and normally it takes 10 milliseconds before each update but then you have a bit of a hiccup and the next update takes 50 milliseconds it takes five times longer than normal well if all you did was just update the position of the dinosaur for example by a static amount each frame it would move for example five pixels every frame even though the time between frames is different for example one frame may take 10 milliseconds while the other takes 50 milliseconds the dinosaur moves the same distance but it takes five times as long to render out that distance so it looks like it's moving five times lower that's really bad in a game that requires reaction time and so on so if we can determine the time between frames we can actually scale how far we move the dinosaur and cactus and so on based on how long each frame took so the movement is consistent no matter how slow or fast the frame rate is for the person's computer now to do this is a little tricky because this time variable just constantly counts up if we console log this and we come over and just inspect our page real quick go over to the console you can see this is a value that's just constantly counting up essentially we need to keep the previous value subtract the new value from it and that will give us the actual time since the last time that we ran a frame we can create a variable called you know last time for example set it to nothing to start with and then instead of our code we can say last time equals time and then up here we can get our delta which is going to be the time between frames which is our time minus our last time and then we can print out that delta right here now you may think this is exactly what we need to do but we're going to run into some issues like we could default this to 0 for example and then we can inspect our page go to our console and it looks like it's working pretty well you can see we get these update times here mostly around seven milliseconds but if we scroll up to the very top you can see this first time is 71 milliseconds and the reason for this is because the very first time that your code runs through here this last time is set to zero but our time is from when the program began so it's going to be a large gap between the time we actually start the game and when the actual time here is going to be ran so our time here could be really long especially if we sit on the start screen and wait to hit start until much later it's going to be like you know 10 seconds or like 10 000 milliseconds and then ten thousand milliseconds is going to be a huge delta so the very first time we run update if our last time is null for example we don't have a last time so if last whoops last time equals null then what we want to do is we of course want to update our last time to be the correct time so our last time is now going to be our current time we want to call our thing again to update and then we just want to return out we don't want to do any of the rest of our code we're just going to ignore everything else now by doing this our delta is going to make sure it's always the correct amount because the first time we call this when last time is null it's going to set it to the previous time and then the second time we call update is when the game is actually going to start so now if i save and i inspect over here go to the console and scroll to the very top you're going to notice even our very first frame here is that seven milliseconds and you'll notice some jumping of the frames that's perfectly okay that's why we're doing the code where we're taking into account the delta because you can see this frame took 20 milliseconds this one took 62 it took much longer that's okay though because all of our code is going to take that into now the very first thing that i want to work on updating inside of our code is going to be the position of the ground itself so what we can do is we can create a new file we're going to call this ground.js and inside here we need to select the ground elements so inside of our html let's go to our ground and we're just going to add a data ground attribute to it we're going to add that to both of our grounds so we can select both of them then inside of that ground file we can say that our ground elements are going to be equal to document.queryselectorall of data round now this is both of our ground elements selected and we can create a function called updateground and this updateground function is going to take in essentially the delta what we can do is we can update the position of our ground by just making it move to the left constantly and we're going to call this update ground inside of our script here so instead of logging this out let's come in here and say update ground and we're going to pass in our delta and at the very top of our file we need to make sure that we import update ground from ground.js make sure this is dot ground.js inside of here let's just make sure that we export this function as well now we're calling this update ground function inside of here we can update the position of our ground elements so we can just say ground elements dot for each ground what i want to do is i want to take the position that we already have and i want to update it and doing this is a little bit confusing there's quite a bit of code that's involved in actually taking a value from a css variable as you can see here and converting it to a javascript value and then converting it back to a css variable and we're going to be doing this all over our code i'm actually going to create a nice file that's going to be a helper file we're going to call this update custom property.js this is going to include a few functions that make updating these custom values really easy so for example we're going to have a function called the get custom property we're going to have another function which is going to be called set custom property and then we're also going to have a final function and this one's going to be called increment custom property this one is going to be the one we use the most often because we're probably more than likely going to be updating a value we're going to add 10 to the left position for example so we're going to be calling this increment custom property all the time now in order to actually do these let's make sure we export these so we can export all these functions so they can be used in our code and let's start defining it the very first one for getting a custom property we need the element we're getting it from for example our ground element and then we just need to get the property we're getting for example the left property and here what we need to do is we can say return the get computed style of our element this allows us to get css variables then we want to get the property value of our prop this is going to return to us a string because everything in css is a string we need to convert this to a float because everything we're dealing with is numbers and then if there is no value we're just going to default it to 0. so all that this code is done here is it's saying hey i want to get the computed style which means get me the css values i want to get the specific css property for our prop we're passing in i want to convert it to a number and if there is no number just default to 0. now setting a custom property is similar we're going to get our element we're going to get our profit but we're also going to pass in the value we want to set this to and here we can just take the style property of this we can call the set property method the set property method takes in our prop and our value so this is like dash dash left and this would be seven for and then increment custom property just combines these two together so we can call set custom property with our element and our prop which of course we're going to pass in here element and prop and also an amount to increment by we're going to set the custom property to the getting of the property so we're going to say get custom property which is from our element and our prop we're just going to add in our incremented value so here all we're doing is we're getting the current value we're adding this incremented amount to it and then we're setting that value pretty straight forward and that's all of our helper functions done so now we can actually use these inside of our code inside of our ground here we can use that increment function what i want to do is i just want to say increment custom property and again make sure we import that at the top here i want to increment it for our ground element the custom property is called left and that's because in our css we called it dash dash left so just copy that right here i want to increment it by some amount this amount we know we need to scale based on our delta so we're going to say delta times we also need to scale it based on some speed variable which we're going to call speed and then we want to make sure it moves backwards so we're going to multiply this by negative 1. up here let's just say const speed is equal to 0.05 there you go and this is going to increment our ground position and essentially move it to the left every single time we call update so now if we save you're going to notice nothing is on our screen that's because it should say update custom property dot js and we save you can see our ground is moving to the left that looks really good but you'll notice in just a few seconds here we're going to reach the end of the ground and obviously that is not what we want we don't want the ground to have an end to it we want it to constantly loop back what we can do is we can create another function this is going to be called export function setup ground this setup ground function is what we call right when we start our program and all i want to do here is call the set custom property method which we're going to make sure we import up here and i want to take the custom property for our ground elements i want to get the very first one and what i want to do is i want to set that left property and i want to make sure it's set to zero we're going to be setting the left property of the first ground element to 0. then for the second ground element i'm going to set the left property to 300. the reason i'm doing that is because in our styles here you'll notice we set the width to 300 and this left is a percentage so if the left property of our second ground is set to 300 percent and that means the left of our second ground is exactly where the right of our first ground is so they're going to match up with one another so it's going to give us one ground element that's twice as long as a normal ground element then in our update what we can do is you'll still notice as this runs through eventually we're going to run out of space it'll be twice as long but we're going to run out of space just make sure that we call this setup ground here we're going to do it at the top for now just say set up ground we'll import that oops import that up here you'll notice our ground moves to the left and it's going to be twice as long as our normal ground but still it's going to run out of space within that what we can do is we can create an if here that says you know get custom property so we're going to get the custom property for our ground and for our left property what i want to do is i want to say hey you know what is this less than or equal to negative 300 and what this means is saying hey has our ground moved all the way off the edge of the screen at negative 300 that means the right side of our ground just moved off the edge of our screen when that happens i want to loop this ground all the way around and put it on the end of the second ground element so we can do is we can come in here with another increment custom property of our ground with the left here and i can put 600 as the value the reason i'm doing that is because i essentially want to take this negative 300 add 300 to it and what that would do is make it so our ground is at the very start of our screen if i add another 300 to it it's going to make it so that this ground element is now after our second ground element now our ground elements are going to be constantly looping around each other if i just increase this speed here a little bit we're gonna like set it to like point two so they move really fast you're gonna notice nothing's working we need to make sure we import this get custom property there we go so now you can see our ground is moving incredibly quickly but you'll notice we're never running out of ground it's constantly looping itself back around if we change this back to like 0.05 and make sure that this is there and if i just inspect our page i hover over one of our elements for our ground so let me just come in here to our body i'm going to find our world and you can see we have our ground you can see when i hover over one of these elements you're going to see that the ground meets up with the first ground element so both the ground elements meet up directly with each other so this ground element is moving along and the second ground element meets up exactly with it and you can see these left properties are moving and as soon as one gets to negative 300 essentially the value of that one is going to be moved 300 away from the first one switches creating a perfect tape that's going to be looping around forever and ever and we never run out of ground now there's one last thing that i want to do in this ground file here and that is i'm going to pass in a second property called a speed scale and this speed scale is going to be the scale of our game because as we go along i want the game to speed up to get harder and harder that's how the normal dinosaur game works so this speed scale is going to be a value that constantly is increasing it starts pretty small but it's going to constantly increase and the game is going to get faster and faster and faster as time passes so what i want to do is want to take our speed scale and multiply it by the other values here so as the speed scale increases it's going to mean that we increase the speed at which we move our ground and we increase the speed at which we move everything else in our game in our script we can pass in our speed scale which for now i'm just going to pass in the hard-coded value of one and you'll see it moves at normal speed if it's two for example it now moves twice as fast and so on we're just going to keep it at one for now so right now you'll notice that when i refresh my page the ground immediately starts moving there's no chance for me to press the any key to start so what i want to do is i want to create an event listener on our document we're going to call it add event listener we're going to call it a key down and this is just going to be called handle start we're going to make sure that this only runs one time so that we can only actually make this start function run once otherwise every time we press space to jump it's going to call the start function not quite what we want so whenever we press any key we're going to call a function called handle start this handle start function will just come down here function handle start and then here is where i want to start our loop so i'm going to put the window.requestanimationframe inside here also i want to make sure our last time here is equal to null just so that we have the first update call making sure that our last time is null so it's going to update properly that way when we lose and restart the game it's making sure everything starts from scratch at the very beginning also in here is where i'm going to call the setup ground function i'm going to come in here set up ground now when i save you'll notice our game doesn't start but when i press a key now our ground starts moving so it's hooked up to this handle start function right here we can also handle our speed scale inside of here so we can say like speed scale is equal to one up here let's create our speed scale and then inside of our update we can pass that speed scale into here then we can create another function that updates our speed scale so we could say update speed scale and inside of here we pass our delta and we can just create that function down here update speed dale takes in delta this is a really straightforward function we're just going to take our speed scale we're going to add on to itself our delta times our speed scale increase this is just a constant variable that we can define up here that defines how quickly our game is going to speed up so we can say speed scale increase is equal to and we want this to be a very small number i'm going to set it to .00001 now when i save you can see that not quite everything is working right away so let's inspect our console and see what errors we have in our console we have no errors and that's just because i forgot i need to hit space to start you can see the game starts at a normal speed but over time slowly it's going to get faster and faster and faster it's a little bit hard to see because our speed scale is pretty slow it's not going to be getting that much faster to start with so if we just increase this a little bit by removing a 0 for example and now i press space to start you're going to notice it starts off slow and it's going to quickly ramp up faster and faster and faster until it's flying across the screen so quickly you can barely keep up you should already be able to see it's getting a lot faster than it was before and obviously this is getting way too fast way too quick which is why i had the speed scale with four zeros and then a one and that gives it a nice gradual difficulty increase now while we're at it we can also take our score into account we can create this update score variable which takes in a delta as usual let's just create our score variable up here and then we can reset our score to zero every single time that we call handle start and then we can just create that update score variable taking in our delta and here essentially i want our score to be 10 like our time that's passed divided by 10. so we can just say score plus equals our delta times 0.1 this is actually let's make it 0.01 so for every 100 milliseconds that passes we get one point so for every second we get 10 points and that's going to be our update score and to actually update the content of our score here we can just take our score element dot text content make sure i spell this correctly there we go score elem.text content is equal to math.floor of our score just so we don't have any weird decimal fractions and we can get this score element way up here by just saying for element is equal to data score and then inside of our html we can put that data score on our score now hopefully when i press a key you can see our score counts up and every second we're gaining 10 points and that's just going to go on forever and ever until we lose the game and we stop counting the score another thing you'll notice is we're not hiding the start screen that's probably something we should do so let me just add a data start screen onto here inside of our script what we can do is we can get that start oops start screen element which is data start screen then if we scroll way down here to our handle start we can just say start screen element dot class list dot add and we're going to add that hide class now when i click start you can see it gets rid of that and if i refresh the page it's going to come back and that pretty much handles a lot of the like foreground we have our score our title screen our ground next thing i want to work on is the interactive part which is going to be our dinosaur let's create a simple dyno.js class or a file i'm sorry in our script js what we can do is we can just import that file and we're going to have an update dyno and we're going to have a setup dyno just like we did for ground so i'll say setup dyno make sure that comes from dyno.js then we can call update dyno pass it in in our delta and speed scale and then down here we can call setup dyno just like that in our handle start now we can actually create those functions inside of here so export function setup dyno and we can export a function called update dyno and this takes in a delta and a speed scale there we go and then inside of the update and the setup we can do all the code we need to position our dinosaur and make it move to the right and jump up and down and so on now the very first thing i want to do is just set up a couple constant variables at the very top of our screen the first one is going to be for our dinosaur element a dino element equals document.queryselector of data dyno i spell that data and then if we just go over to our index html obviously we're going to add that on to our dinosaur data dino just like that now what we can do is we can use that dyno element inside the rest of our functions also i want to create some variables for our different constants so we're going to have like our jump speed for example which i'm setting to 0.45 we're going to have our gravity which i'm setting to 0.011 and these are values that i spent a lot of time tweaking to make the game feel pretty similar to the actual original game you can change the gravity by lowering it or raising it change the jump speed by lowering it or raising it and you can really change the arc of the curve of the jump and make it longer or shorter or taller and so on it's entirely up to you i just found these values look and work pretty well next we need to set a variable called dyno frame count we're going to say dyno frame count and we're going to set that to 2. and the reason we're doing this is because in our images you'll notice we have two animations for our dyno we have this one and this one these are two different frames of our animation for our dinosaur running so we have two of them which is why i set the frame count here to two we can oscillate between these two different animation frames and it's going to look like our dinosaur is moving even though it's just two static images being changed between also we can set our frame time we'll say frame time i'm going to set that equal to 100. so it's saying that every single frame of our animation should last 100 milliseconds so every second we're changing our animation 10 different times now we can actually do all the code inside of setting up and updating to make our dinosaur work during our update we essentially have two different functions we have a handle run function and we're going to have a handle jump function so let's create those we'll say handle run and we're going to say handle jump just like that we're going to handle run that's where we're going to be moving through our different animations for our dinosaur moving so in handle rent what i want to do is i first want to say hey is the dinosaur jumping i will say if it is jumping which is a variable that we can create so by default we're going to set is jumping to false and then up here we can just say let is jumping to create the variable and then in our handle jump we can change that so if the dinosaur is jumping obviously we do not want to change their animation we want to set them to the stationary animation that's how it works in the original dinosaur game like i say dino element dot source equals images slash dyno stationary.jpg this is just putting us to the stationary image because when it's in the air that's the image you get when you play the actual dinosaur game and then we return we don't want to do anything else if we're not jumping though then we want to oscillate the animation so we're going to create a variable called current frame time and we want to check to see if that's greater than or equal to our frame time oops frame time just like that and this current frame time is something that we're going to be incrementing every single time we go through this function so current frame time here we're just going to add in our delta times our speed scale so that as our game gets quicker and quicker the dinosaur is going to move faster and faster so the animation will play faster so here we're going to pass in that delta speed scale and up here we're going to do the exact same thing make sure it gets passed in now what happens is we're saying every single time that we call this function we want to update our current frame time by you know adding in our delta times our speed scale so it's going to be getting faster and faster as our game goes we're going to count faster and faster so our animation changes faster then once our current frame time becomes larger than our frame time variable which is set to 100 what i want to do is i want to swap the frame to the other dinosaur frame so i can say that a variable is going to be called dyno frame this is our current dinosaur frame so by default our dyno frame is going to be set to 0 for example and up here we can say let dino frame just like that so we're going to take our dyno frame and we're going to update it so we're going to take our current dyno frame modulo here i'm sorry plus one then we're going to do a modulo of our dyno frame count so what this does is say hey our dyno frame that we're currently on which in our case is zero add one to it so our frame is now one then what we're going to do is we're going to module that against our dyno frame count so imagine that we had 10 frames total and we're on frame zero then the next frame is going to be zero plus one that's where plus one comes from now we're doing one modulo 10 and that's just saying that if we have for example frame 20 and we have a total i'm sorry let's say we have frame 15 and there's a total of 10 frames on our animation 15 modulo 10 is going to give us 5. so it's going to make sure that we run frame 5 of our animation just make sure that our animation loops so when we go from the last frame it's going to loop back to the first frame for us automatically that's all this does this updates our frame to the next frame and then we can take our dyno element take the source of it and we can get our image for that frame so images slash dyno run dash and then we just want to pass in the dyno frame dino frame dot png the reason for that is if i look over here in our images we have dyno run dash 0 and dyno run dash 1. these are our two frames of our animation and our dyno frame value here is going to be either 0 or 1 so we can just kind of loop between them that way now the final thing we need to do is we need to take our current frame time and we need to subtract this frame time variable from it to make sure that we reset it essentially that it's going to go back down towards zero and then it's going to start counting up again for us to change the frame again in another time now we can save this and you'll notice nothing is really changing right now but when i click a key to start of course we're getting an error let's see what that error is in our console current frame time is not defined well let's define that variable by default right here we're going to set it to 0 and then inside of here we can say that we can just define our variable and by default it's zero during setup now when i press a key you notice our dinosaur is moving it is actually walking and it's playing the animation and if is jumping was set to true for example now you'll notice that we don't actually get our animation right now it just doesn't quite look right like nothing is working right at all so clearly we have something wrong with our code this should probably say png instead of jpeg now you can see that our animation looks like it should because in the air when we're jumping we don't want to play an animation but when we're not jumping on the ground we want to play that nice animation now we can get to the hard part which is going to be handling our jump this is going to need to take in our delta so let's just pass in the delta here and there and then down inside of here first i want to say hey are we jumping because if we're not jumping so if is jumping is not true then return only handle jump if we are jumping that's pretty self-explanatory then what i want to do is i want to take the bottom position of our dinosaur and i want to move it upwards or downwards depending on what my velocity of the dinosaur is so the way our jump is going to work is when we first start our jump we're going to set our velocity to an upward value which is going to be our jump speed here and then as time passes we're going to be essentially subtracting our gravity from our velocity so as our dinosaur moves upwards in the air it's going to get slower and slower as the velocity starts to reach zero and then as velocity reaches zero the dinosaur is going to start moving downward because the velocity is going to become negative because gravity is pulling it back downwards so inside handle jump what i want to do is i essentially want to first take our y velocity we're going to have a value called y velocity which is going to be by default set to our jump speed when we jump what i want to do is i want to increment the bottom of my dinosaur by that amount so we can call that increment custom property which we have imported up here make sure it just has dot js at the end and i want to increment this for our dino element i want to increment the bottom position of my dinosaur what i want to do is i want to take that y velocity variable multiplied by our delta and that's going to be what i increment by then what i can do is i can see hey if getting our custom property which again make sure we import that up here get custom property if getting the custom property for our down or our bottom of our dyno element element and bottom i want to say hey is this less than or equal to zero so less than or equal to zero if so we obviously don't want to move down anymore because we'd move past the ground what i want to do here is i want to set our custom property for our bottom to zero because obviously it can't go below the ground our bottom here is going to set itself to zero and then is jumping is going to set itself to false because once we touch the ground again we are no longer jumping then i'm going to take our y velocity and i'm just going to subtract out our gravity i'm going to make sure here i multiply this by our delta so it's going to scale again with our frame rate and up here we need to make sure we make this a little bit smaller let's try like .01 to see how that looks or maybe we'll try like .015 there we go so now we save this is looking pretty good we have no way to set our y velocity to jump because right now you can see it's working but we're not actually jumping we need to do is create a function called on jump this is going to be an event listener that plays whenever we click on a key and in our case we care about the code for the space bar so if we're not pressing space then we just want to return and also if we're currently jumping don't do anything so if is jumping is true also return you can't jump when you're already jumping that would be terrible then our y velocity here is going to be equal to our jump speed like i said and is jumping is going to be set to true now we just need to hook up this on jump by using an add event listener so we can say dyno element actually this is going to be document dot add event listener on key down and what we want to do is add the on jump event listener and we also want to make sure that we remove this event listener also because every time that we lose the game we're calling setup again so we need to first remove the event listener in case there already is one then we're going to add it back so now what i can do is i can click space and our dinosaur should jump but as you can see it's not doing anything if we come and we inspect our page you can see that it looks like in our console y velocity is not defined that's because obviously in here i didn't create a y velocity so let's do y velocity by default our y velocity here is going to be set to zero and also we want to set the custom property for our dinosaur which is going to be the down property so whoops dino element for our bottom here we want to default this to zero as well when we set up our dinosaur so now let's make sure we import this set custom property now when we save click space you can see our dinosaur jumps up in the air and when it jumps it doesn't have the run animation and then on the ground it does have the running motion so that looks pretty smooth and i think it works like i would want it to now we have our dinosaur moving across the screen and jumping the animation is playing it gets faster as the game goes on overall all of this looks really good now we essentially move on to the final thing we have to care about which is going to be the cactuses which are the obstacles for our game so let's come in here we're going to create cactus dot a s for our cactus file and inside of our script at the very top i'm going to essentially copy the code so we have update cactus and we're going to have setup cactus this comes from cactus.js and we're going to call those down here so update cactus which is delta and speed scale and we're going to have our setup down here for our cactus setup practice just like that pretty straightforward and this is going to be the code that's going to spawn our cactuses on the right side of the screen move them over to the left and it's going to despawn them when they move off the edge of our screen so let's save that go into this cactus file we're going to export a function called setup cactus and we're going to export a function called update cactus this update takes in our delta and our speed scale so again like we did in the last file i want to create a few constant variables the first one is going to be our speed which we're going to set to 0.05 and it's important really important that the speed of our cactus is the exact same as the speed of our ground otherwise they're going to move at different speeds and it's going to look really bad so we just want to make sure those are the same speed then what we can do is we can create our cactus interval minimum which we're going to set to 500 and we're going to do the exact same thing for a maximum value which we're going to set here to 2000 and what these do is essentially a interval for how long between summoning a cactus on the screen so the smallest amount of time is 500 milliseconds so half a second between cactuses is the minimum and the maximum time is two seconds between cactuses we're going to choose a random value between them and that's going to be when we summon a cactus and then finally something a little interesting is we need to get the world element we can say document.queryselector our data world element the reason we're doing that is because we need to add the cactuses to our screen when we create them and that's what this world element is going to do and speaking of creating cactuses let's do that in our update cactus what we can do down here is we can essentially say if our next cactus time is less than or equal to zero that means we need to summon a new cactus and this next cactus time by default we're gonna set to the minimum so the cactus interval min and then we can just create the variable next practice time there we go by default it's going to be the minimum amount of time so it's going to summon a cactus pretty quickly once the game starts and then once this reaches zero then what we're going to do is create a new cactus and every single update frame we're going to take our next cactus time and subtract the delta from it so what this is doing is it's constantly making this value smaller and smaller and smaller and once it gets below zero we create a cactus so let's just call a function called create cactus pretty straightforward then we need to take our next cactus time we want to set it to a random number between our minimum cactus interval so our cactus interval min and our cactus interval max and we're going to divide this by our speed scale the reason that we're doing this is because we want to make it so that as our game gets faster and faster the cactuses also spawn faster and faster because they don't there's kind of no point in making the game fast now let's call that function create practice and actually write the code for it it's pretty straightforward we're going to create a cactus variable which is coming from document.createelement and this is just going to be a simple div and then our cactus we're going to set a data set of cactus to true and this is because we're going to use that data set so we can say like document dot query selector all oops of oops data practice and this is going to be how we select all of our cactuses that's why we set this right here then what we can do is we can take the source of it and i forgot up here this needs to be an image obviously because we're moving these as images we can set that to images cactus dot png so it's going to be the cactus image we can add the class list here of cactus so add the cactus class list so it has all the styles from our style sheet and then we want to add this to our world so we're going to say append our cactus and finally the important thing is we need to set a custom property make sure up here we import that with js at the end and this custom property is going to be on our cactus it's going to be the left custom property and by default we want it to be all the way on the right side of our screen so our left is going to be 100 that'll move it all the way to the right side of our screen now we can create cactuses and they're going to show up on the right side of our screen next thing we need to do is make them so that they move because right now they just show up on the edge of the screen and never move also we need to create this random number between the function so let's just do that real quick it takes a min and a max this function what we're going to do is we're going to get a random number and then we're going to multiply that random number by our maximum minus our minimum plus 1. and then finally what we want to do is we want to take our math.floor or this is going to give us just a whole number instead of a decimal number and add our min here at the end as well and we return this so to explain exactly what this is doing we get a random number between zero and one then to make sure that number falls between our minimum and our maximum we first multiply it by the difference between our maximum and our minimum plus one and that's just make sure it falls between our max and min because at the end we add our minimum so the minimum value we can get is our min if the random number is zero zero times whatever is zero plus our minimum is our minimum if this value is one one times our maximum minus our minimum plus one is going to essentially be our maximum value especially when we add our minimum on at the end here so that's our random number between and this maps out four just make sure we get a whole number instead of a decimal now we can actually work on moving our cactuses so when we select all of our cactuses we can just do a simple for each practice and here we can write the code for moving it moving them is pretty simple we use that increment custom property function which of course we need to import so increment custom property and we want to increment it on the individual cactus we're currently on i want to get the left property on it i want to increment it by some value which is our delta times our speed scale times our speed minus one exactly the same as when we did our cactus and i'm sorry this is times negative one just like when we did our ground then same thing with our ground we can do a get custom property which again make sure that we import up here get custom property we want to get it for our cactus we want to get the left value and here we want to see if it's less than or equal to negative 100. and this means is our cactus way off the edge of our screen if so just get rid of it we don't need it anymore it's just going to slow down our game we're going to call cactus.remove now if our cactus is off the edge of our screen it removes it so let's click start and see what happens you can see we get a cactus and it summoned it and another cactus and another cactus and you notice the time between them is pretty variable it depends you know some of them are really quick back to back and some of them are much longer between so this is a really good variance that we get for our different cactuses and i kind of like it if we inspect our page over to our elements look in our body we look inside of our world you're going to notice we have a bunch of cactuses down here at the bottom but they're constantly being added and removed you can see we never have more than like three or four on the screen at once and that's because once they fall off the edge of the screen they get removed which is exactly what we want so that way we don't have hundreds or thousands of cactuses populating our html slowing down our game now the last thing we have left to do is way up inside of here what i want to do is i want to take our document i want to get all of our cactuses i'll say data cactus and i want to remove all of them so for each one i want to call the remove function the reason i'm doing this is because when we start our game so like when we lose there's going to be cactuses on the screen and when we restart our game i want to get rid of all the old cactuses otherwise you would immediately lose again not very fun to play this is just going to get rid of any cactuses that are currently on the screen so when we restart they're all going to be completely removed so now we have all of our code for moving around all of our pieces jumping and interacting the last thing we have left to do is just to make it so you can actually lose because a game you can't lose is pretty boring to play so to determine if we lose we essentially want to determine has our dinosaur interacted with a cactus so as it's moving along has it ran into a cactus to do that we need to be able to get the dimensions of our cactus we can create a function called export function get cactus rex this is going to give us all the rectangles for all of the cactuses that are on the screen so we can just come in here get our document.queryselectorall for our cactuses and the reason i'm doing this spread operator here so i can actually use a fancy array method called map so i can convert this cactus element into a different value so i can say cactus here and i just want to return our cactus dot get bounding client erect this is going to give me a rectangle that has like a left a top a bottom a right position for every cactus that is currently on the screen so that we can actually interact with them all then i want to do the same thing for our dinosaur i'm going to create essentially a similar function that's going to do the same thing export function get dyno rect this is going to return dino element dot get bounding client rectangle now if we save that go into our script we can actually import both of those we can say git cactus rex and get dyno erect and we can create a simple function called is lose let's just say function is blues actually we'll call it check this checklist function is going to get our dino rect const dino rect is equal to and then in order to determine if they've interacted we can return our get cactus rex get cactus rex what i want to do is i want to call the dot sum array method which is going to say hey if any of these cactuses return true for this value then we know that we want to return true for the entire dot sum so if any of these functions that we create return true this entire thing will return true we'd want to say hey is there a collision between our rect and our dinorect this is a function we're just going to create real quick function is collision this is collision is going to take in a rect 1 and a rect two and all we wanna do is check does any portion of these rectangles collide so we can just return that value here we're gonna return rect1.left less than rec2.right so if our left side of our rectangle one is to the left of our right side of our rectangle 2 and they're overlapped in that direction we can also check to see if our rect1.top is less than our rect 2. bottom again they have to be overlapping for it to be considered an overlap same thing for our rect1.right it needs to be greater than our rect two dot left and then lastly our rect one dot bottom needs to be greater than our rect two dot top now if we save if all of these are true then that means that we have an overlap and if they're true for any of our rectangles then we lost the game so if the check lose function is true then what i want to do is i want to return handle lose and the reason i'm doing a return here is because i don't want to call my update function again i want to immediately exit out of our update and stop updating and call this handle lose instead we can create a function called handle lose and this is going to handle all of our losing code that we need to worry about such as stopping our update loop and then making sure we can restart it again so the first thing that i want to do is i want to change the image for our dinosaur to this lose image right here because that's what happens in the actual game so we can call set dino lose which is a function that we're going to create for our dinosaur up here we're going to import that whoops set dino lose then inside of our dyno we can export that function export function set dino lose and inside of here we just want to take our dino element dot source and we want to set it equal to the images slash dyno dash lose dot png so when we lose it should hopefully get the lose image also if we go inside of our script and scroll back down there's a few more things that we're going to want to do we're going to want to do them inside of a set timeout and this may not be entirely obvious why but the reason is is because as you're playing the game you're clicking space a bunch of times right and if you lose you may have been clicking space at like the exact frame that you lost and if that's the case well then if we don't put this inside of a set timeout and we add an event listener here for clicking a key to start the game over again it's just going to automatically start the game as soon as you accidentally clicked that down key or that space key what i want to do instead is give a 100 millisecond delay so like 10th of a second that way you can see that you lost and then it's going to add the event listener and that way you don't accidentally click the key that's going to restart the game by accident while you thought you were still playing the game this is just going to call handle start and again we want to make sure this is set to once true and then lastly i'm going to take our start screen i'm going to remove the class list for hide so that it's visible again on this now if we press a key you know we're playing along everything's working fine we can jump over these cactuses this one's fine but if we hit one like we did here you can see our score stopped counting we got that death animation for our dinosaur and now it says press any key to start we click it again and hey look we're now started back over the beginning we can jump and everything is working exactly fine we hit a cactus and boom again everything loses and that's all it takes to create this dino game clone if you enjoyed this you're going to love my flappy bird clone it's linked over here as well as a palm clone which you're also going to love that said thank you very much for watching and have a good day
Info
Channel: Web Dev Simplified
Views: 92,011
Rating: undefined out of 5
Keywords: webdevsimplified, chrome dino game, dinosaur game chrome, chrome game js, chrome dino game js, javascript game dev, js game dev, js game, javascript game, js game dev dino, easy game dev, game dev beginner
Id: 47eXVRJKdkU
Channel Id: undefined
Length: 53min 35sec (3215 seconds)
Published: Sat Dec 11 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.