Using JavaScript to implement a Tetris

Source: Internet
Author: User

Qingming holiday period, idle bored, do a little game play, the current game logic has not found a bug, just look slightly ugly some-.-
Project Address: Https://github.com/Jiasm/tetris
Online demo:http://blog.jiasm.org/tetris/?width=16&height=40 (Modify the URL parameters to adjust the difficulty)

The whole is divided into three pieces for development, using object-oriented programming (in fact, I prefer to use functional programming, but it is more intuitive to use some of the game's state to store objects):

    1. Game
      1. Responsible for generating new blocks.
      2. Responsible for the processing of block movement
      3. Judging the bottom of a block
      4. To remove rows that meet the purge criteria
    2. Render
      1. Responsible for using Game the data to render the entire game interface
    3. Controller
      1. Responsible for accepting user input (up and down various actions) and processing
      2. Feedback the status of the current game to the user

This layering brings a benefit, the logic module of our game Game does not depend on the current program running environment, but can be, Render Canvas DOM or even console output. We want to migrate to other platforms and only need to be modified Render .

Project structure

Ignoring some structures that are not directly related to the game

. ├──model│   ├──brick.js│   ├──game.js│   └──index.js├──utils│   ├──buildenum.js│   ├── deepcopy.js│   ├──getshape.js│   ├──index.js│   ├──lineindex.js│   ├──matrixstring.js│   └── rotatearray.js├──enum│   ├──gametype.js│   ├──index.js│   └──pointtype.js├──data│   └── shapes.js├──controller│   └──index.js└──view    ├──rendercanvas.js    └──index.js

The index.js in each directory is for the convenience of referencing multiple files at the same time, roughly like this:

default as Model1} from './model1 'default as Model2} from './model2 '

Then we can write in the places we use:

Import {model1, model2} from './xxx '

Model

This is where the core logic of the game lies.

A matrix game like Tetris, the most appropriate way to store data is a two-dimensional array.
To be more intuitive, we chose the height of the game as the length of the first layer array:

New Array (height). Fill (new  Array (width))//  width:2 height:4[  1, 1  ],  1, 1],  1, 1],  1, 1]

And this choice is more convenient for some logical processing:

    1. When we move down, we just change the first level subscript of the element.
    2. To determine whether a bottom is being touched, we only need to determine if there are elements in the current subscript + 1

We define the elements in the array:

    • 0: Empty, indicating that the current coordinates are blank
    • 1: A new block that represents the currently active block
    • 2: Old blocks, blocks that have been fixed to the bottom

Next, we have a problem with how to handle the placement of blocks.
We know that the game will keep loading new squares into the board.
If we remove the corresponding block element in the current two-dimensional array every time we move down, it's too cumbersome to plug it into a new position.

So we initialize two two-dimensional arrays when we initialize the data.
When we load a new block, we plug the element corresponding to the block into one of the two-dimensional arrays.
then wait until we have other operations, such as moving around, down, and so on.
We use the second two-dimensional array directly to overwrite the current array, and then plug the changed subscript block into the array.
so on the data, we have finished moving the block.

class Game {  init () {    //  initialize two matrices    this. Matrix = [[], []]     this. Oldmatrix = [[], []]  }  Move () {    //  Reset Current matrix data    this. Matrix = deepcopy (this//   dereference    //  load block data this    . matrix[y][x1] = 1 This    . matrix[y][x2] = 1  }}

Handling of left and right moves

The movement of the left and right cannot be as moving downward as the simple subscript +1.
We need to determine if the current operation is valid.
For example, if the right side encounters an obstacle or reaches the edge, we must not be able to move again.

// Blend is the shape description of the active brick [[1, 1, 1], [0, 1, 0]] similar to this structure if (  >= width-brickwidth | | = = {    = oldmatrix[y + rowIndex    ]return row && row[    BrickWidth-1] && _pos && _pos[x + Brickwidth]  }))  return//  obstruction on right side, unable to move

Use logic like this to make sure that the current block does not overwrite the previous block when it is moved to the right.

Fast-down processing

I see some games implemented, seemingly the drop trigger is just a faster descent (this situation only needs to change the speed of the timing drop)-.-Here is the realization that the direct contact bottom

So you will encounter a problem, where can the current bricks drop to a maximum?

[111][0< Span class= "token punctuation", 00][020][ 222"           

Just like this data, both 0|2 columns can move down two columns, but this results in overlapping of the middle column.
We must take out the value that has the lowest descending amplitude.
So we have to figure out the last line 1 subscript and the first line 2 subscript, subtract the two subscript, the minimum value is our current block can fall distance.

Handling of rotating blocks

Rotating blocks should be a more complex piece of logic in the game.
It's not just a simple way to change a two-dimensional array of squares from rows to columns, and sometimes we need to determine if a block can be rotated.

Like this, the green strip in the middle is not able to rotate.
So we have to get the rotated data to compare with the data in the current game, to check if there is overlap, and if it does, it means that it cannot be rotated.

Touch Bottom Detection

After each move, we need to check the bottom of the block.
That is, the current block, if there is already an element placeholder, if any, it is already touched bottom, the current element will be fixed into the matrix array.
Similarly, when we judge, we do not need to check all the subscript of the block, only need to check the bottom layer of the valid elements.

[1, 1],[0, 1],[0, 1],

For a block like this, we only need to determine whether the second row of the first column & the fourth row of the second column has elements to complete the check.

Remove rows from

When a row is filled with elements, we need to remove it.
If a block is pinned into the array after the touch bottom detection is triggered, we can then remove the row.
Because if there is no new block entry, this step of removing the line is not necessary.
At the same time, the score count should also be done here, we will record the number of rows removed, and the number of rows obtained is the score.

At this point, all operations on the matrix data are ended.
GameObjects only maintain such a two-dimensional array, the object itself does not contain any game-related operations, only the corresponding processing when it is called.
A new two-dimensional array is then generated.

Utils

Here are some of the more common methods used to improve the efficiency of development.
For example, to get the bottom level of the block, such as the subscript tool functions.

Enum

It holds the enumeration of States, the state of the game, and the status of the block, similar to the data:

{  0,  1,  2}

Data

Stores the various blocks of information used in the game.
Squares, ladders, and the like in a two-dimensional array of the corresponding description.

Controller

That's what we're talking about, the module used to interact with the user, Controller to get the game-related information, and invoke the Render rendering.
Listen for keyboard events and render some control buttons on the page.
and Game The drop method of timed triggering.

View

The rendering part of the game interface, currently selected is used canvas , so only wrote RenderCanvas .
In this part of the rendering, a little bit of optimization is done to separate the active blocks from the fixed blocks.
This will not re-render the entire game layout when the user moves up or down, rather than rendering the active block canvas .

Xiao Kee

Two days more time to develop, which has half a day in the repair FlowType of warning tips ...
After the end, I feel that the main difficulty in achieving this is the square rotation & touch bottom judgment here.
Can clearly manage the game corresponding to the two-dimensional array, this game development will be very smooth.

The interface has yet to be optimized.

Using JavaScript to implement a Tetris

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.