Making A Platformer With HTML

Introduction

In this tutorial we will be making a platformer. The game will have basic 2D physics and will be drawn on a canvas element. The final result can be found here HTML Platformer (link)

If you are completely unfamiliar with programming or find this tutorial to be too complicated, try my more basic tutorial here: Making A Game With HTML (link)

Setting Up The Files

First of all let's make a folder that will hold all the files of the game. Name it whatever you want your game to be named. Next, make a file named index.html (not index.html.txt). In the html file create a canvas element for the game screen:

We need the canvases to be positioned properly and also add a background color if you want. For that we create a CSS file. CSS is a language that deals with the style of elements in a web page. So create a file named style.css:

"overflow: hidden" is because I don't want to see any scrollbars in the game window. "#game-canvas" refers to the element which has an id of "game-canvas". The canvas is going to be placed 0 pixels from the top of the page and 0 pixels from the left of the page.

Next, create the first JavaScript file. let's put all the JavaScript files in one folder, that's easier to manage that way. So create a folder scripts and in it a file named main.js

Now add the CSS and the JavaScript files to index.html :


        <script src = "scripts/main.js"></script>

From now on, every time we create a new JavaScript file, add it to index.html in the same way we just added main.js .

Drawing Rectangles

To draw anything on the canvas we need to first get it in the code. To do that, we use the id of the canvas. We will also get the context, a property of the canvas. In main.js :

let's set the width and height of the canvas to the width aand height of the window:

For start, let's simply draw a rectangle on the canvas. Set the color to red and draw with the fillRect method:

The first two arguments are the position of the left top corner of the rectangle (y coordinate goes from top to bottom). The second two arguments are the width and height of the rectangle.

That's nice but we want to represent objects on the screen. Not hard coded drawings like this one. So first define an object that's going to make drawing and physics simpler in the future. Vector2D will hold an x value and a y value. Define it in a new JavaScript file named physics.js :

Every time we'll call this function it will return a new vector.

Now it's time to define GameObject. The stuff that actually moves and interact, like walls and enemies. For this create a new file gameobject.js :

gameObjects is a list of the gameObjects that were created, added by the push method. Each gameobject gets a new id, I chose to just incrament. The properties position and scale are vector2D and color is a string like "red".

Again, remember to add these two JavaScript files to index.html .

In main.js , make a function for drawing a gameObject. Then, a function to draw everything.

The first line of drawGameScreen clears the canvas. Now you can add gameObjects by calling the GameObject function. After that call drawGameScreen to draw them.

We create the gameObjects in the event listener because we want to make sure the scripts are loaded before the functions are called. If you didn't do that and you added the main.js script before gameobject.js, the GameObject function would not be defined.

Frames And Movement

We are going to make a function that will be called every frame, let's name is "frame". We do this by using window.requestAnimationFrame which also gives us the time at which each frame was called.

timeStamp is the number of milliseconds that has passed from the start of the game to the current frame. I moved the call of drawGameScreen to the frame because we will want to redraw the screen after we make changes to the gameObjects.

To be able to update the game state based on time we need the time that passes between a frame to the next. This is basically the timeStamp of the current frame minus the timeStamp of the previous one.

I put the maxDeltaTime there because when the framerate drops too much stuff can get messed up, like missing collisions and such. updateGameObjects is a function that we will implament shortly.

For movement, add a new vector2D property to GameObject called velocity. Basically it's how many units does the gameObject move each milisecond. In gameObject.js :

In physics.js , implament the updateGameObjects funcion. It will be responsible for moving the gameObjects for now. While we are at it let's implament vector copying, addition and multiplication:

let's change one of the gameObjects velocity to see the movement. In main.js :

Controls And Player GameObject

We are going to set up the controls in, you guessed it, a new JavaScript file. In controls.js make an object that represents the state of the keys:

The property key of each Key is basically the name of the key and the keyCode is a number that distinguishes between the different keys. The pressed property is a boolean that tells us if the key is pressed or not. Write a function to change the pressed value of the keys in responce to keydown (press) and keyup (release) events:

Now that we have the controls we can use them to move the player. To do that we will first define an onUpdate property in GameObject and call it on the updateGameObject function. So in gameObject.js :

And in physics.js :

Back to gameObject.js , create a specific type of GameObject called Player. This is done by calling the GameObject function and modifying the resulting gameObject before returning it. let's start by just defining the scale and the color, and writing an empty onUpdate function for it:

Using the controls object, we can write if statements inside onUpdate to change the velocity of the player:

Now let's spawn a player. In main.js call the Player function to spawn one:

Now you can move the player by pressing A and D.

Collision

In this game, collision means that two gameObjects share some area in the world. In other words, they overlap. GameObject as we difinde it is a rectangle. So, two rectangles A and B overlap if the right edge of A is more right than the left edge of A, the left egde of A is more left than the right edge of B, and the same goes for top and bottom. In physics.js , start by writing a function that receives two gameObjects and returns true if they are overlapping:

The first line in checkCollision is so that gameObjects don't collide with themselves.

In gameObject.js , define onCollision, a function that will be called on collision. let's also give the player some collision effect just so that we can see it's working. let's go for changing the color to the other gameObjects color.

Back to physics.js , we call the onCollision function of the gameObject for every collision and pass the other gameObject to that function:

In platformers, colliding with something solid usually means that you stop. So, let's define a property in GameObject named solid (you may want to create objects that do not block movement). We will give the player up and down movement for now to test the collisions from different angles, even though platformers usually don't have that kind of movement. While we are at it let's also delete that silly color changing thing:

let's start with only sideways blocking. Just setting the x component of the velocity to zero would stop the gameObject but than it would get stuck. What we need is the velocity to zero out only if the gameobject is going towrads the gameobject that's blocking it. Additionally, we set the position of the gameobject to be outside of the blocking one so it doesn't stop inside it. In physics.js :

Now the player stops when it hits the gameobject from the side. Blocking from all directions is a bit trickier though. If we did the same thing for top and bottom the player would teleport unexpectedly when hitting something diagonally. Because of that, we need to determine if the gameObject is colliding vertically or horizontally. We will determine this by calculating the x component of the overlap, the y component of the overlap and then which ever one is smaller, is the direction of collision:

Gravity And Jumping

In real life, objects with mass accelerate towards other objects with mass, this is gravity. In this game gravity is simply going to point down. We will define g, which is the falling acceleration, for every gameObject (even though that's not how it works in the real world). A nother property is going to be a boolean named grounded, whether or not the object is touching the ground. It's a good idea to remove that up and down movement from the player now. In gameObject.js :

Time to fall. In physics.js , on update, each gameObject that's not grounded y velocity is going to increase by its g for each milisecond. In the blockGameObject function we set grounded to true in the case of collision from above. In physics.js :

maxFallSpeed is the maximum speed at which a gameObject can fall. I find that to feel better when you don't fall in creazy speeds. Also, this phenomenon does exist in real life because of air resistance.

If you refreshe the page now everything falls. Platforms that the player is going to jump on should have a g of 0 so that they don't fall themselves. Define a type of gameObject named Block in gameObject.js :

let's set up a few blocks in main.js :

Now you land on a platform, and you can walk off the edge.

Next, we add jumping. To jump, the player gets upwards speed when the up key is pressed but only at the start of the jump. In other words, when it is grounded. In gameObject.js :

In platformers, usually holding the jump button for longer makes you jump higher. To achive this we can change the g of the player when the up key is released. let's limit the jump time so it doesn't look like the player is parashooting down. In gameObject.js :

Camera

To show gameObjects relative to a camera we subtruct the cameras position from the position of the gameObject. If the position of the camera is the same as the position of the gameObject we want to show it in the middle of the screen, not the top left corner. So, we add half of the width of the gameCanvas to the x and half of the height to the y. For start, set the player to be the camera. In main.js :

Now the screen follows the player sharply. That's not very nice to look at if you ask me. To make the screen follow smoothly create a type of gameObject named camera. It's going to move to the player fast when it's far and slow when it's close. In gameObject.js :

The higher the followPresentage is the sharper the movement is going to be. Now set the camera to be an instance of this gameObject. if main.js :

Enemy And Destruction

It's not a good idea to destroy gameObjects in the middle of an update. That can cause some unexpected results. So instead, we set a property named destroy to be true when we want to destroy something. Only at the end of the frame we filter the gameObjects by that property. In main.js :

In onCollision of player we check if the gameObject it collided with has the property damage. If it does, we set destroy to true. We also make an enemy with that damage property. In gameObject.js :

Now, add an instance of that enemy in main.js :

When the player touches the enemy it is destroyed.

One last thing, moving the enemy. This is going to be done in it's onUpdate function. let's simply have it walk one way and swhich direction after a set time. In gameObject.js :

And that's it. Hope you had fun. Thanks for reading.