Create a Donkey Kong Country-ish Platformer in JavaScript

Table of contents

Creating a game map from scratch is a hassle; use a tile map editor to simplify your workflow and unleash your creativity.

Today, I'm going to teach you how to develop a 2D platformer with JavaScript. We'll start by learning how to build out a map using a tool I created called Map Remade. Once we've created something we're happy with, we'll use the base export function from Map Remade to scaffold our project. Then, we will get into some core 2D platformer concepts, including how to scroll a scene with Parallax, how to create and animate Sprites, how to add enemies and collectibles, how to add a basic UI, and much, much more.

If you're looking to take your development journey to the next level, I have plenty more courses and content only viewable on chriscourses.com. The first 50 people to sign up for premium can get 30% off any membership with the promo code Sunnyland, so be sure to check that out when you get the chance. Otherwise, let's get started!

When it comes to developing platforms, I want you to think of ways in which we can create some sort of game map. You might be thinking we can just import an image into our game, grab certain parts of it, and then place them in certain sections of our game using code. We’ll have XY coordinates for our collision blocks in which our player can fall on and jump off of, and so forth. However, if we really think about that, it is very time-consuming to do through code. I never recommend doing all that through code unless you're just really trying to learn the process of creating something like a side-scroller.

So, what do most developers do instead? Well, they use something called a tile map editor program. What we're looking at here is exactly that. Imagine creating this map with just code; there would be so much that goes into this, like placing collision blocks in the exact right spot to determine where our character can run and where they can't. We would also need to place different things, such as decorations like this tree here. It’s just a big ordeal.

Instead, we have this tile set editor program, where we can determine where we want to place certain blocks from a tile set. A tile set is just an image that contains a bunch of blocks that we can connect together. For example, if I wanted to add some more platforms, I would go over to our tile set tab and start drawing those platforms out. Now we have something new for our player to walk on. That is kind of how the tile set program works; we just grab what we want and add it to our map.

There are a couple of tile set editors out there right now, such as Tiled and LDtk. However, as I was using those for one of my previous videos, I found it very hard to do certain things, such as uploading a tile set image. I got so frustrated that I decided to create my own, and now we have what is called Map Remade. I believe this is the easiest way to start drawing out maps. It is a premium app, but everything we need to do to create a side-scroller is free, which is perfect for this tutorial.

So, how would we get started using Map Remade to create a map that we can use as a base to code off of? We will want to head on over to mappermate.com, where you can see everything that Map Remade does. It creates maps and is cloud-based, allowing us to manage multiple projects and export them directly into a game. This is actually one of the premium features, but we also have a slim export option that gives us a nice base to start off with. This is exactly what we're going to use for this tutorial.

To get started, we can just go to the top of the page and click the button to start making maps. If you already have an account, you will be redirected to the app page. However, if you don't have an account, you will be redirected to the signup page. Simply enter your email, create a password, or sign up with GitHub. After that, you will be redirected to the main page. I want to assure you that I’m not collecting any unnecessary data; the only data I collect is to associate projects with your account so that you can come back and access them on any computer. It's all cloud-based because I got tired of searching through my computer to find files within Tiled.

Now, we want to create a new project, so we will click the button to create a new project. This platformer will be called Sunnyland, as that is the name of the tile set we will be using. Once you create a new project, you need to select some...

=> 00:04:57

Create your dream game in one spot—no more searching for files, just seamless cloud access to all your projects.

To begin the process, you will be redirected here to the signup page. Simply enter an email, enter a password, or sign up with GitHub. After that, you will be redirected to this page. It's important to note that I'm not collecting any data. The only data I collect is to associate projects with your account, allowing you to come back and access them on any computer. It's all cloud-based. I got tired of having to find files within tiled and then search through my computer just to open the project. Therefore, I wanted to ensure that I have one spot where I can go for all my map projects.

Now, we want to create a new project, so we will click the button right here. This platformer will be called Sunnyland because that is the name of the tile set we will be using. Once you create a new project, you need to select a tile set. I have provided a few from really great artists around the globe, and the one we want to use is this one by Anuz. We will hit select, and it will provide us with the tile sets we need to start drawing out a map for our game.

So, how do we start drawing out a map? I will go over to the decorations tab because I know I want some sort of background. If we scroll through this decorations tile set and zoom out a little bit, we will see a nice sky and ocean. I want to start from the top left of this sky and drag towards the bottom right. Once that's all filled up by our selector, we now have access to draw this out on our map on the left. To draw this out, all I need to do is click, and there it is; we have filled up all the tiles for that specific section. If I want to keep extending this, I will just go to the edge, click again, and now we have this nice sky background for our map.

However, for our game, we want to make this a little larger since a side scroller is going to be quite long from left to right. To increase the size of our map, we will go over to the column section, increase our columns from 40 to 100, and hit enter. Now we can continue drawing our sky. We want to ensure that we place this exactly; you can see when I hover over, we have this little overlay with a bit of opacity on it. It doesn't really match up unless I put it in the exact position, so we need to keep that in mind. We must draw over the exact position to fill in that little area we didn't have enough room to draw on before.

This looks like it matches up, and everything is looking great. Let's keep drawing our map and go all the way to the end to ensure that we have a nice base to work off of. We do have this little black area on the bottom, and I want to fill this up with water. There are a few ways we can do this. One option is to select the water and then start clicking and dragging, but that's a little tiring. I prefer a quicker method. If I want to fill in this black area, I can select just one blue tile that I want to fill up the entire area and then select our fill tool. Now, you'll see that it automatically highlights everything that isn't filled in currently. If I want to fill it all in, I will just click, and now it is filled in. This is quite a time-saving method compared to having to click and drag everything within that area.

Next, I want to start adding this little Bramble Bush on top of our sky and ocean to give our scene some depth. I will click to add a layer and then select the portion of the bramble bush I want to start drawing. I won't go all the way to the bottom, as I think that's a little excessive. Instead, I will go to right about here. You can do this to whatever taste you would like, and I think this is a good height because it gives us a little view of the ocean as we start connecting these bramble bushes.

With new Layer Two created, I want to ensure that I have that selected, and now I can start drawing the bramble bushes across our screen. I will go all the way to the far right. I think that is okay, even if we don't use these or see everything within our scene in the end. We are creating a really nice scene now, and I like how this is looking. However, we need to determine what comes next. I want to base this off the finished map I already created behind the scenes, which I was showing you earlier. It looks a little like this: we have already completed the section with the sky, the ocean, and the bramble bushes. Now, we are going to begin drawing out the area in which our character can move. This entails the tiles, these little floors, and these platforms.

=> 00:08:51

Creating a game scene is all about layering and using the right tiles to bring your vision to life.

With the new Layer Two created, I want to make sure that I have that selected. Now, I can start drawing the Bramble bushes across our screen, extending all the way to the far right. I think that is okay, even if we don't use these or we don't see everything within our scene in the end. However, we're creating a really nice scene now, and I like how this is looking.

Now, we need to determine what comes next. I want to base this off the finished map I already created behind the scenes, which I was showing you earlier. It looks a little like this: we already completed the section with the sky, the ocean, and the Bramble bushes. But now, we're going to begin drawing out the area in which our character can move. This entails the tiles, these little floors, and these platforms—everything that you see right here. We're going to begin drawing that out now.

It is a little confusing to have to keep coming back and forth between these two tabs, so we can use a reference. What I'm going to do is open these up side by side. It might look a little clunky because I'm recording on such a small screen to ensure that everyone can zoom in and see exactly what I'm teaching, but I think we can get away with it. I believe we can side by side this and make sure that everything is working correctly.

So, how would I begin emulating what we have over here on the right-hand side? It looks like we have some basic tiles extending from the left side of the screen. I want to go over to our tile set to begin selecting those tiles. I'll click this tab right here, and you can see this opens up a new tile set. I want to start drawing out now. Currently, we have new layer 2 selected, which is using the decorations tile set.

If we switch over to a different tile set and try using tiles from that one on our map on the same layer, we will not be able to do so. The way this exports into actual JavaScript requires that each layer has its own individual tile set. It's an unfortunate little quirk that I just haven't gone around to integrating yet. However, by adding a new layer and associating each tile set with that different layer, it just makes development so much easier. I think it's worth it to keep things the way they are right now.

Since we can't draw on that layer, we need to add a new one because we have a different tile set. I'm going to select this little block right here, which is going to act as our first ground in which we step on. Now you'll see that's hovering, and I can begin using that tile to start drawing out the same sort of tile we have over here. We don't have to be exact with what we see, but we just want to follow the general idea.

You'll see I just clicked and dragged to make that platform that you saw before, but we can also use the line tool right here, which gives us straight lines. This way, we don't need to worry about messing up with our brush. We might actually do something like this if our hands are a little shaky or something. I just want to show you all the features that are available to us, and we'll begin using all of those.

I want to continue our platforms using this block right here. I'll select our line tool, which we can also select with L, and then underneath this one block, I'm going to click and drag. There is the platform that we see right here. Now, I want to go a little bit up and start a new platform at a different height to vary things up a bit. I think that's going to end right about here.

So, we have the beginning of a game, and it's looking to be in great shape. Of course, we can make this better. We have this entire tile set available to us exactly for that—to make our game immersive and just visually pleasing to look at. What would I add here? To start, let's just make this one little platform or this one little block of all the tiles and the dirt that we see right now.

How would I go about doing that? Well, let's start where we left off right here. It looks like even though everything is red—those are Collision blocks, by the way—we'll get there. It seems we have a little corner piece right here. So, I want to select the corner piece available to us in the tile set and then replace the little center piece with that corner piece. This way, we have a little bit of variation going on.

Now, right beneath that corner piece, we have a side wall piece that represents a wall going down. We can grab that and start drawing down, and now we have something which is almost self-contained. However, I need to get rid of this little space right here before we fill everything in with dirt. So, what do we want to use to fill in that spot?

=> 00:12:48

Creating a captivating map is all about layering and adding unique details to make it come alive.

To begin our project, let's start where we left off. It looks like, even though everything is red—those are collision blocks, by the way—we have a little corner piece available to us in the tile set. I want to select this corner piece and replace the little center piece with it to introduce some variation.

Now, right beneath that corner piece, we have a side wall piece that represents a wall going down. We can grab that and start drawing down. At this point, we have something that is almost self-contained, but I need to get rid of a little space before we fill everything in with dirt. To fill in that spot, we can go over to the left and select the left piece of the wall. Once I click there, the left wall piece is filled in.

Once we create some sort of container, we can begin to use our fill tool again to fill in this container with something like dirt. The dirt tile is just this little maroonish type square, which is what I want to use. I will select that, go over to our fill tool, and now you can see which tiles we would fill in based on what is and what isn't already taken within the layer. If I clicked in the wrong area, we would fill in our sky with dirt, which we don't want. We just want to fill in this one area, so I will click, and that’s looking pretty good to start.

If I want to add in decorations, I would need to search this tile set for certain decorations. We do have another tile set called decorations, but there are also decorations within this tile set, including dirt decorations. To add little rocks to our dirt, I will click in random locations after selecting those little dirt objects. Now we have some variation to our map compared to before.

We can do the same thing with grass. We have some grass tiles that we can select, but it's a little hard to drag these out correctly. Therefore, I will use our line tool to create a straight line easily and place these wherever I feel like. I think I will fill in this entire section with some grass. It looks like we have some grass in the beginning next to the house, so I will try to fill that in correctly as well. Our map is really coming together!

Next, I want to show you how to add something like trees and houses to spice this up even more. To do that, we will add a new layer and go back to our decorations tile set. If I want to use this house, I will scroll over to it, select it, and then go to the location where I want to place it. It’s that easy—just click, and now it's there. However, you'll notice that the house is actually covering the grass. You might want this, or you might prefer the grass in front.

If I want the grass in front of the house, I can grab the layer and move it above the house. Now, you can see that portion of the grass is covering the house, which is really cool. Let's add a tree now. I like the one used in the example, so I will select that while staying on our layer four decorations tile set and place it where I would like. I think I prefer it behind the grass, so I will keep that as is.

If you're ever confused with your layer names, you can hit Control + R to rename the layer. Let's call this layer "Main Decorations" so we know exactly what we're dealing with. We can call layer three "Tiles" because we're really working with main tiles there. This is kind of how we draw out maps.

The next thing I want to show you is how to draw a background piece, which was a little confusing for me when I was first learning how to draw maps with tile sets. I also want to show you how to add collisions, as those will determine where our player stops and where they're able to move. It's very important to know how to add collisions, and I will show you that in a second.

To add the background piece, we will hover over our tiles layer. Since this seems to be in the background, it doesn't make sense to add these new tiles directly to the tiles layer. Instead, we will add a new layer and rename it to Background Tiles. If we want these behind our current tile set, we will need to drag this layer down beneath it.

=> 00:16:44

Master the art of map-making by layering your tiles strategically and adding collisions to control player movement. It's all about creating a seamless experience!

First, we will begin by learning how to draw maps with tile sets. After that, I definitely want to show you how to add collisions because those are what will determine where our player stops and where they are able to move. It's very important to know how to add collisions, and I'll show you how to do that in a second.

But first, let's add this little background piece right here. How would I go about doing that? Well, hovering over our tiles layer, we have our tiles laid out, but this seems to be in the background, so it doesn't really make sense to add these new tiles directly to this tiles layer. What we're going to do is add a new layer and rename this to background tiles. If we want these tiles behind our current tile set, we will have to drag this layer down beneath it.

Now, we can begin adding tiles to that new layer; just make sure it's selected. We'll zoom out right here and start searching for those exact tiles that we need. They are going to be near the bottom of the tile set. You can see that this right here matches up with what we have on the right. So, I'm going to grab this one piece and try to match things up pretty closely to what we have. I'm going to start drawing out four blocks and then want to complete this little structure by extending it to the right.

To do this, I'll draw two blocks right here, and we have to follow the pattern by grabbing small corner pieces that allow us to extend things outwards. As long as you follow the patterns given to you within the tile set, you'll be totally fine with creating something that looks good. Whenever we have some sort of corner piece that goes down, we need to make sure that we select this piece. Then, we can add one more extending corner piece out to the right, which creates a really cool pattern, as you can see right there.

Now, I have this platform on top of it, acting as something on which a player can jump. How would we create that? Well, let's search for our platform tiles, and they are right here. I'm going to select the center piece and then start drawing to extend the platform. We have these cool little edge pieces that we can add on, and we're almost done creating this structure.

Now, how would we fill in what we see right here? You might think we can find some sort of dark color—it's like a dark blue or black purplish color. You might think that we can just select this, go to our fill tool, and then click inside of here. However, if we were to do that, pretty much everything would be filled in. This is simply because, if we hover over the background tile set layer, it is not fully self-contained, or at least that little section we want to fill in is not.

So, what we can do is select our brush tool. We can select anything here; it doesn't really matter. Let's select this little block. I want to start creating, behind the scenes, a container for this little section. When we use our fill tool, it will only fill in the sections that have nothing on them for the layer. You might have seen that I clicked and dragged until I completed some sort of container for this little structure. If I hover over background tiles, you can see now that the structure is complete, which will allow us to grab this black-blue-purple color and then go to our fill tool to fill in that one area right there.

Now, we have this cool little structure that we just created. If we want to add some decorations, we just need to find out where those decorations are. We have a stem that we can extend from the ceiling, and we have some rocks, which I believe are somewhere—here they are, with the purplish background. We have something that looks pretty similar to what we have on the right here.

That's how you would create platforms. You can keep extending these; you'll see that I extended them right here. I added some coins or some gems, which are over within the decorations portion. These are just really cool things that we can add to our map so that we're creating our map as quickly as possible, allowing us to focus on our game rather than the map-making portion directly within development.

The last thing I want to teach you in regards to making maps with tile sets is adding in collisions. This is a cool little feature that I made sure to include within Mapper Mate. So, if we go to add a layer, we have a completely fresh layer. I'm going to call this collisions. Now, the far right tool, which we see right here, is called the collisions tool. We are going to select that.

We have two types of collisions: blocks and platforms. Blocks are collisions in which a player should never pass through, but platforms are collisions in which a player should be able to jump through.

=> 00:20:35

Focus on creating your game, not just the map. Use collision blocks to bring your platformer to life and start building your dream today.

In this tutorial, we will explore some really cool things that we can add to our map, allowing us to create it as quickly as possible. This way, we can focus on our game rather than the map-making portion directly within development. The last thing I want to teach you regarding making maps with tile sets is adding in collisions. This is a cool little feature that I made sure to keep within Mapper Mate.

To start, we will add a completely fresh layer and call it collisions. Now, the far-right tool that we see is called the collisions tool. We will select that, and there are two types of collision options: blocks and platforms. Blocks are collisions through which a player should never pass, while platforms are collisions that allow a player to jump upwards through and then land on top. This is just a cool game mechanic that you see in tons of games like Mario and Sonic, which primarily use blocks or platforms. I made sure to include both of these within Mapper Mate.

So, how do we go about using this tool? We have blocks selected for our collision tool, and now we can begin drawing over the exact spots where we want to add collisions. If we were to export this game without any collision blocks, our player would just fall through the entire screen. However, by drawing these collision blocks on top of where we want our player to land, they will have something to jump off of and land on. Wherever we have ground that I know I want my player to be able to jump on or move across, I will draw out a collision block that extends across that area. This is really all there is to it.

If we want to begin exporting this just to test things out and see how everything works, we would go to our export button. You will see that we have four options: full platformer, base platformer, gdau, and image. The one we will focus on for this tutorial is base platformer, which is completely free. However, if you would like to pay for the pro version of Mapper Mate, you can use the full platformer export, which essentially scaffolds an entire platformer for you with all the tools and code you need to get started. This allows you to focus on the things that really matter rather than the beginning of a game.

If I were to do the full platformer export, this is what it would look like: we would automatically export into a full game, and we can use W, A, S, D to start moving around. You can see that the scrolling functionality is already there. Let me full screen this to show you the really cool stuff I provided through Mapper Mate. Eventually, we will need to add collision blocks and additional functionality to make this a true game, but I am really happy with how this all turned out with the export. I think it's super cool that we have sprites, collision detection, and scrolling—awesome stuff, really!

However, the main focus for this tutorial, as I am trying to teach you how to code a platformer, will be the base platformer export. If we start with this, there is a lot I am already going to include. Let’s open this up, and you can see it’s close to what we had in the premium project. But with this version, we are just given a little square in which we can jump around. There is really no scrolling at the moment, and our scene isn't zoomed in like it was earlier. We don’t have sprites; it’s just a nice base to start off of in case you want to do something different from a more opinionated export. This will be a great way for us to learn how a real side-scroller is coded—not from scratch, but from a good base.

I have already added in the player movement, and if you would like to know how to do that, I have plenty of other tutorials that will show you. For now, we will focus on the main details after exporting from this state.

So, what is the next step for you? I want you to use the full version of what you see over here. I will full screen this again. I want you to base your map on this or create something completely new; it doesn’t really matter in the end. Start creating something that extends all the way from the left to the right-hand side of the screen. Begin creating your dream using the app, and once you have something that you’re happy with, I want you to go over to export and export as base platformer. That is where we will start developing our game. If you would rather skip all of this altogether and just want the coding portion, I will be including the code in a free GitHub repo.

=> 00:24:22

Start creating your dream game by mapping it out and exporting it as a base platformer. Dive into the code and bring your vision to life!

In this tutorial, we will focus on the main details after exporting from the state. So, what is the next step for you? I want you to use the full version of what you see over here. I’m going to full screen this, and I want you to base your map on this or create something completely new. It doesn't really matter in the end, but start creating something that extends all the way from the left to the right-hand side of the screen. Start creating your dream using the app, and once you have something that you're happy with, I want you to go over to export and select Export as base platformer. This is where we will start developing our game.

If you would rather skip all of this altogether and do not want to draw out your own map, you can just focus on the coding portion. I will be including the code in a free GitHub repo for you to start your game with. So, go ahead, pause the video, draw out your map, download the code, and let's start developing!

Now that we have our map created, we will want to export this. To do this, we will go to the button in the bottom right corner. We have a few options available to us, but the one we’re going to use for this tutorial is the Base platformer option because that is a free export, and we are trying to learn to the best of our abilities right now. Once selected, you will see that it creates a download for us. We want to open up the zip file, which will contain the project we will base our game on.

If I double-click on index.html, you can see that we have a map available to us. I’ll zoom in a bit, and a lot of functionality is already provided based on this export. We can move our character around, and our character in this case is just going to be a little red square. We can move the square around with WASD, and as you can see, we are jumping between the platforms. All the collisions that we drew in place work pretty perfectly.

However, you might notice that as I try moving to the right, we are not actually able to scroll over. This is the main functionality we will be adding to this game, along with many other features, because we want this game to be a good platformer. So, let's get started with adding that scroll functionality.

First, let’s get our project in shape a bit. We just downloaded our project, which has a really long name. I want to change this to something a little more understandable, so I’m going to rename it to Sunnyland, as that is the name of the tile set we are using. Then, I want to open this up in a text editor of my choice; in this case, it’s going to be Sublime Text. I’ll grab the folder and drag it onto Sublime Text, and now we have our project available to us, which is absolutely great.

I’ll full screen this and give you an overview of what the mapper mate export provides to us so we can get a little more familiar with the codebase. We did get a bit of a head start, but we still want to understand what’s going on here. Looking at index.html, we have a few base styles that you pretty much add to every project within the realm of web development. We have a canvas, which is what our game is rendered on for performance purposes, and then we have a bunch of scripts being pulled in here.

All these scripts, with a layer and so forth, are data files. All the data we created for each layer within mapper mate is automatically exported into the correct format within JavaScript. This might look like a bunch of random numbers, but these numbers are used to transform each tile set tile into something we can place in the correct spot within our actual rendering. This is all responsible for placing our layers in the correct spot and layering them on top of each other. We don’t really need to worry about the data folder once that’s complete, but we do want to focus on index.js.

If we look inside of here, we have some very basic setup code. We’re pulling in our canvas and setting its width and height. All this layer data and tile set stuff is automatic through mapper mate, so we really don’t have to worry too much about that. We have a tile setup section, and this will actually be the correct comment when you’re working on this tutorial. I’ll make sure to fix that, as it looks like it used an HTML comment there. Regardless, all this stuff with tile setup is something we really don’t need to worry about when it comes to coding an actual platformer.

=> 00:28:01

Mastering the animation loop is key to making your game come alive—it's all about how you handle player input and update the scene.

In this tutorial, we are all responsible for placing our layers in the correct spot, layering them on top of each other. Once that's complete, we don't really need to worry about the data folder; however, we do want to focus on index.js.

If we look inside of index.js, we find some very basic setup code. We are pulling in our canvas and setting its width and height. All the layer data and tile set information are handled automatically through map or mate, so we don't have to worry too much about that. There is a tile setup section, which will have the correct comment when you're working on this tutorial; I will make sure to fix that, as it currently uses an HTML comment. Regardless, all this tile setup information is not crucial when it comes to coding an actual platformer.

The important part begins after the end of the tile setup, where we are creating our player. We are using the function animate, and everything inside this function is where we want to start. The animation loop that we have right now is simply calling itself over and over again, rendering everything on the scene. First, it handles our input; if we press W, A, S, or D, we are calling the player handle input function. This function determines how we should move our player based on the input received.

Next, we have player.update, which updates our player value. When our player is on screen, we react to the input and determine how to move the player accordingly. Following that, we have the render scene code, which involves c.save and c.restore. In summary, we are clearing each frame of our canvas with clearRect, drawing our background image with drawImage, and that constitutes the whole scene we created in map or mate. We need to draw our background and then draw our player on top of it. When we call c.drawImage(background, canvas), we are drawing out our entire scene and then layering our player on top of it.

This process repeats continuously, akin to a flip book or a movie. As we update our player's position and call the next frame of the animation, it creates the illusion of movement. This is essentially how games and movies operate. For the most part, we will be focusing on the animate function.

Now, let's move on to learning how to scroll our scene once a user reaches a certain point. Our scene is represented by what we see, and if I were to draw this out, we only have this visible area. As our player reaches a certain point on the scene—let's say the red square moves into a designated red line—we want to begin translating our scene in one direction. For instance, if our player moves into this line, we will start pushing the scene to the left.

You can think of this red rectangle as a camera; when we run into the red line, our camera pans to the left, moving everything we are rendering out in that direction. This is how we will start scrolling our scene. It is a bit more complicated than that because we need to account for some math, but that's the general idea.

To start translating our scene to the left, we can go inside of c.save and c.restore. To give you some idea of what is happening with c.save and c.restore, the canvas provides some global methods that affect our entire scene. If we don't wrap our code within save and restore, things can get out of whack.

If we want to use the c.translate method, this will be responsible for translating our scene in one direction—whether it be up, down, left, or right. If I were to use this method without wrapping it between save and restore, it would cause issues. However, if I place it inside those two functions, we can add some translation on the x value. For instance, if we want to move our scene over to the left by 100 pixels, we would set the first argument (the x value) to -100 and for the y value, we would set it to 0 since we don't want to translate anything on the y-axis just yet.

Once we save this, you will notice that our scene is translated by 100 pixels to the left. Let's go back to our game and refresh, and now we have a little more functionality in our platformer.

=> 00:31:49

Master your canvas transformations by wrapping translations in save and restore to keep your scene in check.

In this section, we are responsible for translating our scene, which involves everything that's rendered out on the canvas in one direction, whether it be up, down, left, or right. If I were to use this translation without wrapping it between save and restore, things would kind of get out of whack. However, if I place it inside those two functions, as you can see, it's wrapped in between them.

We can then add some sort of translation on the x value. Let's say we want to move our scene over to the left by 100 pixels. For the first argument, which is the x value of how much we want to translate by, I would put 100. For our y value, I don't really want to translate anything on the y-axis just yet, so I'll put 0 there. When we save this, you're going to notice that our scene is translated by 100 pixels over to the left.

Now, let's go back to our game and refresh. We have a little bit more viewing room over on the right, and our character is pushed over to the left. You get the idea. However, if I were to take this out of save and restore and comment all that out, you're going to see that this is just going to keep translating over to the left, which is kind of silly. So, we want to ensure that when we are calling c.translate, it is wrapped within c.save and c.restore. Otherwise, it will keep translating from its current position over and over again.

When we put it inside of those functions, we are essentially saying to only translate it from this one position where we are saving the canvas's current position. Therefore, we need to make sure that c.save, c.scale, and c.restore are all uncommented. Once we do that, everything is translated over to the left by 100 pixels. However, we don't want to translate everything over to the left by 100 pixels to start. Instead, we want to translate it over to the left over time.

To achieve this, we need to ensure that this value is increasing or decreasing based on when a player hits a certain point, which I will refer to as a scroll post. The scroll post is an arbitrary line that we draw, and we say that when a player hits this line, we will begin scrolling our scene. To do that, we need to alter this value.

What is this value going to be called? We can call it camera X because it determines the x coordinate of the visible camera. I prefer to call it simply camera. I will create a constant called camera, setting it equal to an object with an x and a y coordinate, both initialized to 0. When we are translating, we want to translate by our camera's x value.

If we start increasing the camera's x value by an arbitrary amount, let's say 100, technically, the x is going to the right. You can envision this as our camera moving to the right, allowing us to view more of our scene. However, when we translate our map to view more of it with c.translate, we need a negative value. So, although our camera value might be increasing, we need to ensure that our translate value is going to the left.

To do this, we will go back to our code and make sure that camera X is a negative value when we are translating it. Now, whatever we declare camera X to be, whether it be 0, 100, or 200, let's go with 300, we will see the camera shift over to the right while our scene shifts over to the left. You can see that in action right there; it's exactly what we want. If we hold down D, eventually we can get our player over here, and all of our collision blocks are translated correctly.

However, as I mentioned earlier, we need to pan our scene using a certain technique, a specific algorithm. To illustrate this, I will reset camera X back to 0 and create a scroll post so we can visualize how this is happening within our game. I want to place the scroll post approximately in the middle of our game, arbitrarily saying it's about 500 pixels. We can fine-tune this later.

To add a scroll post, I will create a constant and ensure that this is just one value that should never change. Typically, when I have a constant that should never change, I always uppercase it and use underscores; this is a traditional convention for constants. Therefore, I will call this SCROLL_POST and set it equal to a value of 400.

To visualize this, I will call it right beneath the player draw so that it is rendered on top of everything. I will use the c.fillRect method, which will create some sort of rectangle for us based on four arguments.

=> 00:35:55

To create a smooth scrolling experience, track the distance between your player and a fixed scroll post, and only translate the scene when the player surpasses that point.

Right about here, I'm going to assume that's 500 pixels. We can fine-tune this, but how would we add a scroll post in? Well, let's create a const, and I'm going to make sure that this is just one value—a value that should never change. Typically, when I have one const that should never change, I always uppercase it and use underscores. That's just a traditional convention for something constant, at least something that should never change. So, I'm going to call this scroll post right and set it equal to a value of 400.

Now, I want to be able to visualize this, so I'm going to call it right beneath player.draw so that this is rendered out on top of everything. I will use the c.fillRect method, which is going to create some sort of rectangle for us based on four arguments. The first argument is going to be the x value of the scroll post, and I know that the x value of the scroll post is going to be equal to scroll post right. That just means we're going 400 pixels from where our game map is rendered out.

The Y value I don't really want starting from the top of our canvas because I don't know; I just kind of want it in the middle. For now, let's just put something like 200. This value doesn't really matter much; we're just trying to do this for visualization purposes. Then we have a width and a height. I think a proper width for the scroll post will be 10 so we can see it, and a proper height is probably 200.

Now, if I were to save this and go back to our game, we can see that we have our scroll post being rendered out. I kind of want this a little bit more to the right, and even though the height or the Y position doesn’t matter, I kind of want it a little higher up as well. So, to do that, we are going to make sure that we push scroll post right over by 500 pixels, and I want the Y position up a little higher as well, so I'm going to change 200 to 100 here. I think that should fix everything, so there is our scroll post looking great.

Now, what do we do with the scroll post? Well, as I mentioned earlier, as our player begins to move to this position and we pass this position, we need to translate our scene. But how do we determine what we translate our scene by? This is how we're going to do it: if our player has moved this distance past the scroll post—so the distance of this red line right here—we need to translate our scene by that amount.

So, we always need to find the distance between our player and the scroll post. Let's start with this: how do we get the distance between the scroll post and our player's current position? Well, that is going to be quite simple, actually. So, right beneath update, as we update our player's position, I want to start tracking for that scroll post distance. We can even comment that out right here: track scroll post distance.

To get the scroll post distance, we can call a const called scroll post distance. We need to take our player's x value and then subtract from it the scroll post right value. This right here is going to give us this little red line's distance, which is exactly where we want to start because that is what's going to determine how much we should be translating our scene by.

So, this is our scroll post distance, and if I set camera.x equal to that distance, let's watch what happens real quick. If I go back to our game and refresh, you're going to see that initially our scene is translated over to the right by quite a bit. But as I begin moving, we have some sort of scrolling in place, which is very interesting. However, it's not really what we want or need because our scene should be translated all the way to the left to start, and we should be able to move across our scene only until we hit that one scroll post right there.

This is a good start, but we need some conditionals in place to say that we should only be scrolling under certain conditions. That first condition is if our player's x value is greater than our scroll post right value. That is when we want to begin actually altering our camera's x position. So, let's save that and go back to our game. Now our scene is rendered out correctly to start, and only when we pass this value right here are we going to begin scrolling our scene.

The reason this works is that as we continue getting further and further from the scroll post, that distance is going to keep increasing over time. As that distance increases over time, we're telling our camera to translate more over time relative to our player's position and the scroll post. This just kind of works right off the bat, which is pretty amazing. That's all we have to do to begin scrolling from left to right since we're translating our entire scene. All of our collision blocks are coming with us along with the visuals in the background, which is just...

=> 00:40:01

Transform your game scene effortlessly by mastering camera scrolling; it’s all about syncing your player’s position with the scroll posts.

When we want to begin actually altering our camera's Exposition, let's save that and go back to our game. Now, our scene is rendered out correctly to start, and only when we pass this value right here are we going to begin scrolling our scene. The reason this works is that as we continue getting further and further from the scroll post, that distance is going to keep increasing over time. As that distance increases, we are telling our camera to translate more over time relative to our player's position and the scroll post. This just kind of works right off the bat, which is pretty amazing. That's all we have to do to begin scrolling from left to right. Since we're translating our entire scene, all of our collision blocks are coming with us along with the visuals in the background, which is just straight awesome, honestly.

So, what would I do next? If I want to add some scrolling going upwards or downwards, we need to add some similar functionality. How would we go about doing that? Let's head back over to our code, and I'm going to create a new scroll post. This is going to be called scroll post top. So, how far from the top should our player be able to jump before we start scrolling our scene? I believe our scene would be scrolling downwards since we're going up; the scene comes down. I think a value of about 200 is going to be a good place to start.

To make this as understandable as possible, it's always good to begin rendering out these values on the scene. It's going to make your life easier just to understand what the heck is going on here. So, with c.fillRect, I'm going to duplicate that line. Instead of rendering out scroll post right, I'm going to render out an arbitrary value on the x-axis and just say that I want this rendered out 300 pixels from the left of the scene. For our y value, we should definitely be using how far from the top our scroll post should be, and that is going to be the value of scroll post top, which right now is 200. I kind of want this to be more of a horizontal rectangle, so for our width, this is actually going to be 100, and for our height, we're going to use a value of 10.

As I said, we don't actually need this; it's just for display purposes. Now we have our scroll post right here. Technically, with this, if we were to pass the point of the scroll post, we'd want to scroll our scene up or down. So how would we go about doing that? First, we need to make sure that our c.translate value—the thing responsible for moving our camera—moves up or down based on our camera's y coordinate. For our second value, I'll say we want to use camera Y, and we can start off with that just to see how things look. However, we definitely need to duplicate this code right here and kind of alter it for our y-axis instead of the x-axis.

Let's do exactly that. If the top of our player, which we will get with player.doy, is actually less than our scroll post top, then that is when we are going to start scrolling our camera's y position. But first, we need to get the distance between our player's top and the scroll post. What distance do we need to get? Let's say our player is up here; we need to get the distance between the player and the scroll post. So how can we do that? We actually want to start with the greater value here, and since the canvas coordinate system goes from zero and then increases over time to something like 100, we need to make sure that we are first selecting the greater value, which would be the scroll post.

So, let's select the greater value here, get the scroll post top, and then subtract from it our player.doy. This is going to give us the distance between the two if our player were to move upwards past the scroll post. We have the scroll post distance, and based on that, we want to alter our camera y. Now, when our player jumps upwards past the scroll post, our camera is going to pan in the y-axis direction.

So, let's save that and refresh, and you can already see that taking effect. It kind of works, honestly. As I go up, you can see we can keep going up, which is pretty cool. However, there is no extra scene up there; we're just rendering the top of our sky, which might be what you want, or your scene in map remate, whatever you created, might actually have more area up there. That's definitely something that you can do, but in our case, it doesn't really make sense. We want to limit the top of our scene based on what we actually can render out here.

So, we want to keep this condition in place, but we also want to say that if camera Y is greater than zero, then that is really the only time we want to make sure that our camera is panning in that direction. You might be asking yourself, did we actually do anything worthwhile here? Yes, we definitely did because although we might not have more stuff on top of our scene just yet, we are making progress.

=> 00:44:27

Limit your scene to what you can render now, but always plan for future exploration.

You're kind of seeing that we can keep going up, which is pretty cool. However, there is currently no extra scene up there; we're just rendering the top of our sky. This might be what you want, or your scene in the map remate might actually have more area up there. That's definitely something that you can do, but in our case, it doesn't really make sense. We want to limit the top of our scene based on what we can actually render out here.

So, we want to keep this condition in place, but we also want to say that if camera Y is greater than zero, then that is really the only time we want to make sure that our camera is panning in that direction. You might be asking yourself, did we actually do anything worthwhile here? Yes, we definitely did. Although we might not have more stuff on top of our scene just yet, we might add that in the future when we expand our map on mapper mate or if we expand our scene downwards.

Let's say we have some area right over here in which we want to explore in the downwards direction. We might need to scroll our map down in that direction, so I think it's important that we add that downward scroll as well, just in case we need it. We are trying to make this as usable, fun, and exploratory as possible, so we're going to do exactly that.

Let's add one more scroll post for our game, and this is going to be scroll post bottom. We're going to render this out as well. You might know how to do this; I challenge you to actually pause the video and see if you can do it on your own. Render out a scroll post, and when your player hits that scroll post going in the downwards direction, we want to pan our scene up. So, pause the video and see if you can do it.

Regardless, I want my scroll post bottom to be at a value of something like 500. We're going to fine-tune that later, and I want to render this out by duplicating c.fill wct. Our x value is 300, which is totally fine, but instead of referencing scroll post top, I'm going to reference scroll post bottom. Now we have our scroll post way down there, which is way too far down. I might actually move scroll post top upwards and scroll post bottom upwards as well.

So, let's do that. I think scroll post top should actually be a value of 100, and scroll post bottom should probably be something like 280 or 300. I think that's going to be good. Now, nothing is going to happen just yet, but I want our scene to scroll downwards. To do that, I can duplicate our last if statement that we used to scroll our scene upwards.

Now we want to say if player Y is greater than the value of scroll post bottom. To illustrate this, if our player Y, which starts at the top of our player, is greater than the value of scroll post Y, which is right here, and in this case, our player would be somewhere down here, then we want to start scrolling by the distance between the two.

We're going to do exactly that. If player Y is greater than scroll post bottom, we can get rid of this end statement for the time being. Then we want to get the distance between the greater value, which in this case will be player Y, and subtract from this scroll post bottom. That will give us the scroll distance we're looking for, and then I want to alter camera Y by that distance.

Let's test it out. We might actually have to make this negative, I'm pretty sure, but let's just see what it looks like. I'm going to refresh, and now when I go past that distance right there, which I can only do by falling, you're actually going to see that our scene is moving in the incorrect direction. This is why, when we get that distance, we want to make it negative because we want our camera to pan in the opposite direction when we're falling down.

So, make sure that within your code, you set scroll distance to the negative value when going downwards. We can go back to our game and start moving over to the right, and now when we fall, we should see our scene pan, which is awesome.

So, is this going to work for our game at the moment? Maybe, maybe not. I think for our game, if we go past this point, we probably want to do a game over or something of the sort. However, I might actually expand this map in the downward direction just to ensure that we can see that functionality really happening, where we can explore stuff either beneath or above us, really expanding the map in the Y axis.

That is the gist of how we're going to implement scrolling using scroll post functionality and c.translate functionality. It's really smooth and works great for going in either direction and seeing more of your map while exploring things. For now, all I would do is comment out the code.

=> 00:48:32

Adding scrolling functionality to your game is just the beginning; it's time to bring it to life with animations that make your character move, jump, and run.

Starting off, we are moving over to the right, and now when we fall, we should see our scene pan, which is awesome. So, I mean, really, is this going to work for our game at the moment? Maybe, maybe not. I think for our game, if we go past this point, we probably want to do a game over or something of the sort. However, I might actually expand this map in the downward direction just to ensure that we can see that functionality really happening, where we can explore stuff either beneath or above us. We really want to expand the map in the Y axis.

That is the gist of how we're going to implement scrolling using scroll post functionality and c.translate functionality. It's really smooth and works great for going in either direction and seeing more of your map, exploring things. For now, all I would do is comment out the code which renders out the scroll posts. We can do that right here with an animate, so I will comment those out. I want to keep them for debugging purposes later. Now we have scrolling functionality in place, regardless of whether or not we see those scroll posts. So, really awesome stuff right there!

Now, even if we don't see the scroll posts, we can move over to the right, we can move to the left, and when we start going in the Y direction, you're going to see our scene panning accordingly. So, yeah, really cool stuff right there for adding scroll functionality to our game.

Now that we have scrolling in place, I want to add a little bit of liveliness to our game because it's kind of boring that we just have this red square here. I want to replace this red square with a Sprite, so an animation that shows our character moving—not just moving, but also running, jumping, falling, and so forth. We're going to learn how to do exactly that right now.

So, where would I start with this? Well, let's head on over to player.js. I’m going to go to our constructor at the very top, and I want to begin adding in a property. This property is going to be called this.image, and it’s going to be equal to a new image object. This is a JavaScript image object provided to us by default through the language of JavaScript. Using this, we can begin to create an image programmatically.

If we wanted to set this object equal to some specific image within our project, we would call the source property and set this equal to some sort of image. Now, what images are available to us? If we look over here within our images folder, we have decorations and a tile set, but none of these actually contain a player that has movable frames for animation.

If we want to grab a specific Sprite sheet that contains animation frames, we can go back over to mapper mate. You’ll see that I included credits for all these tile sets, and usually, these credits contain Sprite sheets for different characters that you see within the actual tile set. This tile set is by a creator named Anas. I’m going to click that, and it’s going to send us over to their itch.io page. If you go on down, there’s going to be a download now button we can click. You can donate to this author if you'd like; I would definitely recommend it. I did myself because it is really helpful for these artists to be providing all these assets to us for free—super, super helpful—so we don’t have to do it ourselves.

However, if you would like to skip the donation process, you can always click no thanks, just take me to the downloads, and then we’re going to want to download the very last option right here. Once that downloads, we’ll double-click that, and it’s going to open up our finder. We want to head inside of this assets folder, go inside of packs, and then click Sunnyland. There’s going to be a Sprite sheets folder, so we want to grab this right here: player.png. I’m going to copy this by pressing Command C (if you’re a Windows user, press Control C).

Then, I want to find our project folder. It’s been a while since I did this tutorial, so my project folder is going to be within my downloads folder, and it’s called Sunnyland. Here it is! I’m going to go inside of images and then paste in that player Sprite sheet. If we did this correctly, we can go back to our code, and we’ll see that player.png is now available to us. We can begin to use this within our project, and that’s exactly what we’re going to do.

So, within player.js, image.source is supposed to be equal to some sort of string that represents where this image we want to use is located. So, where is it located? Well, we need to go inside of our images folder, and then we can reference player.png. If we did all this correctly, we should have some sort of programmatic representation of this image right here. But to see this drawn out on the canvas, we need to call a very specific canvas function, and I’m going to do that within our draw method.

=> 00:52:39

Mastering animation in coding is all about timing and precision—crop your images wisely and watch your characters come to life!

Sunnyland: So here it is, I'm going to go inside of images and then I'm going to paste in that player Sprite sheet. If we did this correctly, we can go back to our code and we'll see that player.png is now available to us. We can begin to use this within our project, and that's exactly what we're going to do.

Within player.js, the image.source is supposed to be equal to some sort of string that represents where this image we want to use is located. So, where is it located? Well, we need to go inside of our images folder and then we can reference player.png. If we did all this correctly, we should have some sort of programmatic representation of this image right here.

However, to see this drawn out on the canvas, we need to call a very specific canvas function. I'm going to do that within our draw method. That's going to be called c.drawImage. The very first argument that c.drawImage uses is going to be a JavaScript image object. Luckily for us, we have that available, so we can reference this.image which we set up above. The next arguments are going to be X and Y coordinates, which we also have available to us, so we can reference this.x and then this.y like so. We should see this being drawn out on the screen.

But note, if we try calling c.drawImage and this.image is not fully loaded yet, we are going to get a bug and our game is going to error out. Therefore, what I like doing to make sure that this.image is available to us before we call c.drawImage is adding in some sort of this.imageLoaded property. By default, the image will not be loaded, so we'll set that equal to false.

We can tell when the image has loaded by referencing this.image.onload, setting this equal to a callback function. Now, whenever our image loads, we know that this.imageLoaded will be equal to true. So, onload just automatically fires as soon as JavaScript determines that our image is fully stored within this.image. Yes, we can place this before we're actually setting it. I believe I saw somewhere that it's actually recommended to call onload before we set the source. I forget why; it shouldn't really matter honestly. We can put it in either location, but just know that this will still work regardless.

Now that we have this property available to us, we can begin to use it within our draw method by saying we only ever want to call c.drawImage if this.imageLoaded is equal to true. This should prevent any bugs from occurring if this image is not loaded. Let's save this and test it out to see how things look.

Going back over to our game, you can now see our image is being drawn out in its entirety, which is really cool, but also pretty stupid looking. So, what we want to do is only render out a specific portion of this image. It’s very important that we understand how animation works in this case.

I'm going to open up player.png right here within Sublime Text to help give us a visual representation of how this works. Right now, we are currently rendering out this whole image in one go, but really we only want to render out one frame at a time, specifically this little square right here. The whole idea is we're going to crop our image to only render out this one specific portion.

Then, over time, we're going to move the X location of this crop box. Let's say we are ready to move on to our next frame; we are going to grab the crop box and then push it over to the next frame of the animation. Once we're ready to go to the next frame, we'll go to the next and so forth until we get to the end, and then we say we want to go back to the very beginning. If we do this in rapid succession, we're going to get the illusion as if we have some sort of animation in place.

This first animation is being an idle state, so it's just going to be our character bouncing up and down. You can see there are a few states provided to us here within this Sprite sheet: we have a running state, a climbing state, a ducking state, I believe this is a death state, jump state, fall state—you kind of get the idea.

We’re going to implement the most important ones, specifically the idle state, the running state, the jump, and then the fall state using this method of cropping the image and then moving the crop box over time. So, how would we go about doing that? Well, let's go over to player.js and then right beneath this image.source, I'm going to add in something called this.elapsedTime, setting that equal to 0. I want another property called this.currentFrame, which is also going to be set to 0.

We are going to use these later; just keep them in the back of your mind for now. But the most important thing that we need to start off with is adding in some sort of crop box. So, let's just start by cropping our image from this big large...

=> 00:56:48

Master the art of image cropping in game development to bring your animations to life.

In this section, we will discuss the implementation of various states for our player character, specifically the idle state, running state, jump state, and fall state. The method we will use involves cropping the image and moving the crop box over time.

To begin, we will navigate to player.js. Right beneath the image source, we will add a couple of properties: this.doElapsedTime, which we will set equal to zero, and this.doCurrentFrame, also set to zero. These properties will be utilized later, so keep them in the back of your mind for now.

The most crucial step is to add a crop box. We will start by cropping our image from the large sprite sheet to a specific portion. This cropping will take place within the c.drawImage method. Although drawImage is not my favorite method due to its complexity, it is essential for our task. Currently, we have three arguments for drawing an image on the screen at two specific coordinates. However, to crop an image using c.drawImage, we need nine different arguments, which seems excessive. I believe there should be an options property to simplify this process, allowing for a more flexible argument order. But let's set aside that critique and focus on the implementation.

First, we need to define the coordinates, width, and height of the crop box. I will create a constant called cropBox right above the c.drawImage call, which will be an object containing x, y, width, and height. For our crop box, the starting coordinates will be x = 0 and y = 0, as we are beginning from the top left corner of the image.

Next, we need to determine the dimensions of the crop box. Since all the images in the sprite sheet are 32x32, I will set both the width and height of our crop box to 32. Now that we have our dimensions set for one portion of the sprite sheet, we can use them within c.drawImage.

The second and third arguments of c.drawImage will represent the crop box position. Therefore, we will replace the existing this.x and this.y with cropBox.x and cropBox.y. The fourth argument will be the width of our crop box (cropBox.width), and the fifth will be its height (cropBox.height).

Next, we need to specify where we will draw the cropped version of the image. We can use this.x and this.y for the next two arguments, followed by the width and height for the last two arguments, which will be this.doWidth and this.doHeight.

This process has resulted in a function with nine different arguments, which may seem cumbersome, but it should work if implemented correctly. After saving the changes and returning to our game, we can see that we have a cropped version of our image, albeit quite small. To expand the visibility of our player character, we can adjust the width and height for the last two arguments.

Currently, these dimensions are set above when instantiating the player in index.js, specifically on line 116 with player = new Player(). I will change the size from 16 to 32, effectively doubling it. This adjustment should allow us to see our player rendered more clearly.

To enhance visibility further, I will zoom in within my browser, ensuring that you can observe the subtleties in the animation. This step of cropping our image to a specific portion of the sprite sheet is crucial. If I were to change the crop box location, for example, by shifting down to a different row in the grid layout, you would see the effects of that change as well.

=> 01:00:57

Animation is all about timing and precision; even the smallest adjustments can bring your game to life.

In this section, we will discuss how to set up the player and animate it effectively. We know that these are set up above by the size that we're passing through when instantiating a player. We are instantiating the player over an index.js file, specifically on line 116, where player is equal to new player. For this example, I am going to change our size from 16 to 32, effectively doubling it. This change should allow us to see our player rendered out a bit better, and indeed, we are noticing an improvement.

To enhance visibility, I will zoom in within my browser so you can see this even better. This is important because we want to start animating the player, and I want to ensure that you can see the subtleties in the animation. The first step is just cropping our image to one specific portion of our Sprite sheet. If I were to change our crop box location, for instance, by shifting down to the next row, I can do this because everything is laid out in a little grid of 32 by 32 size squares. By pushing our y-coordinate down by 32 pixels or 64 pixels, I can render out the desired portion.

To test that this works, I will change our y-coordinate to 64, which we are using down below with cropbox Y. Now, you can see that we are rendering out the beginning of the climb Sprite, which is pretty cool. I will revert back to Y zero just to confirm that the previous adjustment worked.

The next portion of this process involves creating some animation. Currently, we are at this point with our red cropbox, but how do we begin moving this to the next frame of the animation? After a certain amount of time has passed, we want to increase a value called this do current frame. We created that earlier, and it will help us move the crop box based on whatever frame we should currently be on. For example, if we add a value of one onto this do current frame, we will move our crop box over one to the right. If this do current frame equals two, we will move to the next position, and finally, if it equals three, we will reach the last position. If we want to go back to the beginning, current frame will be equal to zero.

We are essentially looping through an indexed array. The first thing we want to do is determine when we should be adding a value of one onto this current frame to push things over to the right. We will only add one onto this do current frame when a certain amount of time has passed, which is crucial to remember.

Let’s navigate to player.js, specifically within the update function. Right after checking if there is no Delta time, I will start updating the animation frames. For every time we run update, I will always take this do elapsed time and add the current amount of time that has passed between this and the next frame. This will allow us to get the current time associated with our game, and we will store that elapsed time within this property.

Now, we can say that if this do elapsed time is greater than some specific interval, say 0.1 seconds, we want to take this current frame and add one onto it. This action will help us start moving the box over to the right. However, if we continuously keep adding one to the current frame, we will eventually move our crop box to a position where there is nothing to render.

To manage this, I have a nice way to oscillate this do current frame between values of zero and three, as those are the only values we can use when moving our crop boxes. We never want to go to four; instead, we want to return to the beginning. So, how do we begin oscillating between those values instead of just adding one onto this current frame?

We will set this current frame equal to this do current frame plus one, all wrapped in parentheses, and then use the modulo operator to divide by the number of current frames within whatever row we are trying to animate. For our first animation, we have four frames: 1, 2, 3, and 4. Therefore, I will use modulo 4 to allow us to oscillate between values of 0 through 3.

To illustrate how this works, let’s do the math. Let’s say current frame is equal to zero, which it currently is. If we add one onto it, we get a value of one. Now, if we apply modulo four to this, we need to determine how many times four can go into one. Since four is too big to go into one, we have a result of one.

=> 01:05:01

The modulo operator is your secret weapon for seamless animation loops, letting you effortlessly cycle through frames without a hitch.

In this explanation, we are going to use the modulo operator to divide by the amount of current frames within the row we are trying to animate. For our first animation, we have 1, 2, 3, 4 frames. Therefore, I will use modulo 4, which will allow us to oscillate between values of 0 through 3.

To understand how this works, let's do the math. Let's say the current frame is equal to zero, which it currently is. We have a value of zero, and when we add one to it, we get a value of one. Now, if we apply modulo 4 to this, we need to determine how many times 4 can go into 1. Since 4 is too big to go into 1, we have a result of zero. However, when using modulo, we are interested in the remainder. Thus, the remainder when dividing 1 by 4 is 1. This means we actually get a value of 1.

Next, let's consider what happens when the current frame is equal to 1. When we call update, we add 1 to it, giving us a value of 2. Now, applying 2 modulo 4, we find that 4 still cannot go into 2, so the remainder is 2. For the next iteration, we will have 3 modulo 4, which gives us a value of 3. Finally, when we reach 4 modulo 4, we see that 4 can go into 4 once, resulting in a remainder of 0.

At this point, we start back at zero, effectively looping through the values 1, 2, 3, 0 continuously. This approach is efficient because when we need to switch to a different row that has more frames, such as our Running Animation with six frames, we can easily change the 4 value dynamically. This will allow us to loop between 0 to 5 instead of 0 to 3. This function is well set up, providing a clean and effective way to achieve the desired results.

Now, we are almost done. Once 0.1 seconds has passed, we want to clarify this process by creating a constant called seconds interval, which we will set equal to 0.1. This constant represents the amount of time in seconds that we want to increase our frame. When moving to the next frame, we need to consider the lapse time and subtract the seconds interval from its current value. The lapse time can vary (for example, it could be 0.12 or 0.14), so we must account for this variance to ensure consistency in frame updates.

Given that we have set everything up correctly, we can now begin using the current frame within our draw method. To move our crop box from one position to the next, we will focus on the crop box's x-coordinate. For instance, if we want to move this over by one to the right while keeping crop box dox in place, we will add crop box width multiplied by the current frame.

To illustrate, if our crop box is equal to 32 pixels wide and the current frame is equal to 1, multiplying 32 by 1 gives us an x-coordinate of 32. If the crop box starts at zero, we are effectively moving our crop box on the x-axis over by 32 pixels.

To clarify further, if the current frame is equal to zero, we multiply that by the crop box width (which is 32). Therefore, 32 * 0 equals 0, and when we add that to the crop box x, which is also zero, we are rendering the very first iteration of whatever row we are on. However, when the frame is equal to 1, we start pushing the crop box over by 32 pixels based on the value of the current frame.

=> 01:09:16

Animation is all about precision; even a single pixel can make or break the flow.

To begin with, we need to multiply it by one that gives us an x coordinate of 32. The crop box is equal to zero, so we're basically saying to move our crop box on the x-axis over by a value of 32 pixels. Let's start back from the beginning just to really comprehend this. If current frame was equal to zero, which we're always starting off with, we multiply that by cropbox width, which is 32. What is 32 * 0? Of course, it is equal to zero. We add that on to cropbox X, which is zero. So, essentially, we are saying that in that case, for frame number zero, we're going to be rendering the very first iteration of whatever row we're on.

However, as I mentioned, if frame was equal to one, that is when things add up; they make sense, and we start pushing this crop box over by 32 pixels. Based on the value of current frame right here, as long as we're multiplying that by cropbox width, we are always going to be moving our crop box one over to the right until we get to the end. We know that current frame resets back to zero, so we move back to zero. That’s really all we need to do to begin creating animation.

So, let's save and refresh. Yes, you can see that our player is definitely moving in some sort of idle state. However, it doesn't seem to be exact; it seems as if our player is kind of moonwalking forward a little bit, and we certainly don't want that. We want things to be as accurate as possible. Therefore, I think the spacing between the frames within the sprite sheet might be a little off. That's okay; we can just test things out by changing the width of our crop box to 33. As long as we do that, you can see the animation is now looking great. It's at a great rate; we're not changing too fast or too slow. I really like how this is looking.

But we're not done just yet. I want to add some sort of running animation as well. If we're about to add multiple animations, this is how I like doing things: I like creating some sort of main Sprites object that contains all the crop boxes that I want to switch between. The first state in which we're using right now is an idle state. It's just the animation of us standing in one place, bouncing up and down. Our player is currently idle.

Now, what are the crop box coordinates for our idle state? Well, we already declared those down below, so we're no longer going to use this cropbox const because we want to make it dynamic instead of static. Therefore, I'm going to cut this property out of place and then put it where our Idle Sprite property is right here. Now, we no longer have crop box down below, so let's make sure we delete that const. Since we no longer have crop box available to us within draw, we want to make sure that we're referencing something else. That something else we're going to reference is a new property that I'm creating called this.currentSprite.

Out of all the sprites within the Sprites object, which one are we currently on right now? I want to make sure that we are defaulting to this.sprites.idle. That's the only one we have available anyway, so it makes sense to set this.currentSprite equal to that. Now that we have this available to us and we no longer have crop box, we want to make sure that we replace all five instances right here with this.currentSprite. If we did this correctly, we should be getting the exact same result, but we're preparing ourselves to make this dynamic.

So, let's save that and refresh. Yes, things are still working correctly. But let's say that I want a running state instead. How would I do that? I'm going to duplicate this idle object and rename it to run. Now, all I have to do is determine what the running state's X and Y coordinates are. It looks like running is just right beneath the idle state, so if I bump our box down by 32 pixels, I believe we should have everything we need to start rendering out the running state.

Let's say that our y position for running is equal to 32, and by default, we're going to use the Run Sprite instead. If I save that and refresh, just like that, now we're running. However, we don't have the full run animation in place because if we look over here with an update, when we're using mod 4, this means we're only going to the 4th frame within this specific portion of the sprite sheet. We want to ensure that we get to the very end. Therefore, we need to make sure that this four right here is going to be a dynamic value, not a static one.

Instead, I'm going to divide by this.currentSprite.frames, which is the amount of frames within the current sprite animation. If we're adding a frames property, we want to make sure that we're adding that to each of the individual states within our Sprites object. So, how many frames do we have within our idle state? We have four, and how many frames do we have...

=> 01:13:24

Animation is all about smooth transitions; switch between idle and running states dynamically for a seamless experience.

Instead, if I save that and refresh just like that, now we're running. However, we don't have the full run animation in place. If we look over here with an update, when we're using module 4, this means we're only going to the 4th frame within this specific portion of the Sprite sheet. We want to make sure that we get to the very end. Therefore, we need to ensure that this four right here is going to be a dynamic value, not a static one.

So instead, I'm going to divide by this current sprite.frames, which is the amount of frames within the current sprite animation. If we're adding a frames property, we want to make sure that we're adding that to each of the individual states within our Sprites object. For instance, how many frames do we have within our idle state? We have four. And how many frames do we have within our running state? We have six. Let’s just make sure that this works again, and indeed it does. Our animation is a bit smoother because we're running through the full thing now. We're no longer doing just four frames; we're going all the way to the end and then going back to the beginning.

Next, we need to determine when we are in the running state and when we are in the idle state. By default, we always want to be in the idle state, but there will be ways in which we can start switching between the two. At the end of the update, I'm going to call a new method called this.doSwitchSprite. If I'm creating this method, I need to create it as a method within the class, so I will do exactly that. switchSprites is equal to a function, and what is going to happen in here? This is going to determine what the value of this.currentSprite should be equal to.

Now, under what conditions should we be switching from an idle to a running sprite? Let's write an if statement to figure that out. If we are currently on the ground, meaning this.isOnGround is true, and this.velocityOnXaxis is equal to zero, indicating that we're not moving at all, what do I want to happen? I want this.currentSprite to equal this.sprites.idle. These are the conditions which should occur for me to be bouncing up and down in the idle state.

Now, I don't want this to be set for every frame, especially if currentSprite is already equal to this.sprites.idle. Therefore, I will add one more and statement that says if this.currentSprite is not equal to this.sprites.idle, then that is when I want to set this equal to the idle state. Therefore, we're only going to do this once; it's not going to be set over and over again. This just makes things a little cleaner in the long run.

But I can also determine under what condition we want to change to the running sprite. So, we'll add an else if right here and say I want to go to the running sprite if we are on the ground. So, if this.isOnGround is true and this.velocityOnXaxis is not equal to zero, meaning we're moving either left or right, then we have one more condition that says if this.currentSprite is not equal to this.sprites.run, what do I want to do in this case? I want to set this.currentSprite equal to this.sprites.run.

Whenever I switch to one of these new sprites, I always want to make sure that I'm setting our currentFrame equal to zero. The reason I'm doing that is that let's say we're currently on frame number six for our running animation. If we were to switch back to our idle state in this case, we would render nothing out to start. We don't want that; it's going to cause a flash, and it'll just look weird. So, we want to make sure that we're always going back to the beginning of whatever we're rendering out. In this case, when we switch sprites, we want to do this for both our idle animation and also our running animation.

If we want to be as specific as possible, we can comma this out, saying this is idle and this is run. This should help us understand this in the long run. Given that we are calling switchSprites up above, we should see things change as we begin to move about our game.

So, let’s save that and refresh. We are currently in our idle state, which is great. If I begin moving, now we're in our running state, and immediately as I stop, we go back to our idle state, which is looking absolutely awesome.

Now, we only have a few more states to implement here, and for me, those are going to be the jump and fall states. So, how would we go about rendering these out? I want to make sure that I add these to our Sprites object. I'll add in two new properties there: the first one is going to be jump, and the second one is going to be fall. Now, all I need to do is set the values right.

=> 01:17:34

Mastering game development means perfecting every move, from idle to jump to fall. It's all about creating fluid transitions that keep players engaged.

In the long run, we need to understand the implementation of our switch Sprites. Given that we are calling switch Sprites above, we should see things change as we begin to move about our game. So let's save that and refresh. We are currently in our idle state, which is great. If I begin moving now, we're in our running state, and immediately as I stop, we go back to our idle state, which is looking absolutely awesome.

We only have a few more states to implement here, specifically the jump and fall states. To render these states, I want to make sure that I add them to our Sprites object. I'll add in two new properties: the first one will be jump, and the second one will be fall. Now, all I need to do is set the values correctly.

What would our y-coordinate be for the jump state? Right now, we're at 32. If we start extending this by 1, 2, 3, 4, and 5, we should get the correct y position. So, I'm going to take 32 and multiply that by five. The fall state will have the exact same value because, as you saw, the fall state is right next to the jump state.

Now, the jump state only has one frame, and the same goes for the fall state. The only other difference here, I believe, is for the x coordinate on the fall sprite. You can see the x coordinate here is pushed over to the right by about 32 or 33 pixels. I'm not really sure which it is, but we can just say 33 because that's what we're using above for our run animation or maybe it was idle. We'll just start with 33 there and see if it looks right. These should be the correct values for jump and fall.

Now we can begin determining when we want to change over to these sprites, and that's going to happen within our switch Sprites method. All we need to do is add in another else if statement and specify under what conditions our player should switch over to the jump sprite. If this.isOnGround is false, that means we're jumping. Additionally, if this.velocity.y is less than zero, meaning we're moving in the upwards direction, and this.currentSprite is not equal to this.sprites.jump, that is indeed a case in which we want to change our current sprite.

I'm just going to copy these three lines above and paste them into this else if for time-saving purposes. We know this is going to be our jump sprite, and our current frame should definitely be equal to zero. In this case, we're going to switch over to our jump sprite. So let's save that, refresh, and see if it works. If we jump now, you can see we are going straight into our jump sprite state.

Now we just need to implement the same thing for falling, which is very similar to what we just did with jump. I'm going to copy this else if and replace all instances of jump with fall. In this case, if our y velocity is greater than zero, that means we're moving in the downwards direction. So let's save that, refresh, and I can jump. Now, when we're going back down, we're going into the fall state, which is pretty freaking awesome.

The last thing I would do here for the sprite tutorial is show you how to swap or flip our sprite. If we're running over to the left, we should be facing left. It doesn't really make much sense to be running backwards if we're trying to go in one direction. To implement that, the first thing I'm going to do is right beneath this.currentSprite and add in a this.facing property to indicate what direction we are currently facing. By default, it's going to be equal to right.

At the very end of the update method, right before the switch Sprites, I'm going to call this.determineDirection, or we can even call this determineFacing. It doesn't really matter. I'll just say determineDirection. Then, I need to add this method in, so I'll create it down below. This method will determine the direction we're currently facing. If this.velocity.x is greater than zero, that means we are moving to the right, and we should be facing right regardless of whether we're standing still or still moving to the right.

In that case, I want to set this.facing equal to right. If this.velocity.x is less than zero, meaning we're moving over to the left, then this.facing should be equal to left. With this property now available to us, we can alter our code within c.drawImage to ensure that we're flipping things correctly. Flipping things within the canvas can be a little confusing, but it's nothing that we can't handle. So here's how we're going to do it.

=> 01:21:42

The direction your character faces should always reflect the way they're moving, ensuring a seamless and immersive gaming experience.

In this section, I will outline the method to determine the direction we're currently facing. If the dodge velocity on the x-axis is greater than zero, it indicates that we are moving to the right. In this case, we should be facing the right, regardless of whether we're standing still or still moving. Therefore, I want to set this do facing equal to right.

Conversely, if the velocity on the x-axis is less than zero, meaning we are moving to the left, then the do facing should be equal to left. With this property now available to us, we can alter our code within c.drawImage to ensure that we're flipping things correctly. Flipping things within the canvas can be a little confusing, but it's manageable.

To achieve this, we will use a global canvas method called c.scale. This method determines how large we want our scene to be, similar to a magnifying glass effect or zooming out. Additionally, we can set a negative value for x, which effectively flips our canvas. However, I only want to flip our character, not the entire canvas, as it wouldn't make sense to flip the whole world. We only want to flip our player based on the direction we are moving.

Since we want to flip only our player and are using a global method like c.scale, we need to wrap this within c.save and c.restore at the bottom. As long as the global method is within save and restore, we will only affect the draw code within that scope. To demonstrate how this works, we can save and refresh. However, we will no longer see our player because when we scale our canvas and flip it horizontally, our coordinates become misaligned.

For instance, let's say our X position is currently 100, where we are rendering our little square. After flipping, our character is rendered off the left side of the screen because they are being drawn out 100 pixels from the left. To correct this, when we draw, we need to reverse this value. If we do that, we can see our character returning to the screen.

We also need to consider our character's width. Thus, we need to subtract this.width from this.x to position our player correctly while facing the correct direction. To facilitate switching between facing left and right, I know that the values for x scale and x value need to be dynamic based on certain conditions.

The first variable I will create is x scale, which will default to 1. This variable will determine when we are flipping the canvas. The second variable will be x value, which will default to this.dox, representing our player's current position on the screen. Under certain conditions, I want to change these values. If this.facing is equal to left, then x scale will be set to -1, and x value will be equal to the reverse coordinate system we determined is necessary when flipping the canvas.

Now we can begin using these variables within scale and drawImage. We are already using x scale to determine whether it is 1 or -1, depending on the direction we are facing. We can also use our x coordinate, which is dependent on the direction we are facing. As a result, depending on which way we are moving, we should see our character flip.

After saving and refreshing, by default, we are facing the right, which looks great as we move to the right. If I switch to the left, our character flips accordingly. We can utilize all our cool animation switching regardless of the direction we are going. To enhance visibility, let's remove the red debug box for now to better appreciate the end result of our work. After saving and refreshing, everything looks fantastic.

Overall, I am very pleased with how this looks. I will zoom out to see the full picture; our game is really coming together and appears much more lively with these updates. We can apply these same techniques once we add enemies, like this little possum right here. But for now, that is how we will be handling the character's direction and flipping mechanics.

=> 01:25:42

Creating depth in your game isn't just about visuals; it's about making the experience feel alive. Parallax scrolling is the secret sauce that adds realism and excitement to your scenes.

Depending on which way we're moving, we should see our character flip. So, I'll save and refresh. By default, we're facing the right, which looks great. We're moving to the right, and if I go to the left now, our character flips. We can use all our cool animation switching no matter which direction we're going, looking really good. Now, let's get rid of this red debug box for now, just so we can really see the end result of our work. We'll save that, refresh, and then things are looking really, really awesome here. I'm very happy with how this looks overall.

I'll zoom out so we can see the full thing; our game is really coming together now, and it looks much more lively with this. We can begin using these same techniques once we add enemies, like this little possum right here. But for now, that is how we're going to be handling sprites and animation.

Next up, as our scene scrolls, I want my background elements to move at a different rate than the foreground. If our Bramble Bush is back there and both are moving at a different rate—something slower, preferably—it’s going to give off a more realistic effect. This realistic effect is known as Parallax scrolling. You're going to see this come into fruition very shortly, and it's a really, really cool effect to bring liveliness and just an overall, I don't know, better feel to your game. So, that's what we're going to do for this chapter.

Let's start now by heading on over to index.js. I want to start with my ocean and my Bramble bushes, and I want those to go at different rates. To do that, I need to make sure that we're separating these from everything else that's drawn on the screen. Right here within this layers data object, which is something generated from mapper mate, this just contains all the layers associated with our mapper mate project. Now, I didn't name these layers very well, as you can see, but I know which layers contain my ocean and which layer contains my Bramble bushes. That's going to be layer one and two.

You can see if I were to comment these out and go back to our game, refreshing those two layers are taken out of the rendering altogether, and that's exactly what I want to start with. So, preemptively, I'm going to need to create a const called ocean layer data. It's going to be equal to an object, and then here I would put all the layers associated with my ocean layer data. We only really have one that is going to be layer one for me, so I'll paste that in. Then, I need one more const called Bramble layer data, which too will be equal to an object. The layer that contains all the Bramble bushes is going to be layer two. Make sure you call this data, not date, like me.

Now we can begin using these, but where would we use these? If we scroll on down, we're going to want to focus on render static layers. This is a function that creates a programmatic canvas. You can see that's happening right here. I'm calling this offscreen canvas because it's never actually inserted into the HTML, which is good because there's no reason for us to do that yet. What this does is it loops over whatever properties are within this layer data object and renders everything out onto this offscreen canvas. It actually returns a canvas element that we're storing right here within this constant background canvas. Eventually, that's passed through into the animation loop, and we are calling c.drawImage on top of that background canvas, that offscreen canvas.

Basically, all we're doing is taking our mapper mate data, rendering it out onto an offscreen canvas, and then putting it onto our scene with c.drawImage. What I want to do is make sure that the ocean layer and the Bramble layer are put on separate canvases from this background canvas object that we already have right here. By doing that, we're going to be able to change the rate at which those two layers move.

But how would I make sure that I'm rendering those out onto separate canvases? Well, we can extend this function right here of render static layers. To extend this, I want to pass through an argument called layers data. If I call this layers data, what you can see is that we're referencing it right here. We're saying for this argument, loop through all the data and then render it out onto a certain canvas that we're creating. This canvas right here is no longer referencing the const up above; if we were to add in an argument like so, we're just referencing the argument.

If that's the case, we still need to draw out all that foreground stuff—all that foreground layer objects and tiles and all that nonsense. So right here, where we're actually calling render static layers, this is where we would want to pass through the layers data const.

=> 01:29:44

Layering your visuals can create depth and dynamism in your game, making the experience feel alive and immersive.

We can extend the function of render static layers by passing through an argument called layers data. If I call this layer as data, we can reference it right here. We're saying for this argument, we should loop through all the data and then render it out onto a certain canvas that we're creating. This canvas is defined right here, so we are no longer referencing the const declared above. Instead, we are just referencing the argument.

However, we still need to draw out all the foreground elements, including all that foreground layer objects and tiles. Right here, where we're actually calling render static layers, we want to pass through the layers data const that we declared at the very top, or at least that mapper declared. I'm going to pass that through, and if we do that correctly, save, and go back to our game, we will still see our foreground, which is great.

Now, we are setting ourselves up for success because we can duplicate this line where we're calling render static layers. However, we will no longer pass through just our foreground layer data; instead, we will pass through the layer data that we declared at the very top for ocean layer data and eventually Bramble layer data. So, let's pass through ocean layer data like so. This isn't going to be stored in a background canvas const, but rather in an ocean background canvas const.

I don't actually want this to be a const because I want to call this with an a c. draw image method up above within animate. It would be kind of annoying to have to pass this through as an argument in animate, so I'm going to get rid of the const here. I'll copy ocean background canvas and then right above my animation function, I'm going to declare a let and set that equal to null. Now I have this null canvas, and I'm setting it when we call start rendering. We are storing something inside of it, which we can now draw with c. draw image. I'll place that in as the first argument, adding z0 for the Y and X position. If we save and go back to our game, we will have our ocean rendered out, but on a separate canvas—a great start!

Next, let's go ahead and render out our Bramble Bush canvas as well. To do that, we want to create a const above animate called Bramble background canvas. Then, down below, when we call start rendering, we want to make sure that we are storing whatever is returned by render static layers within Bramble background canvas. What kind of data do we want to render out? Well, whatever data is stored within our Bramble layer data object.

We will go back down there and make sure that we're replacing ocean layer data with Bramble layer data. Now we want to draw this out, so I’m going to duplicate c. draw image right here and ensure that I'm drawing this out in between our ocean and then our foreground tiles. I'll say our Bramble background canvas is going to be drawn right in between those two. After saving and refreshing, everything looks the same. How cool is that? Did we really do anything? Not really, but we will because we're setting ourselves up for success, as I said.

So, how do we begin moving these separate layers? Really, all we need to do is change the x coordinate for ocean and Bramble background canvas. If I were to change our ocean background canvas to have an x value of 200, going back to our game, you will see that I just pushed that off to the right by 200 pixels. However, we don't want to do just that; we want to increase this value over time based on where our camera currently is as our scene scrolls.

Let's start with ocean background canvas. I'm going to replace zero with our camera dox and multiply this by a value like 32. By multiplying by a fraction, we're saying don't actually move this layer at the same rate at which camera X is moving; instead, move it at something much slower. You'll see just what a cool effect that creates by adding that code in there.

If I save and refresh, as soon as our scene starts to scroll, you'll notice the foreground moving at a different rate compared to the background. You can see there that the background is moving at a much slower rate compared to the foreground, creating a really nice lively effect. Logically, if we look at this, we have our Bramble bushes up next. Since those are closer to our character, they should technically be moving at a quicker rate. Imagine you have a camera looking at the scenery; if you were to start panning that camera to the side, the things in front of you, the things closest to the camera...

=> 01:33:52

Creating depth in your game isn't just about visuals; it's about simulating reality. When foreground elements move faster than the background, it brings your world to life.

To create a cool effect in our game, we need to add some code that will enhance the visual experience. When I save and refresh the scene, moving to the right will reveal that the foreground moves at a different rate compared to the background. As the scene starts to scroll, you'll notice that the background is moving at a much slower rate than the foreground, which creates a really nice lively effect.

Logically, as we analyze this, we have our Bramble bushes next. Since these bushes are closer to our character, they should technically be moving at a quicker rate. Imagine a camera looking at the scenery; when you pan that camera to the side, the objects closest to you will move much slower than those far away in the background. Thus, we are essentially simulating reality here.

To simulate the Bramble bushes moving, we will take our zero coordinate for the X position of the Bramble background canvas and replace that with camera x times a lower fraction value, like 6. After saving this change and refreshing the game, you will see that the Bramble bushes are now moving a bit quicker than what we observe in the background, specifically the ocean. I believe these values are perfect; I really love the way this looks. It creates that depth feel that is characteristic of many side-scrollers today, such as Donkey Kong and New Super Mario Bros. Almost every good side-scroller employs some form of parallax scrolling, which enhances the liveliness of the scene.

The only other point I want to address is that as we reach the end of our map, some elements may get cut off. This is perfectly okay. To fix this, we would need to head over to Maper Mate and extend our map even further. There is a little "columns" option available, and I believe we discussed this at the beginning of the video. I can extend this out by another 40 columns.

To do this, I will go over to layer one and ensure that I am selecting my background artwork. I will position it correctly, starting from a specific point and moving it over to draw it out a bit more. You might want to extend your map slightly to allow for additional space for the parallax background elements, such as the Bramble bushes, ocean, and sky. After extending the map, we can export this data and replace the current data in our data folder. Theoretically, this should allow us to show more of the scene to the right compared to what we currently have, as we had previously cut off our scene early.

Now, let's move on to implementing hitbox functionality. If we look at our player, they are currently standing on nothing, which doesn't make sense. We want them to fall and improve our collision detection in the process. To implement a hitbox, we will go to player.js and the first step is to add a property called this.doHitbox.

We will set an X value equal to zero, a Y value, a width, and a height. This hitbox will represent the area in which we want to detect collisions, rather than relying on our current box, which is indicated by some commented-out code. If I were to uncomment that and refresh, it would show where our collision detection is currently happening. However, we want something smaller, which is why we are creating the hitbox.

To start, we can keep the X and Y values at zero, but I think our width should be around 20, and the height might also be 20. We will figure this out as we go along. The first thing I want to do is duplicate our code for the debug box to visualize our hitbox. Instead of using a red box, we will adjust the color accordingly.

=> 01:37:45

Creating a precise hitbox is key to accurate collision detection in your game.

We are going to represent the area in which we want to detect collisions with a smaller hitbox, rather than our current box, which is actually indicated by this commented-out code right here. If I were to uncomment that and refresh, this is where our collision detection is currently happening. However, we want something a bit smaller, which is why we're creating the hitbox.

To start, what should our X and Y be? I think we can keep it at zero to start, but our width and height should be something like 20 for both. I'm not really sure; we are going to figure this out as we go. The first thing I want to do is duplicate our code for the debug box and debug our hitbox just to see what things look like visually. Rather than using a red box, I want to make this blue, so I'm changing the first value here to zero, and then for the blue value, I'm increasing that to 255.

Now, instead of rendering out this.x, this.y, this.width, and this.height, I want to render out our hitbox's X, Y, width, and height. Given that we did all this correctly, it looks like our hitbox is in the top left corner because we are just setting that equal to a value of zero. This isn't relative to where a player is going. If we wanted to do that, we would have to go inside of our update method, and we do want to do this, so make sure you're following what I'm doing here.

We want to update our hitbox position to follow wherever our player is on the screen, at least to start. So, this.hitbox.x is going to be equal to this.x, and then our Y value is going to be equal to this.y. As long as we add that there, now our hitbox is going to follow our player no matter where they go. However, we want to offset our hitbox to only surround the visible area in which we want to be activated when collisions occur. We need to give this about four pixels on the X-axis and about eight pixels on the Y-axis.

Let's save that and refresh. It looks like we are a little off; I think the top of our hitbox looks good as it kind of contains the ears. Maybe we can push this down by one on the Y-axis. I think that's really good, but we want to make sure that our hitbox extends all the way to the very bottom of where our player's feet are being rendered out. There's a little gap right there, and if I zoom in, you can definitely see that we want to add about five or six pixels onto our hitbox height. So, let's add four and see how that looks.

We are going a bit past our player's feet, so I'm going to switch that over to three, and that seems to be in the perfect position. Now that our hitbox is being updated correctly and is in a good spot, we can begin to use it within our collision detection code. The collision detection code we have currently is already generated for us with mapper mate within player.js. Specifically, we're going to focus on updating the horizontal position and then checking for horizontal collisions.

We need to update our hitbox position a bit more than we already did. Let's go to the update horizontal position, which is going to be on line 182 for me. I want to make sure I'm not just updating our render box, which is represented by this.x. I also want to update our hitbox X by whatever velocity we're setting our player to, because we're going to use this updated position within the next function we call, which is to check for horizontal collisions. This checks for all the collision blocks that we've added to our game, and if our player is currently colliding with one of them, we stop their movement altogether on the X-axis to start.

Rather than checking for collisions with a render box, we are going to check with our hitbox. Any place right here within this if statement where we're checking for collision—where you see this.x, this.y, this.width, and this.height—we are going to change that to reference our hitbox instead. There are going to be six locations right here, right here, right here, and then right here, and that's what we need to start.

Now we're detecting for collisions with our hitbox, but we need to respond to this hitbox collision code differently. Right now, we are changing our render box's position to be wherever we are colliding with a collision block, whether we're going left or right. However, we want to now change our hitbox's position, and then after that, we will change our render box's position to be equal to the hitbox position minus a specific value, which you'll see shortly.

We want to make sure that in both places where we're going to the left and where we're going to the right, we are referencing our hitbox and altering that X position. After that, we will set the render box's position accordingly.

=> 01:41:59

To achieve smooth collision detection in your game, always update the hitbox position first, then adjust the render box accordingly, accounting for any offsets to avoid jarring movements.

We need to start by identifying the locations right here, right here, right here, and then right here. Now, we are detecting for collisions with our hitbox, but we need to respond to this hitbox collision code differently. Currently, we are changing our render box's position to be wherever we are colliding with a collision block, whether we're going left or right. However, we want to change our hitbox's position first. After that, we will change our render box's position to be equal to the hitbox position minus a specific value, which you'll see shortly.

It is essential to ensure that in both cases—when we are moving left and when we are moving right—we are referencing our hitbox and altering its x-position. After that, we will set the render box's position equal to the hitbox's position. If we save this and then go over to our game to activate a horizontal collision, you will see that we immediately move up onto the next platform. This is because we need to check for collisions with our hitbox rather than our render box on the vertical axis.

We have just updated things for horizontal collisions, but we also need to do the same for updating our vertical position. This is what we are doing right here after we update our horizontal position and check for horizontal collisions. We will go inside the update vertical position function and ensure that we are also updating the hitbox's y-position to be affected by our y-velocity. Once we change our hitbox's y-position, we need to ensure that this is taken into account for our vertical collision detection.

As you can see, we are still checking with the render box. In any place where we have a render box, we want to update it to our hitbox's x and y width and height. This will be six locations right here. Now, we are checking for collision detection with the hitbox, but we will react to this a bit differently. When our player is going up, meaning our head has hit a platform above us, we will no longer change our y-position directly. Instead, we will change our hitbox's y-position, and based on that, we will change our render box's y-position to be equal to the hitbox's y-position.

When we are going down, if you think about it, our hitbox's y-position on the bottom of our player is the exact same position as our render box. If we look at our game, the blue and the red boxes are in the exact same position on the bottom of our player. Therefore, we don't need to do anything complicated here. We can simply copy where we are setting this y-position equal to the collision block's y-position plus the hitbox's height. We will change our hitbox's y-position along with our render box's position, but we will not reference our render box's height here; we will reference our hitbox's height.

As long as we did everything correctly, our collision detection should be based on our hitbox, which it currently is. If I go horizontally into one of these blocks, you can see that it is being taken into effect here with our hitbox, and the same thing happens on the right side. However, there is that jarring kind of jumping movement. This occurs because when we check for horizontal collisions, we are setting our hitbox's x-position equal to the right side of whatever block we are colliding with.

This is the right side of a block, and if we translate that visually, we are saying to move the blue box to the right side of this block, which is correct. However, right after that, we are also saying that this x-position where the red box is is equal to the hitbox's x-position. If we are moving our blue box to the position next to the collision block and then moving our red box as well, we are effectively shifting our player over to the right.

This is the correct position, but we need to ensure that when we set our hitbox position on the x-axis, we account for the gap between the hitbox and our render box, which is about four pixels. For our y-value, it will be nine pixels. Therefore, back where we are checking for horizontal collisions, we want to make sure that the x-position is not set to hitbox x, but rather hitbox x minus that little bit of extra space. This adjustment will ensure that we are not causing our players to jump unexpectedly.

=> 01:46:03

Precision in collision detection transforms gameplay, eliminating jitter and enhancing movement fluidity.

X we're moving the hitbox and the render box over to the right. That's a little confusing, but that's pretty much why we're getting this jumping right here. When we're setting our hitbox position on the x-axis, we need to make sure that we're taking into account this little gap between the hitbox and our render box, and that is about 4 pixels worth. I believe it's pretty much whatever we're adding onto our x value right here when we're updating our hitbox position.

So, for our x value, it's going to be four, and for our y value, it's going to be nine. Back where we're checking for horizontal collisions, we want to make sure that X is not set to hitbox X but hitbox x minus that little bit of extra space in which we used to move our hitbox. This is going to ensure that we're not jumping our player's rendering around, but we're still basing things off of the hitbox.

Collision detection up above requires us to do this for both checks right here, whether or not we're going to the left or to the right. Any place you see where we're setting this.x, you want to make sure that you're subtracting that offset value right here. So, let's save that, refresh, and then check our horizontal collisions again.

Now, when I run over here, you can see that we're taking that offset into account. There's no more jumping or jittering. However, when I go over to the right, it looks like we still have a little bit of jitteriness there. The reason for this is because I forgot to update this position right here to be based off of the hitbox width and not our render box width. You still want to make sure that you have minus four here; I can promise you that.

Let's refresh and test over here when we're going to the right. Now you can see there's no more jittering, which is great. So, we're almost done, but you'll see when I jump up here, I kind of get stuck on the ceiling. We want to basically do the same thing that we did for checking for horizontal collisions but for the Y-axis, specifically when we're going up.

This do hitbox the X has a big type A in it, so that's wrong to start, but we're going to fix that. Make sure that everything's spelled correctly, and then you want to take into account the offset on the y-axis, which we know we set up above. The update for that y offset is going to be nine. If we go back to checking for vertical collisions, we want to make sure that our render box y is equal to the hitbox Y, but we want to subtract nine for that to give us its true position.

So, if we write out minus 9 there, refresh, and hit the ceiling, you can see that now our collision detection is based off the hitbox for the Y-axis, and that's pretty much all we need to do there. You'll see when we spawn, we're no longer stuck on the edge. I can jump up, I can get on top of it, and we only fall once our blue hitbox moves off the side. This is also going to allow us to get into a certain position over here, which you might not have been able to get into otherwise.

This little alcove right here is barely just small enough to where our render box wouldn't be able to fit, but since we're using a hitbox, we can move around in here, we can jump, and we can explore things more accurately compared to when we were just using our render box for collision detection.

Now, the final step here would be turning off our debug boxes, and we can do that within our draw methods. Comment those out, and now we have some pretty good collision detection in place with our hitbox. The next thing I want to cover is how to create some sort of enemy, and the enemy is going to be this little Pome guy that you see right in front of us.

Right now, we just have this built out and rendered in Mapper Mate, but now we're actually going to bring the enemy into the game and start interacting with it. So, how do we get some sort of enemy sprite sheet? Well, the same way we got our player sprite sheet. When we downloaded all the Sunnyland assets, we also got a few other sprite sheets within the sprite sheets folder. The one we want to focus on is going to be aosm PNG; this is going to be our bad guy.

There are a couple of other ones in here, and we might use some of these other sprites like the gem, but for now, we're just going to use the aosm. We want to bring this into our game, and to do that, I'm going to copy this with command C, go over to our images folder within our project, and then paste it in. Now that this is available to us, we should see it over in Sublime Text or whatever text editor within our images folder, and you can see that right here.

So, how do we start using this sprite sheet? Well, the easiest way to do this is simply to copy our player.js class. Yes, we could do some abstraction to make this a sprite class and base our player off of that, but over-abstracting things becomes a...

=> 01:49:59

Sometimes, keeping it simple is the best way to get things done. Duplicating code can save you time and headaches when abstraction gets messy.

Within the Sprite sheets folder, the one we want to focus on is going to be aosm PNG. This is going to be our bad guy. There are a couple of other ones in here, and we might use some of these other sprites, like the gem, but for now, we're just going to use the aosm.

To bring this into our game, I'm going to copy this with Command C, go over to our images folder within our project, and then paste it in. Now that this is available to us, we should see it over in Sublime Text or whatever text editor we are using within our images folder, and you can see that right here.

So, how do we start using this sprite sheet? The easiest way to do this is simply to copy our player.js class. Yes, we could do some abstraction to make this a Sprite class and base our player off of that, but over-abstraction becomes a big pain in the butt. There's definitely a fine line between when abstraction is useful and when it starts acting as a detriment. I really think sometimes it's just easier to duplicate things.

In this case, we're going to duplicate this class. We're going to create a new one by pasting in all of player.js into a new file. We will save this within our classes folder and call it aasum.js. I think that's how you spell aasum, and that's how I'm going to spell it. Now we can begin altering this as needed.

The first thing we need to change is the class name; of course, this should be a possum. We also need to change these consts up here because these are global consts. When we pull them in within index.html, they're going to be available to all the files beneath it. So, we want to make sure that these are referencing very specific variables rather than generic ones that are also used within player.js.

As a result, I'm going to select both instances of x velocity—actually, there are three instances—and I'm going to change this to AUM x velocity. Basically, all I'm doing is prepending these consts with AUM underscore. The same goes for jump power; I'm going to replace this with AUM jump power. For gravity, in these two locations, I want to use AP possum gravity. Then, at the top, I want to make sure that that also has posum underscore. This should fix any pending JavaScript errors that might occur when we pull this file into our game.

To pull this file into our game, we want to go to index.html and duplicate the script tag where we're using player.js, but reference AP pm.js instead. Let's make sure we don't have any errors after importing this. I'll right-click, hit inspect element, and view our console. We seem to be good to go.

Next, let's create an AP possum. To do that, I'm going to go to index.js and find where we're creating a player. I'm basically going to duplicate this code, but instead of referencing player, I'm going to reference aasum. Then, I want to create an instantiation not off the player class but the AP possum class, and I'm going to center AP possum about 400 pixels over to the right, so I'll set x equal to 400.

Now that we have this aosm const, we want to make sure that we're drawing this out. To draw this out, we're going to go down to where we're updating our player. We're also going to update our AP possum position by calling a possum do update. Our update method takes in delta time and collision blocks, the same exact thing we did for our player. We also want to make sure that we're calling not player.draw but a a.draw down below.

As long as we did all this correctly, we can save and refresh. This is technically our aosm right here; we just have another instance of our player because we're still referencing our player sprite sheet. So let's fix that next. I'll go over to AP pm.js and make sure that instead of referencing our player sprite, I'm referencing a pm.png instead. I'll change player to AUM, save, and refresh.

Now we have something going on here, but the animation style for our aosm has different dimensions required for this to work. So, rather than referencing our player's idle sprite data, I'm going to get rid of that. The same goes for jump and fall because there's only one state for aasum, as you can see here, and it is run. I'm going to keep this run instance, but our a possum's y for our crop box is going to be zero because we're not actually moving the crop box down anywhere within the sprite sheet. Therefore, we're going to change y to zero. I think the height is fine; we have six frames, which is perfect, but our width might be a bit different, so we're going to play with this. We also want to make sure that we're changing sprites do idle to equal run by default.

=> 01:54:11

Simplify your code by focusing on the essentials; less is often more in animation design.

AUM save and refresh, and now we have something going on here. However, the animation style for our apum has different dimensions required for this to work. Therefore, rather than referencing our player's idle Sprite data, I'm going to get rid of that. The same applies for jump and fall, because there's only one state for aosum, as you can see here, and it is run. So, I'm going to keep this run instance, but our aosum's y for our cropbox is going to be zero because we're not actually moving the cropbox down anywhere within the Sprite sheet.

Thus, we're going to change y to zero. I think the height is fine; we have six frames, which is perfect. However, our width might be a bit different, so we're going to play with this. We also want to make sure that we're changing Sprites to Idle equal to run by default. Since we got rid of all those other Sprites within our Sprite object, we also need to ensure that we're getting rid of switch Sprites because there's only one Sprite for our aosum. We can just get rid of all this code and make sure that we're deleting the method call with an update.

After saving that and refreshing, we can see how things look, and that's looking better. However, we definitely need to alter the width in which we're changing our cropbox location. Altering the width of our crop box, we can go up to 34 and see how that looks. It seems to be getting a bit closer, so let's go up to 35, and I think 36 will do it. Thus, each frame of our aosum animation is going to be 36 pixels wide, and that looks great.

But our aosum really isn't touching the ground right now, so let's render out our debug boxes and fix this up real quick. I'm going to render out our hitbox and our render box, and as you can see, we have a little bit of a gap between our aosum's feet and the bottom of the platforms. Therefore, we should just need to change our height for our crop box within the Sprite. I think if we go down to something like 28, it should fix things immediately. I'll save and refresh that, and I think that actually is perfect.

However, I want to make sure that our hitbox here is a bit wider. I think the height is good, but really the hitbox doesn't cover the full distance of our actual aosum. So, I want to expand that. I actually don't want any gap right here between the hitbox and the render box because the nose of the aosum is all the way to the left. Therefore, when we are updating our hitbox with an update, I'm going to get rid of this plus 4. Remember, this little offset was being used for Collision detection on the x-axis.

I don't know if we're actually going to use Collision detection on the x-axis for our aosum moving into Collision blocks, but just to be safe, while we're checking for horizontal collisions, we want to get rid of this offset right here as well. As I said, we're probably not going to use it, but that's okay; we're just being safe here by getting rid of the offset. We should see the hitbox all the way to the left, which is great.

Now, we need to expand the width, and we can do that by heading up to our Constructor, finding our hitbox, and let's add about a pixel onto our width here. After refreshing, that seems to be closer. However, I think I want a little more, so let's go up to 30, and maybe I'll even go to 32 to get the full extent of the tail. But I'm kind of having second thoughts here; I think I'm going to stick with 30. So, that looks good for our hitbox.

Now, I want our aosum to be moving left and right between a certain distance. The first thing we need to do to create this effect is to add some sort of velocity onto the aosum. Right now, the velocity is zero on the x-axis, and the default, as you can see, is zero. However, we want it to be moving to the left by default. So, I'm going to say that our default x velocity is actually going to be -20. We can actually use the constant that we declared up above if we want to be super nerd coders. I want this to be very slow, so I will set it to negative -20, ensuring that the aosum is moving to the left.

This is the default if we were to not pass through a velocity. Let's see if we are passing through a velocity. We will go to index.js and find where we're instantiating the aosum. Yes, we're passing through a velocity, but it doesn't really make sense because both of these are zero. We can just declare that the velocity by default is within the class, so I'm going to make sure that I delete that from index.js. When I do that, you can see we're moving to the left, but things are flipped, weirdly enough.

We can fix this real quick just by going into draw and making sure that when we are facing the right, that is when we actually want to flip things for our aosum. So, we will change left to right, and that's going to fix the direction in which our aosum is moving. This is pretty cool!

=> 01:58:13

Sometimes, the key to progress is simplifying the complexity and letting the code flow naturally.

The AOSM is currently moving to the left, which is the default behavior if we do not pass through a velocity. To investigate this further, we can go to index.js and find where we are instantiating the AOSM. Indeed, we are passing through a velocity, but it doesn't make sense because both of these values are zero. Therefore, I will declare that the velocity, by default, is within the class. I will ensure that I delete that from index.js.

Once I do that, we can observe that the AOSM is moving to the left; however, things appear to be flipped. We can quickly fix this by going into the draw function and ensuring that when we are facing right, that is when we actually want to flip things for our AOSM. So, I will change "left" to "right," which will correct the direction in which our AOSM is moving.

This is pretty cool, but eventually, we need our AOSM to turn around. We can see that our collision detection code works here, which is quite impressive for having some sort of AI movement within the AOSM. However, I want to set a specific distance for reversing our AOSM. To achieve this, I will add a new property within our constructor called this.distanceTraveled. I believe that is one "L," but I will choose one "L" in this case. By default, we will set distanceTraveled to zero.

As we begin to call the updateHorizontalPosition function, we will add to this new property, this.distanceTraveled. Our distance will be equal to this.velocity.x * deltaTime, so we will be continuously adding to distanceTraveled. If a certain condition occurs, specifically if Math.abs(this.distanceTraveled) is greater than 100, then I want to reverse our velocity on the x-axis. I will call this velocityX, but as you can see, we are calling the negative version, effectively swapping the direction of our velocity so that our AOSM starts moving in the opposite direction.

When this happens, I want to reset this.distanceTraveled back to zero. Thus, if we travel 100 pixels to the left, we will reverse our velocity and reset distanceTraveled to zero. Similarly, when we travel 100 pixels to the right, we will reverse the direction again. This will create a cool effect with our AOSM moving left and right.

After saving and refreshing, we can see that it is moving to the left, which is quite cool. After 100 pixels, it turns around. However, this value for distanceTraveled in which we turn around should actually be something passed through during instantiation. This is because the distanceTraveled might differ depending on where our other AOSMs are located, especially if we decide to add more into our game later on. Therefore, I will replace the 100 value with this.turningDistance.

To implement this, I need to go up to the constructor and add in the new property, this.turningDistance. This will represent the distance we need to travel in order to turn. I want to pass that through as an argument within the constructor, so I will add in turningDistance, which by default will be equal to 100. Now that this is available to us, I can set the property turningDistance equal to what we are passing through as an argument. This allows us to change this value to something larger or smaller later on, providing us with more flexibility, which is quite beneficial.

Next, I want to move our AOSM over to where the rendered AOSM is from M Mate. This is easy to do; all we need to do is go to where we are instantiating our AOSM and adjust the x value to the right. I will increase it to 550, then go back to our game and refresh. It seems that 650 is a bit better in this case, and that appears to be right about where we want the AOSM to start. They will be traveling back and forth within that distance, which looks perfect to me.

Now, let's start adding some sort of collision detection so that we can begin jumping off the top of our AOSM. Eventually, we will add health and ensure that we are checking for collision detection, reacting in specific ways based on where we collide with the AOSM. For now, let's just add some simple collision detection. I want to go over to utils.py and return a value based on the condition under which these two objects interact.

=> 02:02:29

Collision detection is the key to creating dynamic gameplay; it’s all about how your player interacts with the environment.

To begin, let's conduct a quick refresh on our game mechanics. We are aiming for a distance of approximately 650, which seems to be a better fit in this case. This distance is right about where we wanted the osum to start. The characters will be traveling back and forth between that distance, which looks perfect to me.

Next, we should start adding in some collision detection so that we can begin jumping off the top of our osum. Eventually, we will also incorporate health checks and ensure that we are reacting in specific ways based on where we are colliding with the osum. For now, let's focus on implementing some simple collision detection.

I want to go over to utils.py and return a value that indicates the condition under which these two objects are colliding with each other. In this scenario, let's define square one as our player and the green square as the osum. To check for a collision between these two, the first step is to get the right side of our player. We can achieve this by grabbing object one and obtaining its x-coordinate. The x-coordinate starts on the left, but to find the right side of our player, we can add our player’s width, which we can do by using object one.width.

Now, we can say that if this value is greater than or equal to the left side of our osum (the green box), then we know we are colliding in that direction. For this, we will use object two.x. However, we also need to check for all sides of our boxes. Currently, we have only checked the right side of our player; we must also check the left side. Therefore, we will add to this conditional an additional statement that says if the left side of our player (object one.x) is less than or equal to the right side of our osum (object two.x + object two.width), then we know that the two are colliding from the left side of the player.

Make sure to wrap this in parentheses when adding the and statement. Now, we need to check for a collision on the top side of our player. To do this, we can reference object one.y. We will say that if object one.y (the top of our player) is less than or equal to the bottom of our osum, we can get the bottom with object two.y + object two.height. If this condition is met, we know the two are colliding from the top side of our player.

Next, we need to do the same for the bottom side of our player. To find out if we are colliding with the osum from the bottom, we can get object one.y and add object one.height. We will check if this value is greater than or equal to object two.y. If so, we know they are colliding from the bottom side of our player.

Assuming we have written this correctly, we can now begin to use it within index.js, specifically within our animation loop. Right beneath where we are updating our player and osum values, we will check for collisions using the function we just created. If our player is colliding with the osum, we will log out a message that says "colliding."

After refreshing, we should inspect the element and navigate to our console. When I touch the osum, I will change the location of my console to the bottom. You can see that we are only logging this out when we are touching the osum, confirming that our collision detection code is working, which is great. However, we want to react to this differently. Instead of logging out some trivial code, I will set our player’s velocity on the y-axis to a large negative value, something like 200. This means that when we touch the osum, ideally from the top, we will be launched upwards, creating a jump effect.

While this isn't our final approach, it will be fun to start with for this portion of the video. As you can see, after adding that in, our collision detection code works correctly, and we are now jumping off the top of our osum. However, this is relative to our render box, not our osum hitbox. To fix this, we need to go to utils.js and ensure that we are referencing our hitbox instead of our render box with x, y, width, and height.

We will add in hitbox wherever necessary. I might even fast forward this part of the video because it can be somewhat tedious to watch me paste hitbox everywhere.

=> 02:06:37

Jumping into game development means embracing the details—like ensuring your collision detection is spot on for a smoother experience.

In this segment, we are going to create some sort of jump effect. While this isn't what we're going to do in the long run, I think it'll be fun to start off with, at least for this portion of the video. You can see that when I add this effect, our Collision detection code works correctly. We are now jumping off the top of our aasum, but this is relative to our render box, not our a possum hit box. Therefore, we definitely want to make sure we're taking that into account.

To fix this, we just need to go to u.JS and ensure that we're not referencing our render box with X, Y, width, and height, but rather our hitbox instead. So, we'll add in hitbox here, pretty much everywhere. I don't know, maybe I'll even fast forward this part of the video because it's kind of boring watching me paste hitbox everywhere. Or maybe I'll make you watch this as I talk endlessly and ramble with the Bramble bushes.

After saving that with hitbox updated in every single location for object one and object two, we know both of them use hitboxes anyway. Now everything should still work, but we're going to have more accurate Collision detection for jumping. You can see now we're bouncing off the blue box rather than just the red one. If we want to be super cool here, we can comment out the debug boxes for our aasum in both of those locations.

Going back to our game, now we're going to get an even cooler effect that we can see better. So cool! Now we're jumping off of our enemy right here. We're going to create that jump even if we run to the side, but we're going to fix that very shortly as we begin to add in health for our player.

Next up, when we jump on our enemy, I want to remove them from the scene and also create some sort of Animation to show that they disappeared. Luckily for us, within this asset pack, we have some sort of disappear animation. So let's go over to our asset pack within Finder. Here it is! I'm going to go inside of the assets folder, then into packs, and back inside of Sunnyland. If we check the Sprite sheets right here, you're going to notice that we have an animation called enemy dead. I think this is misspelled, but it doesn't really matter. You can see what's going on here; we have this little explosion effect occurring when an enemy disappears.

That's what we're going to integrate now. To do that, we want to copy the sprite sheet, just like we did for all of our others. I'm going to do that by pressing command C, going over to our project folder, and then I want to paste that in. I'm going to change the name here just so it's spelled correctly and call that enemy death. Now, within Sublime Text, we can begin integrating this, and you should see enemy death.png within your images folder.

So, how would we begin integrating this? I think I want to make a new class specifically just for a sprite animation. I know I said earlier that I wasn't going to make a Sprite class, but here I am about to make one because this is a little more generic for what we're about to do, and we might use this for some other sprites as well. I want something rendered out on the screen, and I want it animated; therefore, I'm going to create that Sprite class.

I'll create a new file, save this, and call it sprite.js, putting it within our classes folder. Now that we have it available to us, I want to base this off of another sprite. I actually think our aosome.js file will be the best one, so I'm going to copy everything within here, go over to sprite.js, and make sure that I save that.

We are going to edit this quite a bit. This generic Sprite class is just going to animate one thing in one particular spot. I don't really want it to have any gravity or updating movement; this should really just be a sprite that stays in one location but animates. Therefore, I want to get rid of these consts at the top. I want to change the class to Sprite. Since I got rid of those consts, I want to make sure that I get rid of them here as well.

I'll just say velocity is equal to zero, although I don't really want velocity on this anyway, so let's get rid of velocity and turning distance. We also want to make sure that we take out anything related to velocity and turning distance. Therefore, I'm going to get rid of turning distance, distance traveled, and we don't need a hitbox for the sprite.

What else do we have here? I know I got rid of velocity; we don't need is on ground. I think I'll keep everything else for the time being, but remember, since we got rid of those consts and also a few of those properties, we need to make sure that we're removing these from our other files as well.

=> 02:10:35

Refactoring your code means cleaning out the unnecessary clutter to focus on what truly matters. Simplify, streamline, and watch your project thrive.

To get rid of these conss at the top, I want to change the class to Sprite. Since I got rid of those conss, I want to ensure that I remove them here as well. I'll just say velocity is equal to zero, although I don't really want velocity on this anyway, so let's get rid of velocity and turning distance. We also want to make sure that we take out anything related to velocity and turning distance. Therefore, I'm going to get rid of turning distance and distance traveled. We don't need a hitbox for the Sprite, and what else do we have here? I know I got rid of velocity, and we don't need is on ground. I think I'll keep everything else for the time being.

However, remember that since we got rid of those conss and a few of those properties, we need to ensure that we're removing these from our other methods. So, what methods do we need to get rid of? Well, anything related to our hitbox. We don't need gravity or anything related to updating our horizontal position or checking for collisions. We don't need any of this; we just want to render this out with the current frame, lapse time, and so forth to get that sprite animation. Therefore, I'm going to get rid of Jump, update horizontal position, and pretty much all of our methods, as they are not needed for this Sprite class.

I think that is a good refactor; we're about to find out. Let's pull this into our project by going over to index.html. I'm going to copy the last script tag and make sure that I'm pulling in sprite.js. Going to our project, hopefully, there are no errors pulling that in. I'm going to inspect the element, go over to our console, refresh, and we seem to be good to go.

Now, let's start implementing this to also ensure that our refactor was correct and that we have no errors in the process. To do that, I'm going to go to index.js and find where we created a const of a possum. I want to ensure that I'm creating this in a similar spot here, so I'm going to create a const, but I'm going to call this Sprites. This is going to be equal to an array because we could have multiple Sprites being rendered out on our scene at once. Later, we might do the same thing with our aosm and create an aosm array so that we can render out multiple at a time. But for the time being, let's just focus on the Sprite.

I want one explosion Sprite to start. How do I do that? Well, I'm going to create a new Sprite. What arguments does a Sprite class take? Let's look within sprite.js. We have an X, we have a Y, and we have a size. Well, Sprites might have a different width and height, so I don't really want to use size here. I actually want to pull through a width and a height separately to ensure that we're setting our properties accordingly. I think that's going to be it for now; we'll just use the aosm to render out this one Sprite.

So, X, Y, width, and height—let's add that in. What's our current X position? I don't know; let's just shove it off to the right somewhere like 300. Y will be something like 100. Our width for the Sprite will be 32, and then height will be 32. We're going to change all these later, but we just want these rendered out so we have an array full of Sprites. We want these rendered out at the same time, regardless of how many are within the array.

To do that, we need to go into our animate loop, and I'm going to render these out right beneath where we're rendering out our aosm. Well, I guess this isn't rendering out our aosm, but where we're updating our aosm data. We're first going to update our Sprite data for all of our Sprites within the Sprites array. One way we could do this is by calling Sprites.forEach, looping through every Sprite within that array, and then calling something like sprite.update.

However, the reason I'm not going to do things this way is that we are going to be removing Sprites from this array over time. If we loop from the beginning of the array to the end and remove something while we're looping through, we're going to get some sort of render flash on the screen, which we don't really want. The way to fix this is to loop from the back of an array. I know it sounds kind of weird, but hopefully, I can explain it as we go.

To loop through an array backwards, we're going to use a for loop. For our first little portion of the for loop, we are starting at the back of an array. So, what value represents the back of an array for our Sprites array? We can get that by saying i is going to be equal to our Sprites array length minus one. Currently, we only have one item within our Sprites array, so we subtract one to make sure that we get an index of zero. Therefore, we're only going to be looping through one iteration to start.

=> 02:14:29

Looping through arrays backwards can eliminate unwanted render flashes and make your animations smoother.

To improve the rendering of sprites in our game, we need to address the issue of flashing on the screen that occurs when looping from the beginning of the array to the end. Instead, we will loop from the back of an array. This may sound a bit unconventional, but I will explain it as we proceed.

To loop through an array backwards, we will use a for loop. In the first part of the loop, we will start at the back of the array. The value that represents the back of our Sprites array can be determined by setting I equal to our Sprites array length minus one. Currently, we only have one item in our Sprites array, so we subtract one to ensure we get an index of zero. Thus, we will only loop through one iteration initially, where I is equal to zero.

Next, we need to determine how many times we want to loop through this array. We will continue looping as long as I is greater than or equal to zero. For each iteration, we will subtract one from I. This is a standard way to loop through the back of an array and grab a specific sprite. We can create a constant called Sprite to represent one sprite, and then we will access our Sprites array using the current index we are looping over.

Since we are starting from the end and only have one sprite, I is equal to zero, allowing us to grab that one sprite and store it in Sprite. Once we have this sprite, we can call its update method, which is defined in sprite.js. This method takes Delta time as a parameter, and while we are not using Collision blocks anymore, we can remove that and only pass in Delta time for our animation.

After updating our sprite's values, we also need to render it on the scene. I prefer to do this later in the animation loop, specifically right beneath a pm.draw. Since all of our sprites are within an array, we need to ensure we loop through it again. We can copy the loop we used earlier and paste it beneath a pm.draw. Instead of calling sprite.update, we will call sprite.draw and pass through the canvas context C instead of Delta time.

If we have done everything correctly, we should see an AASUM rendered out on the screen at the coordinates 300, 100, and it should be moving. Let's check how things look by refreshing the game. However, I encountered an error with sprite.determineDirection. Since we are not using that functionality right now, I will remove it completely from our Sprite class, along with anything related to facing.

After saving these changes and trying again, we can see our AUM rendered out on the screen in a specific position. However, we want this to be an explosion sprite, and we also want the class to be dynamic. This way, if we have another animation we want to insert into our game, we can easily use the same Sprite class to animate it.

To make our Sprite class dynamic, we need to add an argument to reference our image source. Currently, this is hardcoded, but it would be more beneficial for it to be dynamic. Therefore, within our constructor, I will add a parameter for image source and set our HTML image source property accordingly. I will replace the hardcoded string with image source.

Now, we need to ensure that we pass through a source when creating our new Sprite. I will add this when we instantiate our Sprite, referencing the image located in our images folder, specifically enemyDasDeath.png. With these changes, we should see a different animation. Although I suspect the coordinates may not be correct for this animation, we will find out shortly. After going back to our game, we should now see some sort of explosion animation. However, we need to ensure that all of our Sprite values are correct to achieve the desired appearance.

=> 02:18:30

Transform your code to be dynamic and responsive; it's all about adapting to change for better results.

To pass through an image source, we are going to set our HTML image source property right here. I will replace that hardcoded string with the image source. Now, we need to ensure that we're passing through a source when creating our new Sprite. Therefore, I will add that when we instantiate our Sprite right here.

What image do I want to reference? I want to go inside of our images folder and reference enemy Das death.png. Now, we should see a different animation. I don't think these coordinates are correct for this animation, but we'll see very shortly. Going back to our game, we now have some sort of explosion animation. It’s in the works, but we need to ensure that all of our Sprite values are correct to achieve a good look here.

To do that, I will refactor our Sprite class a bit more. Really, I only want our Sprites to have one sort of Animation associated with them, at least for now. Instead of having this Sprites array, I will get rid of that and assign our current Sprite equal to one object. This makes sense because if this is going to be a dynamic class, we know that the X, Y, width, height, and frames are all going to vary based on what Sprite sheet we're using. Therefore, this object should also be variable.

We don’t want to use static values like we're using right now; we want to reference some sort of argument. So, what should we call that argument? Maybe something like Sprite cropbox. We can then pass it through Sprite cropbox. I want to ensure that when we come back to this code, we know what value should be inside a Sprite cropbox. Typically, you would use something like TypeScript for this, but I'm trying to make this tutorial as accessible as possible. For now, we will just pass through a default value of that same object we cut out, indicating that we need an X, Y, width, height, and Frames value within Sprite cropbox.

However, we want to ensure that we're passing through the correct values when instantiating our new Sprite. Therefore, within index.js, I will pass through a new Sprite cropbox value. What do I need here? Well, these same values that we declared within the argument. I will copy these and paste them within Sprite cropbox. Now, I just need to ensure that these reference the correct values of our Sprite sheet.

Let’s see what those are. We have one, two, three, four, five, six frames; the frames value is correct, which is great. But what is the width and height of this? If you're not completely sure, you can just go back to your finder and reference the image width and height. The width is 240 and the height is 41. So for height, we know that's going to be 41, and then 240 / 6 gives us a value of 40. Now, this should render out correctly.

Let’s save, go back to our game, refresh, and yes, you can see that looks pretty darn good. What do we want to happen next? I only want this to occur once; it’s kind of silly to have this just loop over and over again. However, if our Sprite class is going to be variable, maybe there is an animation we want to loop multiple times. But really, the most important thing is getting this rendered out where our AOSM is after we jump on top of it.

So, let's start with just the position first. I will no longer create some sort of Sprite within this Sprites array by default. I will copy everything we have right here and cut it out. I want to make sure that I get rid of that explosion; we shouldn’t just have something animating by default on our screen. However, I know I want this to occur when we jump on an enemy. Where is that happening? I know that's happening within our anime Loop, specifically when we're checking for collisions for the player and the AOSM.

After we jump on an enemy and check for those collisions, we set our player's velocity in the Y direction to be a negative value, a large one, so that we back up. But what else do we want to happen? We want to remove our AOSM from the game altogether and add in a new Sprite that animates in the AOSM's place. After that Sprite animation has run once, that is when we want to remove that as well from the game.

So, let's say we're going to add in a new explosion Sprite when we jump on the enemy. How do we do that? I will grab our Sprites array and push in that same Sprite that we copied. Instead of spawning this at a position of 300, 100, I will spawn it at the position in which the AOSM is currently located. I can do that by saying if we're colliding with this AOSM, then I can get that AOSM's X value and its Y value, and say, “Hey, I want to create a new explosion Sprite at this coordinate.”

Let’s save that, go back to our game, refresh, and now we should only...

=> 02:22:50

Create dynamic game experiences by ensuring animations play once and then disappear, keeping the gameplay fresh and engaging.

In the game development process, we want to add in a new Sprite that animates in the explosion place. After that sprite animation has run once, we want to remove it from the game. For instance, let's say we are going to add in a new explosion Sprite when we jump on the enemy. How do we do that?

First, I will grab our sprites array and push in the same Sprite that we copied. However, instead of spawning this at a position of (300, 100), I will spawn it at the position where the AOSM is currently located. I can achieve this by checking if we are colliding with the AOSM. If we are, I can get the AOSM's x and y values and create a new explosion Sprite at those coordinates. After saving the changes and refreshing the game, we should only see the explosion in the same area where we jump on an AOSM.

Currently, we are spawning multiple explosions because we keep pushing new ones into the array. Eventually, we will want to ensure that these explosions disappear after one run through. To accomplish this, we will go back to our sprite.js file and add a new property called this.doIteration. This property will represent the current iteration of the animation. By default, it will be set to zero.

In the update function, we know that we have reached the beginning of an animation when this.currentFrame is equal to zero. Within this if statement, we can say that if this.currentFrame is equal to zero, we want to add one to this.iteration. This means we have gone through a full loop of our Sprite sheet. Now that we have a way to track which iteration of our animation we are on, we can return to index.js, where we are currently updating our Sprites.

On line 173, after calling sprite.update, I want to splice out a specific Sprite that has an iteration value of one, indicating that we have completed one loop of the animation. I will write a statement that says if the current Sprite we are looping over has an iteration value equal to one, we will grab our sprites array, call splice on it, and select the specific instance we are looping over to remove it from the array altogether.

Let’s see this in action. When I jump on top of our AOSM, we will now only see one iteration of our animation taking place, which is exactly what we want. However, we also need to remove our AOSM from the game entirely. I believe the best way to do this is to create an AOSM array so that we can have multiple AOSMs within our game. After they have been jumped on, we will splice them out of the game altogether.

To implement this, I will find where we are creating an AOSM, which is located on line 129. I want to create a constant called APossums (plural), which will be equal to an array. By default, I want at least one or two AOSMs within my game. Therefore, I will remove the old constant of AOSM and ensure that this new array functions the same way. However, we will need to loop through this array and call the update and draw methods on these new AOSM objects.

Next, I will find where we are currently referencing the singular AOSM in the animate function. This will be in several locations where we are updating the AOSM's position. To update the position of multiple AOSMs within the AOSM array, we need a for loop that iterates through the array. I will copy our previous for loop, paste it above, and instead of looping through our Sprites, I will loop through our new AOSM array.

To reference one singular AOSM, I can create a constant called AOSM and set it equal to our AOSM array. By referencing the index we are currently looping over, this update method should function exactly the same. However, we are not done just yet. When we are jumping on an enemy, we are checking for collisions with one AOSM, but we need to ensure that we handle multiple AOSMs effectively.

=> 02:27:04

To create dynamic gameplay, ensure your collision detection and object rendering are perfectly aligned. Adjust your hitbox size for accurate interactions and watch your game come to life.

In our earlier discussion, we addressed the updates needed for the AOSM position in a few locations. Specifically, we focused on how to update the position of multiple AOSM within an AOSM array. This can be achieved in the same way we did for the Sprites array by implementing a for loop that iterates through the end of the array.

To start, I will copy our previous for loop and paste it above. Instead of looping through our Sprites, I will loop through our new AOSM array. To reference a singular AOSM, I will create a constant called AOSM and ensure that it is equal to our AOSM array. By referencing the index we are currently looping over, this update method should function exactly the same. However, we are not done just yet.

When checking for collisions while jumping on an enemy, we are currently only checking for one AOSM. Since we have removed that singular AOSM constant, we need to check for collisions with all the AOSM within our AOSM array. Therefore, I will take everything within the if statement where we check for enemy collisions and paste it into our new for loop where we are updating all of our AOSM. Now, our collision detection should function the same, but we still have more to do.

Next, I will copy our for loop where we are looping through the end of our AOSM array. We know that we are drawing one singular AOSM down below, so we need to ensure that we are looping through our AOSM array. We will create that one singular constant so that we can call AOSM.draw, referencing our AOSM array with the singular index to get that one singular AOSM.

After making these adjustments, I will save the changes and refresh to see how everything works. It appears that everything is functioning exactly as we want it to. The exciting part is that we can now add multiple AOSM this way. If we return to the AOSM array within our code, we can see that we created one AOSM. If I duplicate this and adjust the x-value to the left a little, say to 550, and then save and refresh, we now have two AOSM in place. This is quite impressive, as we can jump on top of these and create explosions.

However, as mentioned, we want to remove these from the game after a jump occurs. To do this, we will go to where we are checking for collisions between our player and AOSM. While we are pushing in a new explosion sprite, I want to ensure that we call our AOSM array and apply splice on top of that. We will specify the index from which we want to splice out, which will be the current iteration we are looping over, as we know that is the AOSM we just jumped on. We will remove one AOSM from that current index position by adding one as the second argument.

With these changes, we should see the AOSM disappear after we jump on them. Now, I think this is pretty perfect for some sort of disappear animation. One thing I noticed is that the AOSM width and height are a little off compared to what we had in Map Remake. This discrepancy is likely due to the fact that if we check AOSM.com back within our AOSM array, I can no longer use size; instead, I need to use width and height for both instances. I will remove size from both instances, and that should function exactly the same, which it does.

To determine the actual size of our AOSM, we can look at AOSM PNG. Upon checking, we find that the height is 28 and the width is 216. Since this is a six-frame animation, if I divide 216 by 6, the width will be 36. Therefore, whenever we create a new AOSM, we want to use 36 for the width and 28 for the height to ensure correct rendering.

However, our collision detection code might still be a bit off. To investigate, I will uncomment our debug boxes in AUM JS to see what is going on. Upon doing so, I can see that our hitbox is too far beneath our render box. The hitbox is what is used for all the collision detection, so we need to ensure that our hitbox is the correct height. Currently, it is too large, which is causing issues with our collision detection.

To fix this, I will reduce the height of our hitbox accordingly.

=> 02:31:20

Fixing collision detection is key to creating a challenging game experience. Adjust your hitbox to match your render box for smoother gameplay.

In this case, the detection code might be a little off, but we are going to find out real soon. Currently, our AOSOM is stuck in the ground using these new values. However, as you can see, the width and height of these are pretty accurate to what we created in Map Remade. I like how that looks, but let's fix the collision detection code real quick over in AUM JS.

I'm going to uncomment our debug boxes to see what's going on here. If I do that, you can see that our hitbox is just too far beneath our render box. Our hitbox is what's being used for all the collision detection and so forth, so we need to ensure that our hitbox is the correct height. Right now, it is just too large, causing some issues with our collision detection. For our hitbox height, I'm going to reduce this value of 23 down to something like 19. That might have actually been a perfect value to choose from the get-go.

That should be all we need to do to fix the collision detection; we just need to make sure that the hitbox value is at the very bottom of the render box, and things should go back to normal. That looks pretty good to me, so I'm going to uncomment—or I guess recomment—the hitboxes. We'll get those out, save that, refresh, and I think that looks pretty sick. Really, the last thing I'm going to do for this chapter is just create a couple of these enemies for fun. I think I'm going to end with that.

I'll add in two more enemies and want these evenly spaced between each other. I'll say this one has an x value of 600, and this one will be 500. No, not 5,500. Save that; they're all in sequence now, so I can jump on top of these and get rid of them in order, which is pretty fun. Honestly, we're not done just yet. Next up, we are going to add in collision detection for multiple sides so that when we run into an enemy, we actually get hurt. We don't just want to disappear the enemy from the game altogether; we want to ensure that this is some sort of challenge here.

So, that's what we're going to do next. When we run into an enemy, I want our player to start losing some sort of health. I need to be able to detect in which direction our player is hitting the enemy. Right now, we're not really deciding which direction we're hitting them; we're just saying we are hitting them, and as a result, we are removing them from the game and creating this sprite explosion. However, we need something different to happen if we're hitting from the side compared to if we're hitting them from the bottom of our player.

That's what we're going to implement: when we run into an enemy, we're going to give our player a brief period of invincibility, and we're going to make sure that we're not actually removing the enemy unless we hit them from the top. How would we go about implementing this? Well, the first thing we need to do is change up our collision detection function over in utils.py. We still want to know if we are colliding with the enemy, and if we are not colliding, then we want to return null. This is going to make sense shortly.

If we're altering our function this way, we need to go inside of index.js to find out where we're checking for collisions. We know this returns a value now, so I'm going to take it out of the if statement. Right above the if statement, I'm going to say this is a constant called Collision Direction—which direction are we currently colliding with the enemy? We're still going to call the check collisions function, and it's going to return a result within Collision Direction.

We could say if a collision direction exists, then that is when we want to remove the enemy from the game. However, we also need to return what direction we're colliding in. How would we go about doing that? Well, it's a little complicated here. We know we're colliding at least after this if statement. To determine which direction we're colliding in, we need to look at a chart real quick.

We need to determine if we are overlapping more on the x-axis or the y-axis when looking at the collision between two objects. For example, if we consider a red box as player one and a blue box as an enemy, we can see down below that when we look at this example, player one is currently colliding with the enemy on the x-axis. How can we understand that?

We can determine which axis we're colliding on based on whether or not the overlap on the x-axis is less than the overlap on the y-axis. The red line represents the distance of overlap on the x-axis between two objects, while the other line represents the distance of overlap on the y-axis. Whichever overlap is shorter means we are colliding on that axis.

=> 02:35:47

Understanding collision direction is all about comparing overlaps—less overlap means that's the axis of impact.

To determine which direction we are colliding in, we need to analyze the overlap between two objects on the x-axis and the y-axis. Let's look at a chart to clarify this concept. For instance, if we consider a red box representing player one and a blue box representing an enemy, we can see that player one is currently colliding with the enemy on the x-axis.

How can we understand this? We can determine which axis we are colliding on by comparing the overlap distances. The overlap on the x-axis is represented by a red line, while the overlap on the y-axis is represented by a different measurement. Whichever overlap is shorter indicates the axis on which the collision is occurring. In this case, we observe that we are colliding on the x-axis.

However, if we examine another example, we might find that the y-axis distance of overlap is significantly less than the x-axis distance. In such a scenario, we would conclude that we are colliding from the bottom of the player to the top of the enemy. Thus, our goal is to calculate the overlap on both axes, compare the two, and identify which one has the lesser distance to determine the axis of collision.

To start calculating the overlap between the two objects, we need to determine how much overlap occurs if the right side of our player overlaps with the left side of the enemy. We can achieve this by using a constant called X overlap. This constant will be equal to a Math.min value, where we calculate the right side of our player (object one) by taking its x-coordinate and adding its width. We then subtract the left side of the enemy (object two) from this value.

Additionally, we must consider the possibility of overlapping with the enemy from the left side of our player. To accurately determine the minimum overlap, we need to compare the overlaps from both sides. This is why we use Math.min to find the minimum overlap value.

Next, we will apply the same logic to the y-axis. We can simply copy the constant we created for the x-axis, rename it to Y overlap, and replace all x-values with y-values and width with height. This will yield the y-axis overlap.

At this point, we have calculated the distances on both the x-axis and the y-axis. Our next step is to determine which of these distances is smaller, as this will indicate the direction of the collision—whether it is on the x-axis or the y-axis.

In our code, we will check which is smaller: the X overlap or the Y overlap. If the X overlap is smaller, we can then return a direction, which will either be left or right. To ensure we return the correct value, we will utilize a ternary operator.

For example, if we find that we are overlapping with the enemy on the right side of our player, we can determine the direction by comparing the x-values of both objects. If the left side of our player has a lower x-value than the left side of the enemy, we conclude that we are overlapping from the right side. Thus, we would return the value for right. Conversely, if this condition is false, we would return the value for left.

=> 02:39:58

Understanding collision detection is key to mastering game mechanics; it’s all about knowing which side you’re hitting from.

Returning to our example, let's say that we are overlapping with our enemy on the right side of our player. We already know this, but how do we really determine whether to return left or right in this situation? Well, we have an x value related to our player, which represents the left side of the player. We also have a left value associated with our enemy. If these two are overlapping, we need to identify which x value is less. In this case, it will be our player's x value.

So, we would say: if object one’s x value is less than object two’s x value, then we know that we are actually overlapping with the enemy from the right side of our player. In this scenario, we want to make sure that we are swapping left and right. If this condition is true, we want to return the first value of right; if it is false, we want to return the value of left.

Now, let's consider the opposite situation where object one’s x value is greater than object two’s x value. Here, we take object one, and since the x value is always going to be the left side of the object we are dealing with, if this x value (the left side of the red box) is greater than the x value of the blue box, we know that the red box is colliding with the blue one from the left side of the red box. Therefore, we conclude that a left collision is occurring for our red box. This is why, in this case, we are returning left.

Next, we know that if the x overlap is actually greater than the y overlap, then our player is probably colliding with our enemy from the bottom or the top side of the player. Thus, we are going to add an else statement that says we want to return something similar to what we have here. We will copy and paste that, but all we need to change in this case is x to y. If object one’s y value is less than object two’s, we need to determine from which side of object one we are colliding with object two; it will be the bottom. The only other option left will be the top.

This logic will give us the direction in which player one is currently colliding with whatever object we specify in object two. Now, let's go ahead and test this out. I will find where we have this if statement, and as we collide with some sort of enemy, I want to log our collision direction. After saving this, I will go to our game, hit inspect, open up the console, and refresh. Now, when I come in from the side, I want to see something logging out as "right." In this case, you can see right there that we collide on the right. Yes, the enemy still disappears; we will change that shortly. However, if I come from the top of an enemy, you will see that we collided with the bottom of the player, confirming that this is working correctly.

Now, we can add some conditions here to specify that we only want the enemy to disappear if we are colliding from the bottom of the player. Here, where we are changing our player's velocity, pushing in a new sprite, and taking our position out, we really only want that to happen if our collision direction is equal to bottom. So, let's wrap everything in that condition and test it out. Going back to our game, when I run into an enemy, let's see what happens.

Oh, well, it seems that there is still something occurring there. I can tell you that we are still getting some direction returned to us, indicating that the collision direction is from the bottom of the player, even if we run into the enemy. This happens because eventually, the overlap is so great on the y-axis that it just outputs that bottom value. To fix this, we could say that we only want this to occur if the collision direction is bottom and our player is not on the ground. This means we know we are jumping. If a player is running into an enemy, that is when we want to add some sort of damage to our player.

So, let's save that. Now, if I run into an enemy, you can see that I am not hopping up anymore; the enemy isn't disappearing. However, if I jump on an enemy, both of those disappear because we are monitoring for collision based on the side from which the collision is occurring. So far, everything looks good, but I want to add a little bit of invincibility to our player. Eventually, we will add hearts to this as well, but for now, let's ensure that we get the visual interaction. If we run into an enemy from the side, our player should fade out a bit and then fade back to the normal color.

To implement this, I will go over to player.js and add a new property called isInvincible. By default, this will be equal to false. However, if this property ever equals true, what do I want to happen? I want to go inside of our draw method, and where we are currently drawing out our image, I will add in a new canvas property. This is a global property, so I want to make sure it's within save and...

=> 02:44:05

Invincibility is not just a shield; it's a chance to reset and come back stronger.

Collision is based on the side from which it is occurring. So, looking good so far, but I want to add a little bit of invincibility to our player. Eventually, we will add hearts to this as well, but let's just make sure that we get the visual interaction. If we run into an enemy from the side, our player should fade out a bit and then fade back to the normal color.

To do this, I'm going to go over to player.js and add a new property called isInvincible. By default, this will be equal to false. However, if this is ever equal to true, what do I want to happen? I want to go inside of our draw method, and where we're currently drawing out our image, I want to add a new canvas property. This is a global property, so I want to make sure it's within save and restore because I only want it affecting c.drawImage. Right here, right before scale, I'm going to add in a c.globalAlpha value, which is going to be equal to any value from 0 to 1. By default, it will be equal to 1, but I can set it to another value based on whether or not we are invincible.

I can say if this property of isInvincible is equal to true, then I want our globalAlpha to be equal to 0.5. This will give the drawImage method some sort of transparency, as if the image is faded out a little bit by 0.5 opacity. Else, if we're not invincible, of course, I want the globalAlpha to be 1 to show the full amount of our image.

So, how do I set this isInvincible property equal to true? To do so, I'm going to create a new method, and I'll just do this right above draw. I'll call this method setIsInvincible. If this is called, I want to set isInvincible equal to true, and I don't want this to go back to false unless a certain amount of time has passed. I can do that with a setTimeout method, which is provided to us by JavaScript by default.

The first argument here is going to be a callback function. After a certain amount of time has occurred, what do I want to happen within this callback function? I want to take this property and set it equal to false. The second argument within setTimeout is going to be the amount of time in milliseconds that we want to pass before we call whatever is within this callback function. If I want a layer of invincibility for about 1.5 seconds, I'll make sure that this value is 1500.

Now, whenever we hit an enemy from the side, we want to call setIsInvincible. We know we're hitting an enemy from the bottom right here, but we can also say at the end of this if statement, to check if our collision direction is actually equal to the left or the right of the player. I want to add an or operator that also checks for the right side of our player.

What happens in this case? If we're running into an enemy from the left or the right, then we want to call player.setIsInvincible. Given that we did all this correctly, we'll save and refresh. Now, when we run into an enemy, we should see our player fade out a bit. So, I’m going to do that, and yes, there you go! You can see that we're setting that property of isInvincible, and we're no longer just hopping up when running into the enemy. However, if we hop on the enemy from the bottom of our player, we're not setting that property. So really, now we have a way to track whether or not our player has been damaged.

Next up, we're going to add some heart interfaces to make sure we're tracking how much health we have left in the game. We have our invincibility in place, but now I need some sort of indicator to show how many hearts we have left. When we reach zero, we want to do some sort of game over or game restart.

To begin implementing some hearts that show on our screen, I'm going to go into sprite.js to start. I'm going to copy all the code we have in here and create a new file. I will paste in all that copied code and save this as heart.js because this is going to be a bit different from the Sprite class. I only want a static animation here; I don't even know if I'd call it that—more like a static Sprite showing in this case.

Within heart.js, I'm going to change the class name here to Heart. How do I really want this class to be different from sprite.js in the first place? Well, I no longer need an update method because this is going to be a static Sprite. I think we can get away with just that. Maybe I'll delete iteration and elapsed time because we're not using those. But yeah, really, this is all we need to start. So, if I want to create a couple of hearts on...

=> 02:48:19

Creating a static sprite is all about simplifying your code and focusing on what truly matters.

To start, I will go into heart.js and copy all the code we have in there. I will then create a new file, paste in all that copied code, and save this as heart.js. This is going to be a bit different from the Sprite class; I only want a static animation here. I don't even know if I'd call it that—more like a static Sprite showing in this case.

Within heart.js, I will change the class name to Heart. Now, how do I really want this class to be different from sprite.js? First of all, I no longer need an update method because this is going to be a static Sprite. I think we can get away with just that. Maybe I'll delete iteration and elapsed time because we're not using those. But yes, really, this is all we need to start.

If I want to create a couple of hearts on the screen and get them rendered out, I would go into index.js. I want to navigate to where we're creating all of our instantiations—where we're creating our player, our aums, our Sprites, and so forth. Right beneath Sprites, I will create a new constant called hearts, which will be equal to an array. Now, I can start populating this array with that new Heart class or at least objects instantiated from that new Heart class.

So, how would I create a new heart? We can always go into heart.js to find out. Looking at our constructor, this is what it takes. I will copy all these arguments and paste them into our new heart. Now, I just need to set them. Where do I want our heart on the screen for the XY coordinates? I think somewhere up here—probably around 10 pixels from the left and 10 pixels from the top. If that's the case, I know my X should be 10, and my Y should also be 10.

What is the width and height of these going to be? I'm not sure to start, but let's just go with 32 by 32 to be safe. As for our image source, we don't have one just yet for our hearts, but we're going to get one. For now, let's just reference enemy_death.png, which is within our images folder. Let's not forget that. Regarding our Sprite crop box, I don't even remember what these values represent for the default, but it doesn't really matter. We just want to get something rendered out on the screen for our hearts in this case.

So, we have this hearts array, and we have an instantiated heart inside of it. We know if we want something rendered out on the screen, we need to loop over the array and then call its draw method. Down here, where we're drawing out our Sprites, I can duplicate one of these for loops. Instead of referencing Sprites, I'm going to reference our hearts, which will be stored within an arbitrary constant. In this case, it makes sense to call it heart because we only have one singular one. Then, I want to call heart.draw on top of that.

However, this still won't work because we need to ensure that we are importing heart.js inside of index.html. Here, I will copy our very last script, which is pulling in sprite.js, and make sure that I'm pulling in heart.js instead. We did everything correctly there, so we should see something rendered out. However, that doesn't look great; that actually is the start of our enemy death animation. As I said, it doesn't look great, but it doesn't matter. We just wanted something rendered out on the screen.

So, how do we get some sort of heart Sprite here instead? Thankfully, on Humble Pixel, the author created a really great pack for us called Health Progress Series Number Two: Hearts. This also has a great license for personal and commercial use. I downloaded this and created a very simple spreadsheet based on it. We just have an on and an off state. If you want just this spreadsheet right here with the on and off state based on Humble Pixel's asset pack, you can download that with a link in the description of this video.

Just make sure you download that, and it's going to be available as hearts.png. We want to copy this with Command+C and paste it within our images folder in our project. As long as we do that, we can see within Sublime Text that we now have hearts.png available to us. So, rather than using enemy_death.png when we're instantiating a new heart, we are going to use this new hearts.png spreadsheet instead.

Let's save that and refresh to see what we get on the screen. Yes, that's looking pretty good, but we just need to make sure that our dimensions are correct based on the Sprite sheet and what we're referencing right here for width, height, and Sprite prop box width and height. So, what are the dimensions of hearts.png? Well, we can always look within Finder. It looks like it's going to be 42 by 18. In that case, our height is going to be 18 in both locations, and then our width is going to be 21 because the overall spreadsheet width was 42.

=> 02:52:14

When coding, the smallest adjustments can lead to the biggest visual improvements.

In this section, we can see that within Sublime Text, we now have hearts.PNG available to us. Rather than using enemy death.png when instantiating a new heart, we are going to utilize this new hearts.PNG spreadsheet instead. After saving that and refreshing, we can see that it looks pretty good, but we need to ensure that our dimensions are correct based on the sprite sheet and what we are referencing for width and height.

So, what are the dimensions of hearts.PNG? We can check within Finder, and it appears to be 42 by 18. Therefore, our height will be 18 in both locations, while our width will be 21, since the overall sprite sheet width is 42. Dividing that by the number of frames gives us a value of 21. Let's change our width and height in those two locations, refresh, and now we have one singular heart rendered on the screen, which is exactly what we want.

However, we also want two more hearts rendered on the screen. To add these, we can simply duplicate this instantiation and paste it down below twice. We need to change the x value for each heart. Since each heart is about 21 pixels wide, we will add that to our x value. Changing 10 to 31 is the first step, but I also want a little bit of spacing between each heart. Therefore, I will add a value of 2 to that. This means we can think of this as adding 23 to each x coordinate. If we take the value of 33 and add 23, we get 56. This should give us some evenly spaced hearts on our screen. After saving and refreshing, there we go!

Next, we need a way to turn off the very last heart, but only if we run into an enemy and activate this invincibility mode. To achieve this, we can go inside hearts.js and create a new property for our heart called depleted. By default, this will be set to false. If it is depleted, we need to determine what should happen. Looking at hearts.PNG, we know we are rendering out the very first frame. However, if the heart is depleted, we want to render out the second frame. To do this, we can change the value of current frame from 0 to 1. This will push the crop box over to the side, as we are using current frame to draw out our image. Depending on this value, we will move our crop box around for whatever image we are dealing with.

Within our draw method, right beneath let x, I will say that if this heart is depleted, then I want current frame to equal 1. We can check this by setting depleted equal to true within our constructor. If everything goes according to plan, we should see all these hearts turned off. And there we go! However, by default, a heart should not be off for this game, so I will ensure that it is equal to false. After saving and refreshing, we are on the right track.

Now, we need some sort of method to only turn off the last heart as we get hit. We can begin implementing this by going to index.js and finding where we are running into an enemy. This is happening within our animate function, where we are colliding with the player and the enemy. Right here, when we set our player to invincible, I want to grab the very last heart within our hearts array, specifically the last full heart, and set its depleted value to true.

First, we need to grab all the full hearts within our hearts array. We can do this by creating a const called fullHearts, grabbing our main hearts array, which could be depleted or non-depleted hearts. By calling filter on this, we can use a callback function. This will be a basic arrow function, and there will be an argument passed through this arrow function by default, which will be whatever value we are currently looping over within the hearts array.

Think of filter as looping over each individual heart within the array, and we can return a condition to say whether or not we want to return it to a new array stored within fullHearts. The first argument in the callback function will be an arbitrary value, which I will call heart, as we loop over each individual heart within the hearts array. The filter function requires a return statement to specify under which condition we should return a heart. If the heart we are looping over has a depleted value equal to false, indicated by the bang operator, then we...

=> 02:56:16

Filter out the noise and focus on what truly matters—keep your hearts full and your game strong.

In this section, we are discussing the implementation of an Arrow function that processes each value in the hearts array. Think of filter as simply looping over each individual heart within the array. We can then return a condition to determine whether or not we want to include a heart in a new array, which we will store in Full Hearts.

The first argument in the Callback function is an arbitrary value, which I will call heart, as we are looping over each individual heart within the hearts array. The filter function requires a return statement to specify the condition under which we want to return a heart. If the heart we are looping over has a depleted value equal to false, indicated by the bang operator, then we know it hasn't been depleted, and we want to store it within the Full Hearts array.

Next, we need to determine under what conditions we want to set a full heart to be equal to depleted. We will grab the very last heart by referencing the array's index, specifically full hearts.length minus one, and check its depleted value. We will set it equal to false, but only under certain conditions. If the player is not Invincible, we want to set this value to false. If the player is invincible, the hearts will deplete too quickly, so we need to ensure there is some space between when we set the depleted property to false for the last heart.

Additionally, we need to consider the case where Full Hearts has a length of zero. In this scenario, we won't be able to access anything to set its depleted property to false. Therefore, I will add an and statement that checks if full hearts.length is greater than zero. If this condition is met, we will grab the last heart and set its depleted value to false.

Moreover, we need to ensure that we set the depleted property to true when a heart becomes depleted, as we are effectively losing that heart. Now, when we refresh and run into an enemy, you will see that the first heart depletes. If I do it again, the second heart depletes, and the third one will only deplete after the invincibility wears off.

You might have noticed that as I begin to move, we lose track of our hearts because they are currently being translated with the rest of our scene. To fix this, we will move the for Loop that renders our hearts outside of the save and restore section we had implemented earlier. When we call c.save, we create a subsection where we can call global canvas methods. The c.save is happening right before we scale things up and translate along with our camera.

To correct this, I will paste the for Loop below c.restore. After refreshing, our hearts appear smaller because we are no longer calling the scale on top of them. We still want to scale the hearts so they are visible, but we do not want the translate method affecting them. A simple solution is to copy the c.save and c.scale and wrap our for Loop within c.save and c.restore. This way, we are calling those global canvas methods and only affecting what is within c.save and c.restore.

Now, our hearts look good again. If we start moving to the right, you will see that the hearts follow us because they are no longer affected by c.translate, which is exactly what we want. However, when we hit zero hearts, I want to implement a basic restart. To achieve this, we need to reset all the values that we are currently only setting on load, such as our hearts, our score, and our player.

To facilitate this reset, I will create a new function called init right above our animate function. This function will stand for initialize and will be called whenever we have a game over to ensure everything is reset. We need to reset pretty much anything that determines where our enemies are spawned, how many enemies are spawned, and how many...

=> 03:00:14

Resetting is the key to a fresh start; when the game ends, make sure everything goes back to square one.

When we hit zero hearts, I kind of want to implement some sort of basic restart. To do this, it's important to ensure that we're resetting all the values that we currently only set on load. This includes things like our hearts, our possum, and our player. We want to set these back to their original values after our last heart has been depleted.

To achieve this, I think the easiest way is to create an init function. So, right above our animate function, I'm going to create a new one called init. This stands for initialize, and basically, we're always going to call this whenever we have some sort of game over to make sure everything is reset.

Now, what do we need to reset? Pretty much anything that determines where our enemies are spawned, how many enemies are spawned, and how many hearts we have. I'm going to take everything from our player, possum, sprites, and hearts, and paste that within init. I know I'm going to change these values later on, so I want to ensure that I'm changing their const to let for player, possum, sprites, hearts, and also the camera.

Now, I can grab everything from player down to hearts and paste all of that within init. This is just saving me some time. When I call init, I don't want to declare a new let; instead, I want to take the previously declared let that we changed from const to let. When I call init, I'm resetting it equal to the player's original position. I can do the same thing for our possum array, sprites, and hearts. Additionally, I want to make sure I reset the camera object, ensuring that we're resetting the X and Y back to zero so we're looking at the beginning of our map rather than wherever we are in the current space.

Now that we have init available to us, we can begin using it. Where do we want to use it? Well, where we're currently depleting our hearts and setting our player to be invincible, we already have a statement that says to only deplete a heart if we have one full heart left. However, if there are none left, what should we really have happen? I'm going to add an else if statement that says if full hearts.length is equal to zero, then I want to call init and basically reset everything.

Let's save that, refresh, and then get our hearts down to zero. Hopefully, we can see everything work perfectly just from that. So, we're at two, and now when I hit three, we have zero hearts and zero full hearts. We should see everything reset, and there we go! We have full hearts again, our player respawned, and we have all of our possums in new positions. We can test this out one more time just to be extra safe by running into one and then doing it again to get to zero hearts. We refreshed our game, and it worked!

This is just some basic game over restart functionality. You can definitely take this to a further level by adding in an interface and so forth, but I think for now, that is good. We're going to move on to collectibles next.

The next thing I want to do is make sure that we have collectible gems rather than just gems that are rendered out on the screen. You'll see that if I try to collect these, nothing is happening because those are part of the background. We need to take them out of the background, render them separately, and implement some sort of collision detection between them and our player so that we can collect them.

How would we go about doing that? Well, it's going to start over within maper mate. So, go back to your map, and when you're here, you want to ensure that wherever you have your gems placed, the only item within that layer is the gems. I realized that I messed this up; if I look at my layers, I actually included the gems within the shrooms layer, which is meant for mushrooms. I'm not really sure why I put the gems here.

In this case, I want to erase all the gems from this layer. I'm going to cck on the layer and then click the Eraser tool to erase all the locations where I have gems placed. Now that the gems are erased, I can add a new layer and rename it to gems. I can begin placing new gems. The gem is right here within the decorations tile set. I'm going to select one gem, make sure my brush tool is selected, and then redraw out those gems in the locations.

=> 03:04:20

Mistakes are just stepping stones to better organization; separate your layers for clarity and control.

In this tutorial, we will focus on organizing our layers effectively. Your gems placed should be the only item within that layer, but I realized that I messed this up. If I look at my layers, I actually included the gems within the shrooms layer, which is meant for mushrooms. I'm not really sure why I put the gems here, so in this case, I want to erase all the gems from this layer.

To do this, I'm going to cck on the layer and then click the Eraser tool to erase all the locations where I have placed gems. Now that the gems are erased, I can add a new layer. I will rename this layer to gems and begin placing new gems. The gem is located within the decorations tile set, so I will select one gem, ensure my brush tool is selected, and then redraw those gems in the locations I intended. I had one grouping up here and another grouping over there.

Once we have our gems in their own separate layers, we need to grab the data from this one layer. In the future, I may implement a right-click feature that allows you to export data on a layer-by-layer basis, but for now, a safe bet is to go to export, hit base platformer, and then open up the new zip file. If we look inside this zip file, specifically in the data folder, we will find a new file called Lore gems. This is exactly what we want.

I will copy this file and paste it into our project. I am currently within Sunnyland, so I want to go inside my data folder and paste it there. If we return to our code within Sublime Text, we can see that the file lore gems.js contains all the data related to our gem objects that we just placed. However, to use this within our code, we need to ensure that we are importing it into index.html.

To do this, we can copy one of the script tags that were generated for us during our initial export with mapper mate. Since we are doing an extra import/export with an additional layer, we need to copy one of the current script tags and import the new data layer separately. This will be called Lore gems with a capital G. Now we should be able to use this within our game, specifically the const of lore gems.

Looking at this data, we see tons of zeros, but eventually, we hit a row with 18. This indicates the locations of the gems we had over in mapper mate. This is just a data representation of what we built out in mapper mate, and we will use this to render our gems in the exact locations we want them on the screen. With lore gems pulled in, we can now begin using that in index.js.

To start using this, some of the code I had scaffolded with mapper mate is the Collision section. This section looks for any layers that have collisions within them, and if collisions are found, it automatically places collision blocks on the screen. This allows for collision detection between our player and platforms, preventing the player from moving past all the tiles laid out on the screen.

I want to reuse the code we have here but specifically for our gems. To do that, I will copy where we are looping over all of our collisions and place this within our init function, which we created earlier. I will do this as the very first thing within init. However, instead of looping over my collisions, I want to loop over lore gems.

In lore gems.js, I will loop through this 2D array, which contains arrays, and within those arrays, we have a bunch of zeros and other symbols. I want to select lore gems and for each row within this data object, I will loop through each array. Each of these sub-arrays will have a symbol, which can be any character from 0 to infinity. In this case, we are dealing with zeros and 18s.

If our symbol is 18, we know we want a gem in this location. Currently, since we copied and pasted this code, we are referencing collision blocks, but we actually want to use a gems array and push in a new gem. I think I will push in a new sprite, reusing one of the classes we created earlier. For now, I can delete all the arguments and the unnecessary else-if statement. This is the main code we need to implement.

=> 03:08:36

To create something amazing, you need to break it down into manageable pieces and build it step by step.

I want to loop through each array because I know there are tons of arrays within this one larger array. Each of these arrays, or sub-arrays, is going to have a symbol, which is just any character from 0 to infinity. In this case, we are just dealing with zeros and 18s, so we know our symbols are either going to be zero or 18.

I want to say if our symbol is 18, we know we want a gem in this location. Right now, since we copied and pasted this code, we are referencing Collision blocks, but we actually want to use a gems array and push in a new gem. In this case, or actually, I think I'm going to push in a new Sprite. We will reuse one of the classes that we created earlier. Therefore, I’m going to delete all the arguments for now and also delete the else if that is not needed. This is the main code that we need.

However, if we're trying to push into a gems array, we need to make sure that the gems array exists. Right now, it does not, so I'm going to create a new let called gems and set that equal to an empty array. Whenever I call a nit, I want to make sure that I'm setting gems equal to a new empty array. We will repopulate it with this code right here.

Next, we need to determine how we can push some sort of sprite into this gems array. What arguments does a Sprite instantiation take? Well, let's find out where we're creating a new Sprite. I'll do a command F to find new Sprite. This is an instance in which we're pushing in an enemy death Sprite. I want to copy this, go back to the code where we're trying to push in a new gem, and I'm going to delete the old new Sprite and paste in the new one just to save us some time on what these arguments are going to be.

So, what should the x coordinate be for this new gem Sprite we are trying to create? We don't have aosm dox available to us, nor do we want to place a gem wherever an aosm is. We actually want to place a gem at the x coordinate in which we're currently looping over. This is available to us right here; this is an X index of whatever row we're currently looping over.

You can think of it as we're looping over this one sub-array right here. What is the x value of the 18 in which we're looping over? Well, there are no 18s right here, but if I were to add one in, it's saying we want to go 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 x coordinates over from the very left-hand side of the screen until we reach the very end, which is the far right-hand side of our map. You kind of get the idea here, so we're selecting a value of 13.

However, 13 for the x coordinate means we're only going over to the right by 13 pixels. We need to remember that each of these data points right here is actually 16 pixels wide because that is the tile size of our map. Each tile within mapper mate is actually 16 pixels wide, so we need to take the coordinate location and multiply it by 16 to get accurate values when we are pushing in a new Sprite.

I'm going to revert this back to zero, and then within index.js, I want to make sure I'm referencing the X index and multiplying that by 16. We will do the same thing for the y-AIS; we have a y index available to us, and we're going to reference that and multiply that by 16 to get the correct y-coordinate for placing our gem.

Now, what is the width and height of our gem? I want to say it is around 16, but if we don't know, we can always go back to our finder and then look within our images. Actually, it looks like we don't have a gems Sprite available to us. Thankfully, Sunnyland provides one; at least the asset pack does. I'm going to go inside of that asset pack. If you forgot what it's called, it is Sunnyland something files.

This is the one we downloaded near the beginning of the course. We just want to open that up, go inside assets, then packs, then Sunnyland, and then there's going to be a spreadsheets folder. We want to grab gem.png. This is going to be our spreadsheet; it will provide a nice animation and everything. We don't want to grab just this, though; we also want to grab item feedback so that when we collect a gem, we can create some sort of nice animation as well.

So, I'm going to select both of these by pressing down on shift, clicking the other item in which I want, and hitting command C. Then, within my images folder, I'm going to hit command V to make sure that our Gem and item feedback are within our project. I wanted to look at gem.png. How big is a gem? I know it is 13 pixels high, but how wide is this? Right now, it says 75, but I have five Sprites inside of here.

What is 75 divided by 5? We have an official gem size of 15 by 13 in this case. So, back within our code, I want our width to be 15 and our height to be 13. Now, what Sprite sheet am I trying to reference right here? We made sure that we pulled in the gem Sprite sheet into our project. Here it is; we are going inside of our...

=> 03:13:08

Transform your project by ensuring every detail counts; from perfecting gem sizes to flawless animations, every step leads to a polished outcome.

To enhance our project, we need to grab item feedback so that when we collect a gem, we can create some sort of nice animation. To start, I will select both the gem and item feedback by pressing down on Shift and clicking the other item. Then, I will hit Command C to copy them, and within my images folder, I will hit Command V to ensure that our Gem and item feedback are within our project.

Next, I want to look at gem.png. I know it is 13 pixels high, but I need to determine its width. Currently, it says 75, but I have five Sprites inside of this file. To find the official gem size, I calculate 75 / 5, which gives us an official gem size of 15 by 13. Now, back in our code, I will set the width to 15 and the height to 13.

I need to reference the correct Sprite sheet for the gem. We made sure that we pulled in the gem Sprite sheet into our project, and here it is. We are going inside our images folder to reference gem.png. The Sprite crop box should match the width and height we referenced above, so I will enter 15 for width and 13 for height. The Sprite sheet contains five frames, so I will input that as well.

Now, we need to ensure that we are calling update within each of the Sprites in the gems array. Not only do we need to call update, but we also want to ensure that we are calling draw so that we can render our gems on the screen. We can do this within our animation loop. We know we are calling update for a bunch of our Sprites right here on line 396. I will copy this for loop for our Sprites, paste it below, and change it to loop through our gems instead.

In every instance where we had "Sprites" (plural), I will change that to gems, and in every instance where we have "Sprite" (singular), I will change that to gem. We do not need the code that removes a Sprite if its iteration is one because gems do not function like that for their animations. We want to ensure that we are updating our gems' frames over time to achieve a nice animation.

Next, I need to ensure that the gems are rendered on the screen. All of our render code is located below, so I will copy the entire for loop that draws our Sprites, paste it down below, and reference our gems array instead. I will loop over all our gems, grab one singular gem, and call its draw method.

This all looks good, but we must remember that our gem creation instantiation code is called within our init function at the top. Currently, we are only calling init when we restart our game, and there is no other location where we are calling that. To fix this, we want to ensure that init is also called during the unload process. A simple way to do this is to call init right before we start rendering when we load this entire file. This will ensure that our new gem objects are placed on the screen when our file loads.

After saving with init in place, I will refresh the game to see if we can get our gems rendered on the screen. Upon refreshing, there they are! We have a nice animation in place, and the gems are now separate from our background, which means we can call some collision detection code on them and begin collecting them.

However, there is an issue. If I zoom in, I can see that the background gems are still being rendered on the screen, and I definitely want to get rid of those as they are not needed. I need to find the layer where I placed those gems within Mapper Mate prior and change their value from 18 to 0. When I exported this originally, my data layers were not named, which is not ideal. I believe layer six was where I had the shrooms and gems within the same layer.

Since I have a bunch of 18s, I know that 18 is related to the gem Sprite. I can select all the 18s within this file, delete them, and replace them with 0. After saving that and going back to our game, I will see that the background gems are no longer rendered, and we are only dealing with the foreground gems, which we can now collect.

To begin collecting and removing these gems, we have set ourselves up nicely by looping over all our gems and placing them within a separate array. Now, where we are calling gem.update, we can start checking for collision between our player and all the gems we are looping through.

=> 03:17:21

When coding, every mistake is just a stepping stone to a better solution. Embrace the errors, fix the hitboxes, and watch your game come to life.

In this discussion, I reflect on my experience with layer six, which, as I mentioned, is obviously not great. However, I believe it was significant because it was the layer in which I had the shrooms and the gems within the same layer. In this case, I have a bunch of 18s, which I know are related to the gem Sprite. Therefore, I can select all the 18s within this file, delete them, and replace them with zero. After saving that, if I go back to our game, I will see that those background gems are no longer rendered out, and we are only dealing with the foreground gems, which we can now collect.

To begin collecting and removing these gems, we set ourselves up nicely by looping over all our gems and placing them within a separate array. When we call gem.update, we can start checking for a collision between our player and all the gems we are looping over. We have a nice Collision detection function in place from earlier, where we were detecting collisions between the player and any possums. I will grab this check collisions code and paste it within my for loop for the gems. However, instead of detecting a collision between the player and a possum, I will detect it between the player and the current gem we are looping over. If a collision direction exists, then I know I want to remove a gem from the game.

To remove a gem from the game, I can select the gems array and call a splice on it. At what index do I want to remove one singular gem? Well, I is the index of the gem we are looping over. How many spaces from this index do I want to start removing from the array? Just one, as I only want to remove one gem. If we save this and go back to our game, we will notice an error in place. If I inspect the element and go to the console, it says cannot read properties of undefined reading x. Upon further investigation, I realize that I stated our Sprite class didn't need a hitbox, but the hitbox is required for our check collisions function to work.

I might have made an oopsie mistake, whatever you want to call it. In this case, we need to go back to our sprite.js class and ensure that it has a hitbox associated with it. I will add in this do hitbox and set it equal to something dynamic because technically, sprites can have different widths and sizes. Therefore, I will state that our hitbox is equal to a hitbox we are passing through as an argument. I will add that right here within our constructor. By default, the hitbox will have an x, a y, a width, and also a height. You can change these defaults to whatever you want, but I think this is okay for now. However, I do want to specify values for these when creating all of our gems.

Where are we creating a new sprite for our gems? It will be right here on line 233. Now, I can pass through a hitbox and specify the XY width and height values here, which will be the same as the XY width and height we have above. I will copy these values and paste them within our hitbox. Hopefully, now when we reload our game, we will not see an error. After refreshing, everything seems to be functioning again. Given that the rest of our code works, when we hit a gem, we should see it disappear from the screen. And there we go; we are collecting those gems very nicely.

Let's ensure that this functionality works for the gems over here too. I will walk into that area, and all the gems will disappear, which is looking great. However, I would like some sort of animation to occur as I collect these gems. We added an animation when we copied over our Sprite for gem.png. The animation will be item--feedback.png. How would I start using this, or how would I create this animation as I collect a gem?

Within index.js, right here in animate, where we are currently splicing out a gem, we want to create an item feedback animation. We know we were kind of creating this not really an item feedback animation, but an enemy death animation when we removed a possum from the game. We did that above by stating that if an aosom is removed from the game, then we are pushing in an enemy death Sprite to our sprites array. We can basically do the same thing but using a different sprite in this case. I will copy this entire chunk of code where sprites do push, and then, right before we splice out our gems, I want to paste in that same code.

Where do I want this animation to occur? Well, it should occur wherever the Gem's x coordinate currently is. As for the y-coordinate, it should be wherever the...

=> 03:21:44

Creating engaging animations enhances player experience; even small details like gem feedback can make a big difference.

In the index.js file, specifically within the animate function, we are currently splicing out a gem. Before we remove anything, we want to create an item feedback animation. Previously, we were creating an enemy death animation when we removed an aosom from the game. We implemented this by pushing an enemy death Sprite into our Sprites array whenever an aosom was removed. We can apply a similar approach for the gem, but using a different Sprite.

To do this, I will copy the entire chunk of code related to Sprites.push and paste it right before we splice out our gems. Now, we need to determine where we want this animation to occur. The x-coordinate for the animation should match the gem's current x-coordinate, and the y-coordinate should be the same as the gem's y-coordinate.

As for the width and height of this animation, we can refer back to our file to check the size of itemDasFeedback. Its width is 160 and its height is 32, with five frames in total, the fifth being empty. Therefore, we do not need to change anything for the width and height in this case. Our image source will be itemDasFeedback, which we have already pulled into our images folder. The Sprite crop box will be 32 for width and 32 for height, with five frames.

This setup is all we need, so let's save that and refresh the game. As we start collecting some gems, we can see the animation take place. However, the animation isn't centered over all our gems as I would like it to be. To fix this, we need to adjust the x and y values by some sort of offset. I will start by subtracting eight from both coordinates to see how that looks. This adjustment might actually be perfect, centering the animation over all the gems. I love how that looks, and after collecting a few more gems, I feel satisfied with the result.

The last thing I want to address regarding collectibles is to ensure that I am tracking their count next to our hearts with some sort of interface. To achieve this, I plan to render a gem Sprite in the top left corner of the screen, along with a counter that increases for every gem I collect.

To begin rendering the gem UI, I will create a new variable called gemUI above the init function. This variable will be equal to a new Sprite. Referring to the Sprite class below, I will copy all the necessary arguments and paste them into my new Sprite. For the x and y values, I will set them to static values, removing the dynamic x and y coordinates. I think starting this off at 166 for x and 16 for y will work well. Since I don't need a hitbox for this, I will remove that, but I will keep the Sprite crop box. Everything else should be fine.

Next, I will render this gem UI object on the screen. To do this, I will call draw on the gemUI object. In our animation loop, where we call gem.draw for all the gems collected, I will also call gemUI.draw. After saving and refreshing, I can see the gem being rendered on the screen, but I want to push it down a bit. I will go back to where we created gemUI and set the y-coordinate to 36.

Upon checking the game again, it looks good at 36, but I want the gem to align with the heart. To achieve this, I will adjust the x-coordinate, experimenting with different values until I find one that I like. I think 13 might be the right choice, and I am pleased with how it looks. Since I don't want this gem UI to be animated, I won't call update on this Sprite. If I were to call update, it would behave like the other gems, which is not what I want for our UI.

One thing to note is that if I start moving, the gem UI scrolls along with everything else. To fix this, I need to adjust where we render our gem UI. Currently, it's on line 495 with gemUI.draw. I will move this out of the main animation loop and place it within our UI save and restore function. This adjustment will ensure that the gem UI remains static while the rest of the game elements move.

=> 03:25:58

Iterate, adjust, and perfect your game design until it feels just right.

I'm moving X, and I'm just going to toy around with this value, going back and forth until I get something that I like. I think 13 might be the ticket, and yeah, I like the way 13 looks. So, I don't need to call update here because I don't actually want this animated. If I were to call update on this Sprite, we’d get the same effect as we're getting over here with the other gems, but I think this is totally fine for our UI.

Now, one thing here is that if I were to begin moving, you'll see that the gem scrolls along with everything else. If that is the case, we want to find out where we are rendering our gem UI. Remember, we just put that right here on line 495 with gem UI.draw. I want to take it out of the C restore and C.save and put it within our UI save and restore. This is where we're drawing all of our hearts, and we know that in this case, we should also be drawing out our gem because this is kind of where we put things that should not be scrolling on our screen.

So, if I move gem UI here, save, and refresh, and then begin scrolling, you’ll see that it follows us now, which is perfect. So, how do I get some sort of counter next to this gem? Well, let us find where we're drawing on our gy, and thankfully, we were just doing that right here. I want to call right after this C.fillText. This is a native canvas function, and within it, I can draw out some sort of text as the first argument. The second argument is going to be an X coordinate of where I want this text drawn out. I think for X, we said that our gem was at 13, so I’ll move this over to 16. Then, I think for Y, this was at a value of 36.

Just to show you what this looks like, let’s save and refresh. We are now drawing out our text at this location, so it looks like we want to bump this down and move it over to the right a bit more. We’ll increase Y by 10 and then increase X by 10 as well. Let’s see how that looks. It’s getting better; I can definitely move the X over to the side. Let’s try 36, and I’m going to decrease the Y by a couple of pixel values as well to 43. I actually liked it better around 45 or 46. Geez, what am I doing? Okay, we’re going to stay with 46, and then I’m kind of picky about this, so I’m going to go with 33 for X, and I think that looks good for our text position.

So, obviously, we don’t have 4324 gems; we only have zero right now. But we want this value to be dynamic and increase as we collect more gems. If that is the case, I can replace this with gem count, and this is a variable that doesn’t exist yet. If we want to create it, we’ll go right above init and create a let called gem count, setting it equal to zero. I always want to make sure that whenever I call init and restart our game, gem count goes back to zero. I should probably be doing the same thing for Gem UI right here. Whenever we reset our game, I’m going to give us a fresh instance of gem UI, even though in this case, it’s not really required because this is never going to change in the first place. But just to be safe with our init code, I’m going to do things this way.

So, we have our gem count at zero, and we’re rendering it out on the screen. You can see it is also zero now. We want to increase this whenever we collect a gem, and we know we’re collecting gems right here. We can even be very specific, saying this is where we are collecting gems. If we remove a gem from our game, we’re going to take our gem count and add one onto it. So, let’s save that, refresh, and now when I collect a gem, you can see we just collected nine. If I were to restart our game by getting zero hearts, you’re going to see that it resets back to zero, which is exactly what we want.

I’m going to collect all of our gems just to make sure that this works in its entirety. Let’s make sure we can see the whole screen there, and yeah, you can see that’s increasing over time, which is perfect for collecting items. So really, I think the last thing I want to do is expand our map just to give us some more playing area and make this really exploratory. I think it’ll be a lot of fun with the current kind of interactions we have in place to create something very similar to Donkey Kong Country or a 2D platformer in general.

That’s what we’ll be doing next. Everything is looking really good, but I want to expand our map to make this a more fun and playable game. I want to be able to go down, I want to go a little further to the right over here, and maybe add some gems and more enemies in the process. So, how would I go about doing that? Well, we’re going to start back over in map remate, and in order to expand our map, we want to deal with this row and call section down here. I know I want to go down on our map, so I’m going to add probably like 40 or 50 rows. Let’s extend this to 70.

=> 03:30:14

Expanding your creative boundaries can lead to the most exciting adventures in game design.

In this project, we are aiming to create a playing area that is really exploratory, which I think will be a lot of fun. With the current kind of interactions we have in place, we are looking to develop something very similar to Donkey Kong Country or a 2D platformer in general. So, that's what we'll be doing next.

Everything is looking really good, but I want to expand our map to make this a more fun and playable game. I want to be able to go down and a little further to the right, perhaps adding some gems and more enemies in the process. So, how would I go about doing that? Well, we are going to start back over in map remate. In order to expand our map, we want to deal with this row and column section down here. I know I want to go down on our map, so I’m going to add probably like 40 to 50 rows and extend this to 70. You will see that we now have a lot more room to work with, and we can always tailor this later on if we want to shrink things. But I think this is a really good start for giving me some extra boundaries in which I can start extending things.

I want to make sure that I'm sticking with the layers that I had prior. Yes, I did rename things, so when I re-export these, I will have to do a little bit of work to ensure that this lines up with what we already have developed, but it’s not going to be an issue. For this chapter, I will put this on a time lapse. It’s nothing that you haven’t learned or seen before, but I will be adding on to our current map with more tiles, specifically on this layer right here, the back tiles, which represent our general area in which we can land. I will be adding more pathways and places to step on, but I will do it on time lapse since we already know how to build all this out from the beginning of the video. After that, I will show you how to integrate this into our current project.

So, let’s begin the time lapse and start building out the rest of this map. As you can see, this is what I came up with. I added a little cavern where we can explore, and I think I will add an enemy down there as well. I programmatically added a few collectible gems and made sure to place those directly within the gems layer because we know how to deal with those now. Additionally, I added this little ending section, which has a smiley face of gems just as a complete wall blocking everything off. Ideally, I would like to have some sort of flag or item over here to indicate that this is the end of the map. When you hit the end of the map, the game should give you some sort of notice that you have reached the end and are ready to go to the next level.

This was my thought process for creating all this. I made sure that within my collisions layer, I added block collisions and platform collisions for when I want to be able to jump through something like this little block. Now, how can we go about integrating these new changes into our project? We will hit export and export as the base platformer. We will open up this ZIP file and focus on a few things here, but I think the most important is going to be this data folder. We want the contents of this to replace whatever we have within our current project.

I will copy all the files within this folder using command C, then go over to our current project. After some testing, I found the correct folder, Sunnyland. We have all these files within our data folder, and I want to get rid of all these files by moving them to the trash. Then, I will paste in the new ones that we just exported. We are on the right track here, but we are not done just yet. There are a few more things that we need to change.

Let’s start with what we have within index.js. Remember, this is the newly exported project we are dealing with. I want to open up index.js and copy everything from layers data down to tile sets because this will contain all of our new layers that we need to reference. They may have been renamed, so we need to ensure that these are all updated for our project to work with our new data. I will copy these two constants and then go over to our main project, where we have been making all these changes. I will go inside index.js and replace layers data and tile sets with the ones that I just copied. I want to save this, and we are looking good.

However, there is one more thing we need to transfer over, and at least I’m pretty sure this is the last thing. This will be within our new export, so we want to open up...

=> 03:34:21

Sometimes, the key to success is simply knowing what to remove to make room for what truly matters.

This is the newly exported project we're dealing with, so I want to open up index.js and copy everything from layers data down to tile sets. This is going to contain all of our new layers, which we need to reference. Maybe they got renamed; we need to make sure that these are all updated for our project to work with our new data.

I'm going to copy these two consts and then go over to our main project, in which we've been making all these changes. I'm going to go inside index.js and make sure that I'm replacing layers data and tile sets with the ones that I just copied. I want to save this; we're looking good.

However, there is one more thing we need to transfer over. At least, I'm pretty sure this is the last thing, and that is going to be within our new export. We want to open up index.html. So, I'm going to open that up within Sublime Text, and we want to make sure that we're pulling in all the correct data files here. Everything that contains data in the beginning, everything within the data folder, I want to copy that.

Next, I want to go to our current project, specifically within index.html, and replace all of our current data folder items with the new ones that we just exported. As long as most of our layers remain the same, specifically collisions and lore gems, we should be okay here. So, let's go ahead and save this, go back to our project, and see how things look.

I’m going to refresh, and everything is working pretty much as expected. Our map just expanded by a great amount, which is awesome. The only thing I can see being an issue right now is that we are rendering out some background gems here. So, when we collect the main gems, yes, that's great, but we don't want these background gems in place, and that is a real easy fix.

We can simply go within index.js and find out where we're referencing our gems layer right here within layers data and tile sets. We want to remove L gems here because all this layer data stuff is just really referencing our background rendering and, to some degree, our collisions. Not really our collisions, but more so just our background rendering. That's the most important part; we want to get rid of the L gems from our background rendering because we know we're dealing with our gem population down below within our animate Loop or, yeah, within our init Loop.

If I look for gems where we push gems in, that's happening down here. We don't want these rendered out in the background; we just want these pushed into the gems array. So, after removing lore gems from layers data and tile sets, we save, refresh, and now our gems are back to normal. So we can begin collecting those; it's looking great.

Let's explore the rest of our map. Now, I can go down because, as we added vertical scrolling, I can collect more gems down here. Everything is functioning perfectly. I can go to the right and collect those gems. I added platforms so we can jump through the platforms to make our way back up and collect the gems over here. Then, I'm going to cheat by jumping a lot to get to the end, and I can collect all the gems over here.

Really, if you reach some instance over here, that's kind of when you would restart your game or, at least, go to the next level, say you won the level. That's really all there is to it.

A couple of finishing touches here: I think I'm going to be a little smarter on where I place the AP possums, and I want to zoom my map in a little bit because we're looking at a lot right now. That's okay, but if I were to zoom out to a normal width and height, I believe this is the actual size in which the game is being rendered out on. I just have things zoomed in so you guys can see it better.

I want to add a little more zoom onto our game because we see a lot of the map right now, and I'm not really too sure if I want to reveal everything from the get-go. I only want to reveal a little bit at a time. I think it would make more sense if we zoom in on our player right here and then begin shifting our map with a different goalpost value.

So, let's do that now. How would I zoom in on our map? Well, if we go down to our animate Loop, eventually we're going to run into c.scale and DPR right here, which stands for device pixel resolution. Depending on whether or not your screen is rated or high, this value is going to change; it can be either one or two. We want to leave this dynamic, but I do want some sort of default zoom in here.

To do that, I'm simply going to add a value of one onto our default scale, and I believe there are two locations in which we're scaling things. Right here, when we're rendering the scene, and then down below for our UI. So, I'm also going to add one on right here just to see how things look to start. Maybe we don't need it for the UI, but let's add one on, go to our game, and you can see, yeah, we're zoomed.

=> 03:38:19

Sometimes, less is more—start simple, adjust as you go, and let the design evolve naturally.

To adjust the zoom level on our map, we need to focus on the anime Loop. Eventually, we will encounter c.scale and DPR, which stands for device pixel resolution. Depending on whether your screen is retina or high-res, this value will change and can be either one or two. We want to keep this dynamic, but I also want to set a default zoom level. To achieve this, I will simply add a value of one to our default scale.

I believe there are two locations where we are scaling things: one when we are rendering the scene and another for our UI. Therefore, I will also add one to the UI scale to see how things look initially. Perhaps we won't need it for the UI, but for now, let's add one and check in our game. Upon doing this, we can see that we are indeed zoomed in a bit, and the UI appears good. I might consider zooming in slightly more, but I am aware that since I zoomed in, my scroll post will be positioned a bit further than I would like.

I want to ensure that we move the scroll post over to the left a bit to account for this zoom-in. Ideally, it should be positioned somewhere right here, but currently, it is over on the far right. Let's change the scroll post location, which is defined on line 224. The scroll post on the right was set to a value of 500, so I will adjust that to something like 300 and test things out on the fly. After refreshing and changing it to 300, I feel it should be a little further to the right, so I will set it to 330.

Now, if I move to the right, I really like where 330 is positioned. However, I also want to test the scroll post's location when going up and down to ensure that I have enough room to see beneath me. When moving down, I prefer the scroll post to be positioned a little higher up. To achieve this, I will subtract a value of around 50 from the scroll post bottom.

I want to experiment with this adjustment. While I could enable the debug mode and render these out as rectangles, I prefer to proceed a bit quicker and test things manually. After making this adjustment, I find that the new position for the scroll post bottom is much better, allowing us to see more beneath us as we travel downwards, which is exactly what I want.

Now, moving back up with this new zoom value and going to the right, I am pleased with how it looks. If I go over to the far right, we still have the previous setup, but I believe it is acceptable for now. Our focus should shift to the more important parts, specifically the aosm placement. Currently, we have four being spawned, but I only want one. I want to introduce the user to the enemies very slowly to prevent them from dying right off the bat. Therefore, when creating new osum, I will only keep one, specifically the one on the far right side. I will remove the last three, save, and refresh, resulting in only one enemy being present, which is perfect for my needs.

However, there is a little character being rendered out in the background, which is an easy fix. While we could have gone to mapper mate when creating our new extension and erased him from whatever layer he is on, we can also handle this programmatically. First, we need to determine what layer the posum is on, which is decorations. This layer is located near the middle of the map.

By examining our decorations data within the project, we should see a lot of zeros, with one location near the middle of our map that does not have a zero. I plan to replace those symbols with zero. Although this approach may seem a bit unconventional, I believe it will work for our current objectives. The layer is called Lore decorations, and I will navigate to my data folder, selecting that layer. I know that around the middle, there should be values that are not zero.

Looking at the grouping of numbers, they likely relate to the house on the far left for that layer. I will focus on values 90, 92, 93, and 94, along with 51 and 52, which correspond to our sign. Therefore, I will change the values at 92, 93, and 94 to zero. If this process proves too challenging, there are always alternative methods to achieve the desired outcome.

=> 03:42:26

When coding, precision is key; small adjustments can lead to big wins in your project.

The process we are undertaking here involves a layer called Lore. I am currently navigating inside my data folder and selecting it. I know that around the middle, there should be something that is not a zero. All the grouping of numbers I see is likely related to the house we had on the far left for that layer, which lights up. However, I am looking for something a little further along, and I believe that is going to be a value of 90. I will say it's 92, 93, and 94, while 51 and 52 will serve as our sign.

So, I will look for values in 92, 93, and 94; in this case, they should be the exact same for your project, but hopefully, it's not too difficult for you to change. I am going to change those three values to zero. If this is too difficult, you can always go to Maper Mate, erase the AOSM, export it, and then replace all this with your new decorations data. However, in this case, this should work.

After making those changes, I will save and go back to our game. It looks like the rendering is mostly gone, but I had two tiles on the top of it as well. If I go up one row, I can see that we have values of 57, 58, and 59. I will replace those with zeros as well. After going back to our game, we are now good with removing that annoying rendering.

Now, I am considering where else I might want to place an AOSM within our game. I am thinking of creating two or three within this little tunnel that I created, and at the very end, maybe I will add one surprise AOSM. To find the values for this, I should mention that I don't have this implemented just yet. However, I plan to add a little indicator next to the row and columns that shows where your mouse is relative to the map. This way, we will be able to hover over a location, get the exact X and Y coordinates, and specify where we want to place our AOSM.

For the time being, I will use some set coordinates and do things programmatically. I think the fastest way to do this is to go into our animation loop. I will find that animation, and then I am going to console log our player position on the X and Y axes. I will do that within one console log, combining player X and player Y. Hopefully, there is a space between those; we will find out. After refreshing and inspecting the element console, I confirm that there is indeed a space between them.

Now, we are console logging the values of where our player is placed. I will go to positions where I know I want an AOSM placed. I think right about here is a good spot, which is going to be around 906 pixels on the X-axis. I want a little bit of space from where the player actually is on the Y-axis, so I will say the Y-axis is about 510 pixels for the next AOSM.

Let's add that in. We are creating a new AOSM right here; this is the original location where we are changing them. We don't really need any of the creation right here because we are actually creating our AOSM every time we call init, so we can leave that empty. Within init, this is where our real AOSMs are being spawned. I want to copy the first AOSM and paste in a second one. The values for X and Y here will be around 906 for X and 515 for Y, as I am subtracting from the actual value.

After saving that, I will check if we get an AOSM down there in the correct location. We know that guy is good; we fall down, and there is the second AOSM. Now, let's add one more over here. I will say that the X value is 1150. So, we will copy one of the AOSMs, paste it, and set X to 1150 while keeping Y the same since these are basically on the same Y value.

After another save and refresh, I will go down into the tunnel. There is one AOSM, and there is the second one, looking really cool. I love the way this is looking! Finally, I want one more AOSM over here. Let’s say he is the tenant of this treehouse. I will place the AOSM starting right here, and that value is going to be about 1663. I will copy the current AOSM, paste in a new one, and set the X value to 1663.

Now, what would the Y value be here? I can say about 200. We should get that rendered out in the correct location, so I will save that, refresh, and collect all of our gems. I will hit the AOSM and go down into the tunnel if we get hit.

=> 03:46:52

To create an engaging game, focus on refining the user experience by adding features that enhance gameplay, like stopping the camera at the map's edge and celebrating player achievements.

In this tutorial, we are working on expanding our game map. We start by adjusting the y value, which can remain the same since these elements are on the same y value. After saving and refreshing, we proceed down into the tunnel, where we find the first two elements looking really cool. I love the way this is coming together.

Next, I want to add one more aasum over here. Let’s say he is the tenant of this treehouse. I will position the osum starting right here, with a value of about 1,663. We will copy the current dep posum and paste in a new one with the value 1663. The yv value here will be about 200, so we should get that rendered out in the correct location. After saving and refreshing, we can collect all of our gems and hit the osum. If we get hit, yes, we lose hearts, but that is okay. We will remove the current a possums and jump cheat to skip ahead quickly. There is the final AP possum for our game, allowing us to jump on him and collect all the final gems. This is how we expand our map.

As we approach the conclusion of this tutorial, one thing I want to fix immediately is the loss of our Parallax scrolling after extending our map. This is easy to add back in. Currently, we have added new layer data from our previous export, but we need to remember that our ocean layer data and Bramble layer data are within L layer one and L new layer 2. By including those in our layer data, we can render our background correctly.

To do this, we need to remove the two layers being used above, which are new layer one and new Layer Two. After saving that, I will go back to our game, and now when we start moving, our Parallax is back. This looks way nicer than before.

Additionally, I want to add the ability to stop our map from moving when we reach the very end. I didn’t implement this in the last chapter because I hadn’t added this cool feature yet. The feature I added is a mouse coordinate section that shows where our mouse is relative to the map. If I move to the very first tile, we have a row of zero, column zero, an X of zero, and a Y of zero down there. Moving to the very end provides pixel coordinates based on the mouse's position.

The main reason for this addition is to prevent the camera from scrolling once we reach a certain point on the map. I think a good position to stop scrolling is around an x value of 1680. I will take that value and go down to our animation loop. First, I will delete the console log that we don’t need anymore.

Scrolling down, I find where we are moving our camera, which is located on line 448. We are currently tracking the scroll post distance. We want to add a limit that says we want to move our camera if the player's X position is greater than the scroll post on the right, but we want to stop moving the camera if we pass the x value of 1680.

I will save this and refresh the game. As we move to the right side of the map, we should see everything stop scrolling once we reach 1680. This is perfect; we are not revealing anything on the far right that we might not want to see.

If we want to add a win condition, we can either say reaching this point means we win or that we need to collect all the gems. I think it’s more interesting to say that once we collect all the gems, we will present some text that says, “You win!” To implement this, we will check the length of the gems array. When it equals zero, meaning we have collected all the gems, we will log “You win!” to the console.

After saving and refreshing, I will collect all the gems. Before grabbing the last one, I will inspect my element and open the console. Once I collect the last gem, we will see the message “You win! Congrats!”

This is how you might win the game. If you want to take this to the next level, you can present a user interface that says you won, or transport the user to a completely new map. You’ve probably seen a lot of games like that.

Now, let’s check out the finished product.

Thank you so much for watching! I really appreciate every view that comes into these videos. If you would like to learn more from me, I have plenty of tutorials, videos, courses, and more on my website. I look forward to seeing you in the next one!