background
In tossing ES6, I suddenly remembered that I wrote Tetris in C language when I was in college. In this project, I mainly used the class feature of ES6 for object-oriented programming. Project adoption node.js V6.2.0 + electronic V1.1.0 can run across all platforms.
thinking
-
Full application of the object-oriented design idea makes the function cohesive.
-
Think of the seven squares as separate “biological” objects, allowing them to “see” the world around them.
-
Instead of using the traditional large two-dimensional array to represent the state of the game scene, Tetris is allowed to “see” itself.
-
Using HTML 5 canvas to complete, more like CGI programming.
-
Using the least canvas feature, we only use fillRect, strokerect, getimagedata, clearrect and other functions.
design sketch
The highest record I played^_^
Operation method
Project adoption node.js For desktop development with v6.2.0 + electronic V1.1.0, please install the relevant system first:
npm install electron-prebuilt -g
Note: the scheme adopted in this project can run across all platforms. In case of permission problem, please add sudo on the command line.
Source code:
git clone https://git.oschina.net/zhoutk/Tetris.git
Or:
git clone https://github.com/zhoutk/Tetris
Enter project directory:
cd Tetris
Operation procedure:
electron .
Critical code analysis
The functions are as cohesive as possible. The class block encapsulates all the operations of small blocks, and the canvas interface function is basically encapsulated in this class; the Tetris class combines block and encapsulates most of the operations of Tetris.
Block class (small block class)
class Block{
constructor(ctx,fillColor,strokeColor){
this.ctx =CTX; // Canvas object
this.width =Blockwidth; // side length of small square
this.fillColor =Fillcolor |'Blue '; // direct color
this.strokeColor =Strokecolor |'white '; // stroke color
}
Draw (x, y) {// draw block
this.ctx.save();
this.ctx.fillStyle = this.fillColor;
this.ctx.fillRect(x*this.width + 1,y*this.width + 1,this.width-2,this.width-2)
this.ctx.strokeStyle = this.strokeColor;
this.ctx.strokeRect(x*this.width + 1,y*this.width + 1,this.width-2,this.width-2);
this.ctx.restore();
}
Erase (x, y) {// erase small square
this.ctx.clearRect(x*this.width , y*this.width , 30, 30)
}
Cansee (x, y) {// check if a location is empty
let c = this.ctx.getImageData(x*this.width+9,y*this.width+9,1,1)
return c.data[0] | c.data[1] | c.data[2] | c.data[3];
}
Getcolor (x, y) {// takes the color at a certain position
let c = this.ctx.getImageData(x*this.width+9,y*this.width+9,1,1)
return 'rgba('+c.data[0]+','+c.data[1]+','+c.data[2]+','+c.data[3]+')';
}
}
Tetris class (Tetris class)
class Tetris {
constructor(shape,ctx,x,y){
this.data =[[0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0]]; // block shape data
this.shape =Shape | 0; // block shape code
this.ctx =CTX; // Canvas object
This. X = x | 0; // block position data
this.y = y || 0;
this.block =New block (CTX, colors [shape]); // combines block objects
for(let i = 0; i < SHAPES[ this.shape ]. length; I + +) {// square shape initialization
if(SHAPES[this.shape][i]){
this.data[i % 4][1 + Math.floor(i/4)] = 1;
}
}
}
Clean up () {...} // eliminate the layer, score
Movenext() {...} // move the square down one space
Moveleft() {...} // move the square one space to the left
Moveright() {...} // move the square to the right one space
Movedown() {...} // the square moves to the end
Rotate() {...} // square rotation
Candrawnext() {...} // check whether the new square can be placed or not, and detect the end of the game
Draw() {...} // call the block object to draw Tetris
Erase() {...} // call the block object to erase the Tetris
Cansee {...} // calls the block object to detect the placement of Tetris
Block.canSee
Select the color attribute of the pixel to simulate the “vision” of the box.
canSee(x,y){
let c = this.ctx.getImageData(x*this.width+9,y*this.width+9,1,1)
Return c.data [0] | c.data [1] | c.data [2] | c.data [3]; // black is all zero. When XOR is 0, the position is empty, and others indicate that the position has been occupied.
}
Tetris.cleanup
It is complicated to eliminate the formation, so it should be noted.
cleanup(){
Let H = 19, levelcount = 0; // eliminate the level statistical variables at one time
While (H > = 0) {// from the bottom to the top
Let count = 0; // record the number of positions above the same level
For (let I = 0; I < 10; I + +) {// traverse one layer
If ( this.canSee (I, H)) {// position is empty, variable plus one
count++;
}
}
If (count = = 0) {// the layer is full and needs to be eliminated
Let level = h; // layer to be eliminated
Levelcount + +; // add one to the number of elimination layers
SOUNDS['score'].play();
While (level > = 0) {// move all layers above the layer to be eliminated one layer down as a whole
Let CT = 0; // record the number of positions above the same level
for(let j = 0; j < 10; j++){
this.block.erase (J, level); // clear the grid of the layer to be eliminated
If ( this.canSee (J, level-1)) {// null location statistics
ct++;
}else{
Let BK = new // take the color block of the upper vertical grid( this.ctx , this.block.getColor (j,level-1))
bk.draw (J, level) // move down
}
}
If (CT = = 10) {// the first floor is empty, and the whole move down is completed ahead of time.
break;
}else{
Level --; // floor up
}
}
}Else if (count = = 10) {// the first floor is empty, and the layer elimination is completed ahead of time.
break;
}else{
h--;
}
}
return levelCount;
}
Tetris.moveNext
It’s more complicated to move the box down one level, with comments.
moveNext(){
Let flag = true; // variable set for jumping out of double loop
For (let I = 0; I < 4; I + +) {// check whether it can be moved down
for(let j = 0; j < 4; j++){
if(this.data[i][j] && (j ==3 || this.data[i][j+1] == 0)){
if(!this.canSee(this.x + i, this.y + 1 + j)){
Flag = false; // it's finished
break;
}
}
}
if(!flag){
break;
}
}
If (flag) {// move down
this.erase();
this.y++;
this.draw();
return true;
}Else {// final processing
let level = this.cleanup (); // layer elimination
If (level > 0) {// the number of elimination layers is greater than zero
Levels + = level; // scoring
scores += LVSCS[level]
document.getElementById('levelShow').value = levels;
document.getElementById('scoreShow').value = scores;
If ( Math.floor (scores / stepval)! = step) {// speed adjustment level
clearInterval(interval)
interval = setInterval( tick, TICKVAL - ++STEP * STEPVAL );
document.getElementById('speedShow').value = STEP + 1;
}
}else{
SOUNDS['down'].play()
}
return false;
}
}
Operation and rules
-
Up key: rotate
-
Direction left key: move left
-
Right click: move right
-
Direction down key: move down
-
Space bar: move down to the bottom
-
Scoring: 1 point for the first floor, 3 points for the second floor, 3 points for the third floor and 10% for the fourth floor
-
The speed is divided into ten levels, and the difference of each level is 50ms
Summary
The project is now in v1.0.0 version, complete all the basic functions of Tetris game, with sound effects. In the future, I will consider network war, man-machine war and machine self war. I mainly want to do artificial intelligence experiments, from let the algorithm play Tetris!