Game | Part 1 â Intro to Building an Interactive Animated Game in JavaScript
Today we're going to begin a journey  đ of programmatically creating a video game  đšī¸Â in JavaScript without any libraries  đ using the HTML5 <canvas>
element  đ¨Â .
đ˛ Setting Up the HTML Canvas
First things first. We need to define our canvas - the area where we'll be doing all our drawing.
<canvas></canvas>
Easy, right? đ Let's give it some style.
đ¨ Styling the Canvas
Using CSS, let's paint our canvas a cool deepskyblue color.
canvas {
background: deepskyblue;
}
Beautiful! Now we're ready to play with some JavaScript.
đ JavaScript Time
Our JavaScript code will handle the drawing and user interaction. Let's break it down.
Grabbing the Canvas Context
The canvas.getContext('2d')
method returns a drawing context on the <canvas>
- it's the magic brush đī¸ we'll use to draw and manipulate graphics.
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
Creating an Entity Class
Here we'll create our drawable entity
- a cute little rectangle that we'll move around our canvas. We'll first define an Entity
class đī¸ that will have a constructor to set initial position and size, and a draw method to paint the entity
as a darkorange rectangle.
class Entity {
constructor() {
this.width = 30;
this.height = 30;
this.position = {
x: canvas.width / 2 - this.width / 2,
y: canvas.height / 2 - this.height / 2 ,
};
}
draw() {
ctx.fillStyle = 'darkorange';
ctx.fillRect(this.position.x, this.position.y, this.width, this.height);
}
}
const entity = new Entity();
We then instantiate an entity
object from our Entity
class.
Making Things Move - Animation
Let's bring our little rectangle to life! The animate
function is called repeatedly, clearing the <canvas>
and drawing the entity
in each frame. This creates the illusion of movement đ. We also measure the time between each call and log it to the console.
function animate(t1) {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, canvas.width, canvas.height);
entity.draw();
const dt = Math.round(t1 - t0);
console.log(`animate() \ndt: ${dt}ms.`);
t0 = t1;
}
animate();
Adding Interactivity
Time for some user input! We set up an event listener for keyboard interaction. When an arrow key is pressed, the entity
moves 5 pixels in the corresponding direction.
addEventListener('keydown', ({ key }) => {
console.log('key: ', key);
const dist = 5;
if (key === 'ArrowLeft') {
entity.position.x -= dist;
}
if (key === 'ArrowRight') {
entity.position.x += dist;
}
});
Adding Physics
We can add a gravity effect by continuously applying a downward force to our entity
. We'll need to update the Entity
class to account for velocity and then adjust this velocity each frame to simulate gravity.
class Entity {
constructor() {
// ...
// Added velocity property
this.velocity = {
x: 0,
y: 0,
};
}
draw() { ... }
// Added update method to adjust position and apply gravity
update() {
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
// Gravity
this.velocity.y += 0.5;
}
} // class Entity
We've added a velocity property to the Entity
class. We also added an update method to adjust the entity
's position and apply gravity.
Every frame, the entity
's position is updated based on its velocity. Then, a gravitational force is applied by continuously adding a small amount to the entity
's vertical velocity.
function animate(t1) {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Added update method invocation
entity.update();
entity.draw();
// ...
}
animate();
We now need to update the entity
's position and velocity each frame by invoking the update
method inside the requestAnimationFrame
callback.
Collision detection
Let's add a simple check for collisions with the bottom of the <canvas>
. When such a collision occurs, we set the entity
's position to just above the bottom of the canvas
and its vertical velocity to 0 to simulate it hitting the ground.
class Entity {
constructor() { ... }
draw() { ... }
// Added update method to adjust position and apply gravity
update() {
// Update position
// ...
// Update y-dimension velocity (with gravity)
// ...
// Check for collision with canvas bottom
if (this.position.y + this.height > canvas.height) {
this.position.y = canvas.height - this.height;
this.velocity.y = 0;
}
}
}
đ But wait, there's more!
Currently, the user must have a physical keyboard to even move the entity
! Let's set up some development controls that will allow the user to move the entity
if they don't have a physical keyboard.
Development Non-Keyboard Controls
<html>
<head> ... </head>
<body>
<canvas></canvas>
<!-- Controller Buttons -->
<div class="controller">
<div class="row">
<button id="up">â</button>
</div>
<div class="row">
<button id="left">â</button>
<button id="right">â</button>
</div>
<div class="row">
<button id="down">â</button>
</div>
</div>
</body>
</html>
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
class Entity {
constructor() { ... }
draw() { ... }
update() { ... }
}
const entity = new Entity();
// ==========================================
let t0 = 0;
function animate(t1) {
requestAnimationFrame(animate);
.
.
.
}
animate();
// ==========================================
addEventListener('keydown', (e) => { ... });
// ==========================================
// On screen controller
const dist = 5;
document.getElementById('left').addEventListener('click', () => {
entity.position.x -= dist;
});
document.getElementById('right').addEventListener('click', () => {
entity.position.x += dist;
});
Now the user can click the left and right icons on the screen to control our entity
.
Live Demo
Wrapping Up
đ Voila! We've got a rectangle đĻ moving đââī¸ on the screen đĨī¸. Our current game is pretty simple and rough around the edges đŋ, however it gives us something to start đĻ iterating on âģī¸.
In the next episode đŦ, we'll polish up the controls đšī¸, smooth out the physics đŦ, give our player the power to jump đĻ, make our game loop utilize a more professional timing methodology, fix the geometric distortion and blurriness, and level up our collision detection đ¯.
So stay tuned đē for more exciting posts coming soon to a web browser đ near you! đ đ