JS implements the snake game

JS implements the snake game

Page effect:

The Snake game is developed using the mediator model. Our Snake case is not written in one HTML file like before, but is split into classes. Separate js represents each class, and the mediator class is the Game class.

1. Initialization structure

First we need to initialize the page. The initialization layout is not written directly in the HTML tag, but initialized through the game node tree

We set up a table with 20 rows and 20 columns, using this.row to represent rows and this.col to represent columns.

Games.prototype.init = function () {
    this.dom = document.createElement('table');
    var tr, td;
    //Traverse the row and column tree for (var i = 0; i < this.row; i++) {
        // Traverse the rows and create a tree on the node tr = document.createElement('tr');
 
        for (var j = 0; j < this.col; j++) {
            // Traverse the column and create a tree on the node td = document.createElement('td');
            // Append the node to the tree tr.appendChild(td);
 
 
            // Append the node to the tree this.dom.appendChild(tr);
 
        }
    }
    // Tree on the table document.getElementById('app').appendChild(this.dom);
}

Result:

2. Rendering the Snake’s Color

Logic for rendering snakes: The snake class calls the setColor method of the Game class, because the essence is to render the color of the table. The table is created when the Game class is initialized, so it is reasonable to let Game set the method for rendering the color.

// Method to set color Games.prototype.setColor = function (row, col, color) {
    // Set the color of the row and column of the table this.dom.getElementsByTagName('tr')[row].getElementsByTagName('td')[col].style.backgroundColor = color;
}

At this point we let the Snake class call the setColor method of game

Snake.prototype.render = function () {
    // Rendering of snake game.setColor(this.body[0].row, this.body[0].col, 'pink');
    // Snake body for (var i = 1; i < this.body.length; i++) {
        game.setColor(this.body[i].row, this.body[i].col, 'skyblue');
    }
}

At this point there is a question, who will call Snake's render? We can't get game by calling it in our Snake's own constructor, because the four steps of the Game class have not been executed yet, so it is now undefined

The solution is to call the timer, because the timer is asynchronous, so it will not prevent the four steps of the Game class from running.

    this.timer = setInterval(function () {
        // The core of the timer is the essence of game rendering, clear screen - update - render // Render snake game.snake.render();
    }, 20);

Result:

3. Snake Movement

The movement of the snake is actually the update of the body array. The essence is to delete the tail of the body array and add the head, so the snake will render the new state.

Snake.prototype.update = function () {
 
    // The current direction receives willdirection
    this.direction = this.willDirection;
    switch (this.direction) {
        case 'R':
            this.body.unshift({ 'row': this.body[0].row, 'col': this.body[0].col + 1 });
            break;
        case 'D':
            this.body.unshift({ 'row': this.body[0].row + 1, 'col': this.body[0].col });
            break;
        case 'L':
            this.body.unshift({ 'row': this.body[0].row, 'col': this.body[0].col - 1 });
            break;
        case 'T':
            this.body.unshift({ 'row': this.body[0].row - 1, 'col': this.body[0].col });
            break;
    }

At this point you will find that the snake will get longer and longer

Because we just told the snake how to render the color, we need to erase the previous rendering

We set a method to erase the screen for Game

// Clear the screen Games.prototype.clear = function () {
    for (var i = 0; i < this.row; i++) {
        for (var j = 0; j < this.col; j++) {
            this.dom.getElementsByTagName('tr')[i].getElementsByTagName('td')[j].style.backgroundColor = '#fff';
            this.dom.getElementsByTagName('tr')[i].getElementsByTagName('td')[j].innerHTML = '';
        }
    }
}

Next we will perform the three steps of the game in the timer: clear screen-update and render

 // Clear the screen game.clear();
        //Snake movement/update //Snake update speed When the snake side is longer, the speed increases var during = game.snake.body.length < 30 ? 30 - game.snake.body.length : 1;
        // Snake update game.f % during == 0 && game.snake.update();
        // Render the snake game.snake.render();

The snake moves in different directions. You can set a bindEvent event for Game to monitor keyboard events, change different directions, and determine that the up key cannot be pressed when the snake head moves downward.

Games.prototype.bindEvent = function () {
    var self = this;
    //Keyboard event document.onkeydown = function (even) {
        switch (even.keyCode) {
            case 37:
                // First judge, if the current direction is moving to the right, we cannot press the left button at this time if (self.snake.direction == 'R') return;
                self.snake.changeDirection('L');
                break;
            case 38:
                // First judge, if the current direction is moving downward, we cannot press the up key at this time if (self.snake.direction == 'D') return;
                self.snake.changeDirection('T');
                break;
            case 39:
                // First judge, if the current direction is moving to the left, we cannot press the right button at this time if (self.snake.direction == 'L') return;
                self.snake.changeDirection('R');
                break;
            case 40:
                // First judge, if the current direction is moving upward, we cannot press the key at this time if (self.snake.direction == 'T') return;
                self.snake.changeDirection('D');
                break;
        }
    }
}

At this time, the Snake class must also have a corresponding direction match. We set a this.direction='R' when initializing Snake.

Snake.prototype.update = function () {
 
    // The current direction receives willdirection
    this.direction = this.willDirection;
    switch (this.direction) {
        case 'R':
            this.body.unshift({ 'row': this.body[0].row, 'col': this.body[0].col + 1 });
            break;
        case 'D':
            this.body.unshift({ 'row': this.body[0].row + 1, 'col': this.body[0].col });
            break;
        case 'L':
            this.body.unshift({ 'row': this.body[0].row, 'col': this.body[0].col - 1 });
            break;
        case 'T':
            this.body.unshift({ 'row': this.body[0].row - 1, 'col': this.body[0].col });
            break;
    }

4. How to determine the death of a snake There are two ways to determine the death of a snake

The first is that the snake itself exceeds the table part

 // The part that exceeds the edge of the table if (this.body[0].col > game.col - 1 || this.body[0].row > game.row - 1 || this.body[0].col < 0 || this.body[0].row < 0) {
        alert('Game over, your current score is ' + game.score);
        clearInterval(game.timer);
        this.body.shift();
        clearInterval(game.timer);
    }

The second is that the snake itself overlaps with a part of its body.

  // hit itself for (var i = 1; i < this.body.length; i++) {
        if (this.body[0].col == this.body[i].col && this.body[0].row == this.body[i].row) {
            alert('Game over, your current score is ' + game.score);
            this.body.shift();
            clearInterval(game.timer);
        }
    }

5. Creation of Food

At this point we create a Food class to produce food, instantiate it in Game, and render it in a timer

When we randomly generate the row and col of food, we first check whether it is on the snake.

function Food(gameSnake) {
    var self = this;
    // Location of food // The do-while loop statement creates a row and col first and then determines whether the row and col are on the snake. do {
        this.row = parseInt(Math.random() * gameSnake.row);
        this.col = parseInt(Math.random() * gameSnake.col);
    } while ((function () {
        // Traverse the row and col of the snake and then compare them with the newly randomly generated row and col of Food to see if they overlap for (var i = 0; i < gameSnake.snake.body.length; i++) {
            if (gameSnake.snake.body[i].row == self.row && gameSnake.snake.body[i].col == self.col) {
                return true;
            }
 
        }
        return false;
    })())
 
    console.log(this.row, this.col);
}
 
Food.prototype.render = function () {
    game.setHTML(this.row, this.col, '♥');
}

6. The length of the food that snakes eat

When the snake moves, it will add an element to the head of the array body and delete an element at the tail. Therefore, after the snake head touches the food, we only need to keep the tail intact.

   if (this.body[0].row == game.food.row && this.body[0].col == game.food.col) {
        // Create new food game.food = new Food(game);
        // Return the frame number to 0
        // Add score game.score++;
        game.f = 0;
    } else {
        this.body.pop();
    }

Snakes eat food faster

We set a frame number that increments until it hits food, and then let the snake update faster as the snake's length increases.

         this.f = 0;
        game.f++;
       // Update speed of the snake. When the snake's side is longer, the speed increases var during = game.snake.body.length < 30 ? 30 - game.snake.body.length : 1;
        // Snake update game.f % during == 0 && game.snake.update();
        // Render the snake

7. Start Game Function

We just need to write a button in HTML and put it in the right place through positioning, give it a click event, and only after we click the start button will the code in Game be executed.

 <div id="app"></div>
    <div class="startgame"><img src="images/btn1.gif" alt=""></div>
    <div class="stopgame"><img src="images/btn4.png" alt=""></div>
    <script>
        var game = null;
        var btnstart = document.querySelector('.startgame');
        var btnstop = document.querySelector('.stopgame')
        btnstart.addEventListener('click', function () {
            btnstart.style.display = 'none';
            game = new Games();
            // console.log(table);
            var table = document.querySelector('table');
            table.addEventListener('click', function () {
                clearInterval(game.timer);
                btnstop.style.display = 'block';
            })

8. Pause/resume game function

We give a click event to both the pause button and the table. When we click on the table, the stop button appears and stops the timer in the Game. When we click on the pause button, the timer starts and hides itself.

     btnstop.addEventListener('click', function () {
                btnstop.style.display = 'none';
                game.timer = setInterval(function () {
                    // The core of the timer is the essence of game rendering, clear screen - update - render game.f++;
                    // document.getElementById('f').innerHTML = 'Frame number:' + game.f;
                    // // Render score // document.getElementById('score').innerHTML = 'Score:' + game.score;
                    // Clear the screen game.clear();
                    //Snake movement/update //Snake update speed When the snake side is longer, the speed increases var during = game.snake.body.length < 30 ? 30 - game.snake.body.length : 1;
                    // Snake update game.f % during == 0 && game.snake.update();
                    // Render the snake game.snake.render();
                    // Render food game.food.render();
                }, 10);
            })

This concludes this article about implementing the Snake game in JS. I hope it will be helpful for everyone’s study, and I also hope that everyone will support 123WORDPRESS.COM.

You may also be interested in:
  • JavaScript exquisite snake implementation process
  • JavaScript to achieve the idea of ​​​​snake game
  • JavaScript implementation of classic snake game
  • JS practical object-oriented snake game example

<<:  Summary of English names of Chinese fonts

>>:  SQL implementation of LeetCode (178. Score ranking)

Recommend

Vue directives v-html and v-text

Table of contents 1. v-text text rendering instru...

Implementation of breakpoint resume in Node.js

Preface Normal business needs: upload pictures, E...

Graphical explanation of the solutions for front-end processing of small icons

Preface Before starting this article, let’s do a ...

JS realizes the automatic playback effect of pictures

This article shares the specific code of JS to ac...

Detailed process of installing logstash in Docker

Edit docker-compose.yml and add the following con...

Detailed explanation of Truncate usage in MySQL

Preface: When we want to clear a table, we often ...

Implementation of MySQL Shell import_table data import

Table of contents 1. Introduction to import_table...

43 Web Design Mistakes Web Designers Should Watch Out For

This is an article about website usability. The a...

CSS animation property usage and example code (transition/transform/animation)

During development, a good user interface will al...

Negative distance (empathy) - iterative process of mutual influence

Negative distance refers to empathy. Preface (rai...

Nginx one domain name to access multiple projects method example

Background Recently, I encountered such a problem...

Details of function nesting and closures in js

Table of contents 1. Scope 2. Function return val...

Nginx implements https website configuration code example

https base port 443. It is used for something cal...

MySQL Index Optimization Explained

In daily work, we sometimes run slow queries to r...

Why MySQL does not recommend using subqueries and joins

To do a paginated query: 1. For MySQL, it is not ...