DEV Community

ZiomaleQ
ZiomaleQ

Posted on

JavaScript from scratch! Part 2

Hey! Welcome back. I hope you're not tired yet, because we are just starting our SPEEDRUN It was a joke, there is no speedrun.

So what we are going to build today? Nothing. Hah, you fell for it! We are going to build a simple snake game! What are you going to learn exactly? Here is some stuff:

  • Window API (setTimeout for example
  • Events
  • Canvas manipulation
  • Functions
  • Dom manipulation
  • Some other stuff but I'm lazy to write more

Setting up our project!

To set up a project, just make a directory. In that directory make two files: index.html, script.js. index.html file is for basic HTML with all the magic provided by the JavaScript file.

index.html

Just copy this bad boy below into your index.html file and open it using your browser.

<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script src="script.js"></script>
        <title>Snek</title>
    </head>
    <body>
        <canvas id="snake-game" style="width: 600px; height: 300px; border: 2px black solid;"></canvas>
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Ready? So let's keep moving!

Thinkering with javascript

Before we start let's revise what we are going to build, and make a simple checklist. There it goes then:

  1. Variables for config (snake data, fruit data)
  2. Interval running for canvas rendering
    1. Snake
    2. Moving
    3. Fruit generation
    4. Eating a fruit
    5. Tail expansion
  3. Game end, current score

Let's start with variables... There will be one to rule them all

let data = {
    user: {
        x: 0,
        y: 0,
        direction: 2
    },
    state: 0
}
Enter fullscreen mode Exit fullscreen mode

So what we are doing here is that we make a variable called data, then we are using the = operator to assign value to data and here we go. The value here is JavaScript Object Notation (JSON). This thing is very useful for making grouped variables. The basic structure is {key: value, key1: value1} and so on. State key is a property for holding current game state like running, off, lost. user is itself a nested JSON. To access any of that you can use dot notation, like so:

data.user.direction // 2
Enter fullscreen mode Exit fullscreen mode

There some built-in variables like document, window, or console. The document variable is a global variable that corresponds to the current page, a console is your developer console. Let's get our canvas object so we can use it.

// Add after data variable
const canvas = document.getElementById("snake-game");
Enter fullscreen mode Exit fullscreen mode

This lets you get a reference to the canvas object. Notice ("snake-game"), this calls a function with one argument. We will talk about it a little bit later, but it's a built-in function just so we can use it freely. We use const so we won't override it easily, like assigning a number to it.

//After canvas declaration
const ctx = canvas.getContext('2d');
Enter fullscreen mode Exit fullscreen mode

It uses this. As you can see it returns an element of type CanvasRenderingContext2D. On this object we should call methods to draw things to a canvas.

Defining a function

Just to make our code cleaner we can create a function to draw a snake for us. But wait, what are we use these functions for? For example, instead of writing the same code, we can call these magic functions. You can read more about it here.

function drawSnake() {
    //Code to draw the actual snake
}

//Call the function after declaration
drawSnake();
Enter fullscreen mode Exit fullscreen mode

So what we should do here? We should get the current player position. Then draw it according to some measurements. So, what are we waiting for? I'm waiting for some sandwich, how about you?

//Inside drawSnake function
let x = data.user.x;
let y = data.user.y;

//Changing pen color
ctx.fillStyle = 'green';
//Drawing a 10x10 pixels square at x,y position
ctx.fillRect(x * 10, y * 10, 10, 10);
Enter fullscreen mode Exit fullscreen mode

Save the file. Open your web browser and run the index.html file, and check your developer console if there are any warnings. And so? There is something there? Of course, there is! And it's not that bad. But first why is happening this error and nothing is drawn to a canvas? It's because we're getting an element before rendering a page so let's wait for a page load. Using JavaScript of course.

Let's change our function a little to something like this:

function drawSnake(ctx) {
    let x = data.user.x;
    let y = data.user.y;

    //Changing pen color
    ctx.fillStyle = 'green';
    //Drawing a 10x10 pixels square at x,y position
    ctx.fillRect(x * 10, y * 10, 10, 10);
}
Enter fullscreen mode Exit fullscreen mode

Also, remove these two global variables called canvas and ctx you can also remove this one lonely drawSnake function call. Add this little code:

window.onload = function() {
    const canvas = document.getElementById("snake-game");
    const ctx = canvas.getContext('2d');

    drawSnake(ctx)
}
Enter fullscreen mode Exit fullscreen mode

So what it actually does? Let me explain. We access onload property of the global object window then override it with an anonymous function. It's just a function without a name. It executes this code when the page is loaded. Since we no longer have a global variable you can just pass an argument to a function. So far we've got something like this code below.

//script.js
let data = {
    user: {
        x: 0,
        y: 0,
        direction: 2
    },
    state: 0
}

window.onload = function() {
    const canvas = document.getElementById("snake-game");
    const ctx = canvas.getContext('2d');

    drawSnake(ctx);
}

function drawSnake(ctx) {
    let x = data.user.x;
    let y = data.user.y;

    //Changing pen color
    ctx.fillStyle = 'green';
    //Drawing a 10x10 pixels square at x,y position
    ctx.fillRect(x * 10, y * 10, 10, 10);
}
Enter fullscreen mode Exit fullscreen mode

Woah, we did it... But it doesn't move at all. Let's add code to do it! Here goes another function:

//At the end
function moveSnake() {
    switch (data.user.direction) {
        case 0:
            data.user.x--;
            break;
        case 1:
            data.user.y++;
            break;
        case 2:
            data.user.x++;
            break;
        case 3:
            data.user.y--;
            break;
    }
}
Enter fullscreen mode Exit fullscreen mode

But wait, what is this weird thing called switch? It's a shorthand for a long if else statement. We'll talk about it later I promise. After switch keyword you pass an argument that cases will be matched against. So for example, if a direction is equal to 2, the snake will move left. That's because variable++ is a shorthand for variable = variable + 1. It's called incrementation. -- is subtracting 1 from the variable. But wait, there is also a keyword called break. It's there because we don't want the cases to fall through. You can read about switch and cases here.

//in window.onload anonymous function instead of drawSnake(ctx);
setInterval(function() {
    drawSnake(ctx);
    moveSnake();
}, 500)
Enter fullscreen mode Exit fullscreen mode

But wait, instead of one square we see a green strip. Okay, okay. We forgot to clear it up, let's fix it right away! Add this line above drawSnake() call.

ctx.clearRect(0, 0, canvas.width, canvas.height);
Enter fullscreen mode Exit fullscreen mode

We are done! You can see it's moving quite fast. One square every 500 ms. And that's all for this post... Stay tuned for more and see you soon!

Top comments (0)