JavaScript version of Tetris-Reconstruction

Source: Internet
Author: User
As mentioned in the JavaScript version of Tetris, the project structure and many names are chaotic due to temporary start. In addition, scoring and other functions are not implemented. This time, scoring and speed settings were implemented, and a simple reconstruction was conducted before that.

Rebuilding the project structure

In the project structure, the original app is renamed to src, indicating that both the script and the less source code are here. Of course, the app/src that originally stores the script source code is also renamed src/scripts.

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

In addition, the module is subdivided in the base scripts, and two subdirectories, model and RIS, are created during the reconstruction process.

Structure Analysis

A simple structure analysis was performed before the reconstruction, mainly by dividing several modules and placing them under the model directory. The tetris directory is created in the process of refactoring and writing new functions. The function class and auxiliary class are put here. However, the main function is in scrits/tetris. js.

The figure shown at the beginning of model analysis is as follows:

Reconstruction

Writing programs and refactoring are always very necessary, but also very error-prone. The entire reconstruction process of Tetris can be seen from the submit log of the working branch in the source code.

The most important thing about refactoring is to change the code structure without changing the logic. That is to say, the Code must be modified on the basis of the original business logic for every reconstruction. Although this is not 100%, we must do our best to follow this principle, will not generate inexplicable bugs in the refactoring process. This point should be mentioned in refactoring to improve the design of existing code.

Although the principle of changing the code without changing the logic is mentioned in the book refactoring to improve the design of existing code, we recommend that you take a look at this book. Refactoring plays an important role in development. However, many design patterns are involved in the refactoring process, so the design patterns also need to be read.

Private member

During the refactoring process, I added a private member definition for all classes. The purpose of this operation is to prevent users from accidentally accessing the members they are not allowed to access. (This generally means that users are not allowed to access the members they are not allowed to access. However, when users are not authorized to access the members, this may cause errors ).

I have discussed the topic of Private Members in simulating the Symbol of ES6 in ES5. Here I didn't use the method mentioned in that blog, but directly used Symbol. Babel is compatible with Symbol (). If it is in a browser that supports Symbol, it will directly use the Symbol of ES6; not supported, it is replaced by a simulated Symbol implemented by Babel.

The code added to the private member seems a bit strange, such as the following simple Point class code. The following implementation mainly aims to (whenever possible) Ensure that the Point object is generated, and its coordinates cannot be changed at Will-Immutable.

const __ = {    x: Symbol("x"),    y: Symbol("y")};export default class Point {    constructor(x, y) {        this[__.x] = x;        this[__.y] = y;    }    get x() {        return this[__.x];    }    get y() {        return this[__.y];    }    move(offsetX = 0, offsetY = 0) {        return new Point(this.x + offsetX, this.y + offsetY);    }}

Models

Only several classes under scripts/model are pure models. Apart from the Field used to store data and the Property used to access data, methods are also used to access data.

Point and BlockPoint, inherited

Model/point. js and model/blockpoint. js implement two classes used to describe points (small blocks). The difference is that BlockPoint has one more color attribute. In fact, BlockPoint is a subclass of Point. It is too easy to implement inheritance in ES6. below is the structure diagram of the two classes.

class Point {    constructor(x, y) {        // ....    }}class BlockPoint extends Point {    constructor(x = 0, y = 0, c = "c0") {        super(x, y);        // ....    }}

Note the following two key points for implementing pipeline:

Implement inheritance by using the extends keyword

If constructor is defined in the subclass, remember to call the constructor super (...) of the parent class in the first sentence (...). Javaer should be familiar with this requirement.

Form

Form does not mean "Form", but "Shape and Shape". It represents a maximum of four forms formed by rotation of a square image (Shape, each Form object is one of them. Therefore, Form is actually composed of a group of points.

The data structure of Form is not defined in the previous version. It is an anonymous object generated when the Shape is generated. That piece of code looks like a special wrap, although it can also extract a function, but now it is generated through the Form class constructor, not only achieves the same purpose, it also encapsulates the height of width.

Shape and SHAPES

Shape and SHAPES are slightly different from the original Shape. The SHAPES Generation Code simplifies a lot by defining the Form class. After the Shape class is built, color and forms cannot be changed and can only be obtained due to the privatization of members.

Game-related classes in RIS

In addition to a few purely model classes placed in the model, the main entry index. js and tetris. js are placed under the script source code root directory, and other game-related classes are placed under the tetris directory. This is just a basic division of the source code using the concept of package (Java concept) or namespace (C ++/C # concept.

Block and BlockFactory

A Block represents a large Block, which consists of four small blocks. Its Prototype (this Prototype is not JS Prototype) is Shape. Therefore, a Block has a reference to the Shape prototype, and stores the position and form formIndex of the current Block. These two attributes can be changed during the game, it directly affects the position and appearance of the Block.

There are actually only two blocks in the game. One is in the preview area, and the other is in the game area and controlled by the player.

The Block object is no longer a Block after it falls into the game zone. Why is it designed like this? Because Block represents a complete big Block, and once the square below the game area fills up a row, it will be eliminated and the big Block will be incomplete again. There are two solutions to this problem:

You can still place the large block object in it, but mark the removed block so that the deleted block is not drawn during painting.

After a large block falls, it is split into blockpoints and managed through a matrix.

Obviously, the second method is more intuitive and easier to write by using a two-dimensional array. So I chose the second method.

In addition to describing the location and form of a large Block, the Block also works with game control to perform some data operations and changes, such as location changes: moveLeft (), moveRight (), moveDown (), etc, and form changes rotate (); there are several fastenXxxx methods that generate BlockPoint [] for drawing or determining whether the next position can be placed. This has been discussed in the JavaScript version of Tetris.

The BlockFactory function remains unchanged and a random block is generated.

Puzzle and Matrix

Previously, the definitions of Puzzle and Matrix were a bit confusing. Here they are separated.

Puzzle is used to draw a browsing area and a preview area. In addition to describing a drawing area with a specified length and width, it also stores two important objects: block: Block and fastened: BlockPoint []. that is, the blocks in the motion mentioned above, and several fixed small blocks.

Puzzle does not maintain block and fastened, but it needs to plot all blockpoints in the two important data objects.

Matrix is no longer a class, it is two data. One is the matrix attribute in Puzzle, which maintains

The second is the matrix attribute in Tetris, which maintains a matrix of BlockPoint, that is, the matrix form of Puzzle: fastened, it is easier to change through operations such as solidification or deletion.

Because Tetris: matrix remains unchanged for most of the time, you only need to list the non-empty parts in the Puzzle plot. Therefore, there is a better business logic here: when the RIS: matrix changes, the Puzzle: fastened is regenerated from it, which is used for drawing by Puzzle.

Eventable

In the process of refactoring and writing new functions, the importance of events is discovered, and events are used for better processing.

For example, when you click pause/resume and start again, you need to determine the status of the current game and trigger whether the current game is actually paused or restarted Based on the status.

For example, in the scoring and speed selection functions, if the scoring reaches a certain level, the acceleration needs to be triggered.

All of the above mentioned can be designed using the observer pattern, so events are a typical Implementation of the observer pattern. It is not difficult to implement your own event processing mechanism, but you can use jQuery's event processing function to handle events. Therefore, the Eventable class is defined to encapsulate jQuery's event processing, all business classes that support events can inherit from them.

Encapsulation is very simple. Here we use the method of encapsulating the event proxy object. For more information, see the source code. There are only over 20 lines in total, which is easy to understand. You can also encapsulate a jQuery object in the constructor to process the event. In this way, you can point this in the event processing function to your own (Eventable object by yourself ). But fortunately, this project does not need to care about this in the event processing function.

StateManager

When implementing the main game logic in RIS, it is found that status management is not simple, especially when the pause/resume button is added, the pause status can be divided into code pause and manual pause, recovery Operations are also different in two cases. In addition, the game has ended ...... Therefore, a StateManager is defined to manage the status.

StateManager maintains the game status, provides methods for changing the status, and also provides the attribute for determining the status. If JavaScript has the interface syntax, this interface is probably like this

interface IStateManager {    get isPaused(): boolean;    get isPausedByManual(): boolean;    get isRestartable(): boolean;    get isOver(): boolean;    pause(byWhat);    resume(byWhat);    start();    over();}

InfoPanel and CommandPanel

InfoPanel is mainly used for point and speed management, including user interaction (UI ). CommandPanel is responsible for processing two button events.

Tetris

To be honest, I still think the code of RIS is a bit complicated and needs to be restructured and simplified. However, after trying it, I found that this is not an easy task, so I will leave it for later versions to handle it.

Summary

This restructuring of Tetris is only a preliminary restructuring. The initial goal is to clearly define the model, but also to split the business processing. The purpose of the model definition is achieved, but the business splitting is still unsatisfactory.

The previous two projects used TypeScript 1.8. Although TypeScript 1.8 has some pitfalls, the static language features of TypeScript, in particular, static checks are very helpful for large JavaScript projects. I have always thought that TypeScript has increased the amount of code and reduced the flexibility of JavaScript. However, this time I used ES6 to reconstruct the Tetris game, which is not a disadvantage of TypeScript, it can solve at least the following problems in JavaScript:

Static checks can detect many potential problems in the development phase, rather than problems during running. You know, the sooner you find the problem, the easier it is to change.

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.