Write a square in JavaScript

Source: Internet
Author: User
I used TurboC ++ 3.0 to write the Russian box under DOS, and later I used VB to write another version. This time, I decided to use JavaScript to write another front-end es6 project, which is not entirely painstaking. Technically, I want to try to use webpack + babel to build it. I used Turbo C ++ 3.0 to write the Russian box under DOS, and later I wrote another version with VB. This time, I decided to use JavaScript to write another front-end es6 project, which is not entirely painstaking. Technically, I want to try to use webpack + babel to build it.


Project Structure

This is a pure static project, and the HTML has only one page, that is, index.html. Style sheets do not have much content and are still used to writing with LESS. The reason why I don't like sass is straightforward-I don't want to pretend to be forced (Ruby ).

The focus is naturally on scripts. One is to try the complete ES6 syntax, including import/export module management; the other is to try to build a static language project, using the construction idea, we can use webpack + babel to build the target script of es5 syntax.

Source (es6 syntax, modular) ==> target (es5 syntax, packaging)

JQuery is used in the project, but I don't want to package jQuery in the target script or download it manually, so I just tried bower. Compared with manual download, bower is advantageous. At least bower install can write the build script.

In the beginning, the project directory structure was not particularly clear, so the directory structure created was actually a bit messy. The entire directory structure is as follows:

[Root> | -- index.html: Entry | -- js/: Build the generated script | -- css/: Build the generated style sheet | -- lib /: library '-- app/: front-end source file | -- less: style sheet source file' -- src: script (es6) source file introduced by bower

Build configurations

The front-end build script uses webpack + babel, and the style sheet uses less, which is then organized through gulp. All front-end build configurations and source code are stored in the app directory. The app directory is an npm project, including gulpfile. js and webpack. config. js.

Because gulp has been used before, fulpfile. js is quite easy to write, but it takes some effort to configure webpack.

I copied a configuration on the Internet.

const path = require("path");module.exports = {    context: path.resolve(__dirname, "src"),    entry: [ "./index" ],    output: {        path: path.resolve(__dirname, "../js/"),        filename: "tetris.js"    },    module: {        loaders: [            {                test: /\.js$/,                exclude: /(node_modules)/,                loader: "babel",                query: {                    presets: ["es2015"]                }            }        ]    }};

Then, during the writing process, I found that jQuery was needed to be introduced, so I searched the internet for a long time and copied a sentence.

externals: {        "jquery": "jQuery"    }

But later I saw that ProvidePlugin is recommended and I will study it later.

At the beginning of the code development, debugging was very troublesome during the first run. After compilation, the source code of es6 could not be found. At this time, we found that a very important source map is missing. So I searched the internet for a long time and added

devtool: "source-map"

Program Analysis

Because I have written it before, there is still an image in the data structure, and the game area corresponds to a two-dimensional array. Each graph is a set of coordinates with relative positional relationships. Of course, there are also color definitions.

All actions are implemented through changes in data (coordinates. Obstacle (small fixed blocks) the judgment is based on the calculation of the coordinate of each small square by the current graph position and the relative position of all small squares in the definition, and then checks whether the coordinate of the large matrix has small square data. This requires us to calculate the list of coordinates occupied by the current graph in the next form in advance.

The automatic fall of blocks is controlled by the clock cycle. If you want to delete the animation, you may need two clock cycle controls. Of course, we can merge two major common clock cycles into a common clock cycle, but the animation of Tetris is quite simple, there seems to be no need for such complex processing-you can consider suspending the falling clock cycle during elimination and restarting after elimination.

The interaction part is mainly handled by the keyboard. You only need to bind the keydown event to the document for processing.

Square Model

There are only seven images in the traditional Russian box, and a total of 19 images are added with rotation deformation. Therefore, there are not many images that need to be defined, so I am too lazy to write a rotation algorithm and use coordinates to define them directly. So I used the WPS table to draw the image:

Then define the structure in JavaScript based on this image. The expected data structure is as follows:

SHAPES: [Shape] // pre-defines the structure of all SHAPES: {// The colorClass: string, // css class forms used for dyeing: [Form] // a combination of rotation deformation} Form: [Block] // graphic deformation, is the coordinate Block of a group of small squares: {// coordinate x: number of small squares, // x indicates horizontal y: number // y indicates vertical}

SHAPES and Form are both directly represented by arrays. The Block structure is simple and directly represented by literal objects. Only one Shape class needs to be defined. (Some methods should be added in this section, but later I found it unnecessary)

class Shape {    constructor(colorIndex, forms) {        this.colorClass = `c${1 + colorIndex % 7}`;        this.forms = forms;    }}

To be lazy, SHAPE uses a three-dimensional Array of data, which is obtained through Array. prototype. map ().

Class Shape {constructor (colorIndex, forms) {this. colorClass = 'C ${1 + colorIndex % 7} '; this. forms = forms ;}} export const SHAPES = [// square [[0, 0], [0, 1], [1, 0], [1, 1], // | [[0, 0], [0, 1], [0, 2], [0, 3], [0, 0], [1, 0], [2, 0], [3, 0], //... omitted, see the source code address attached at the end of the article]. map (defining, I) => {// data is the forms mentioned above. I didn't think about it well, and I didn't change const data = defining. map (form => {// calculate the right and bottom mainly for the sake of the following exit judgment let right = 0; let bottom = 0; // point is the block, at that time, I did not think about const points = form. map (point => {right = Math. max (right, point [0]); bottom = Math. max (bottom, point [1]); return {x: point [0], y: point [1] };}); points. width = right + 1; points. height = bottom + 1; return points;}); return new Shape (I, data );});

Game Zone Model

Although there is only one game area, there is also a preview area in terms of drawing behavior. In addition to display, the game area also needs to handle the box drop, response keyboard operation left, right, move down and deformation, accumulation, elimination, and so on.

A Matrix class is defined for processing the display. Matrix is mainly used to create small blocks in HTML to display each small block and to draw small blocks based on data. Of course, the so-called "painting" is actually just a set css class, let the browser to deal with the painting.

Matrix creates the DOM Based on the width and height passed in by the construction. Each row is

As a container, but what needs to be operated is the small square in each line, represented. In fact, the structure of Matrix is also very simple. Here we will simply list the interfaces. For specific code, refer to the source code link below.

class Matrix {    constructor(width, height) {}    build(container) {}    render(blockList) {}}

Logical Control

As mentioned above, the main game zone has some logic control, while Matrix only handles the problem of plotting. Therefore, another class is defined: Puzzle to handle control and logic problems, including

Preview The display generated by the image

Game graphics and fixed square display

Ongoing graphical behavior (rotation, left shift, right shift, move down, etc)

Boundary and obstacle judgment

Row judgment can be eliminated after the fall ends

Fall animation Processing

Eliminate animation Processing

Data recalculation after elimination (due to location change)

Game Over judgment

......

In fact, the key issue is the display of images and fixed blocks, boundary and obstacle judgment, and animation processing.

Draw game area blocks

It has been determined that the Matrix is used to process the drawing, but the drawing requires data, and the data is divided into two parts. One part is the current fallen image, and its position is dynamic; the other part is the previous fallen image, which has been fixed in the game zone.

Generate a blocks array from the current falling graph, and then generate another blocks array from the fixed small square. The array is the data of Matrix. render. After Matrix obtains the data, it first traverses all the data, clears the color class, and then traverses the obtained data. It sets the corresponding css class according to the position and color provided by each block. This completes the painting.

Boundary and obstacle judgment

The Shape mentioned above is just a definition of a Shape, and the falling graph is another entity. Because the Shape name is occupied, Block is used in the source code to name it.

This name is indeed a bit messy and needs to be resolved as follows: Shape-> ShapeDefinition; Block-> Shape.

The image in the current drop is a Block instance (object ). The location information and Boundary Information (right and bottom) need to be used in the process of determining the boundary and obstacle. In addition, you need to know which rotation form it is currently ...... Therefore, some attributes are defined.

However, the key issue is that you need to know which coordinates the next state (Position, rotation) will occupy. So several methods are defined.

Fasten (): returns the coordinates occupied by the current position and form when no parameter is set. It is mainly used for plotting. If the parameter is set, returns the coordinates required for the specified position and form.

FastenOffset (), because the usually required displacement coordinate data has only a small amount of offset relative to the original position, so this method is defined to simplify the call of the fasten () parameter.

FastenRotate () Simplifies the call to fasten () after rotation.

One thing to note here is that after a graph reaches the boundary, the rotation may cause an out-of-bounds. In this case, the Block must be displaced. Therefore, the rotate () and fastenRotate () parameters of the Block can be input to calculate the correction position. The corrected position is implemented through a local function getRotatePosition () in the module.

Animation Control

As mentioned above, the animation clock is divided into two types: Fall animation clock and eliminate animation clock. For animations caused by manual operations, you do not need to use the clock to re-paint them after the operation.

Consider that you need to pause the animation when you start to remove the animation, and then start again. Therefore, the falling animation clock is defined as a Timer class to control stop () and start (). The internal implementation is of course setInterval () and clearInterval (). Of course, Timer can also be used for animation elimination, but because the code is relatively simple when writing the animation elimination, you can directly write setInterval () and clearInterval.

In the Puzzle class, the fastenCurent () method is used to fix an image. After the current image is fixed in this method, eraseRows () is called () to check and delete filled rows. The row elimination and compression operations are both processed here, And the animation processing of the row elimination is also performed here-Clear the data of the row to be eliminated from left to right and re-paint it immediately.

Let columnIndex = 0; const t = setInterval () => {// fulls is the row to be eliminated. forEach (rowIndex) => {matrix [rowIndex] [columnIndex] = null; this. render () ;}); // when the column reaches the right boundary, the animation is ended if (++ columnIndex> = this. puzzle. width) {clearInterval (t); reduceRows (); this. render (); this. process () ;}}, 10 );

Summary

The algorithm of Tetris is not difficult, but there are still some problems in this hasty little game that need to be handled in the future:

There is no interaction method to start and end, the page will continue to run once it is opened.

Scoring not introduced yet

Each painting is a full re-painting. It should be optimized to partial (changed part) re-painting.

For more JavaScript-related articles on Tetris, follow the Chinese PHP website!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.