JavaScript canvas Tetris game

JavaScript canvas Tetris game

Tetris is a very classic little game, and I also tried to write it. But I want to implement it with code that is as concise and logical as possible. There is no need for too much code to record the model of the falling blocks, or to record the x and y of each falling block. I thought about the following ideas, and then found that it is very concise to write like this.

There are 7 basic models of Tetris:

There are many ways to record these models, such as recording their relative positions, recording the x, y coordinates of each block, etc. I came up with an idea to record these 7 models. It is very concise and easy to use when writing left shift, right shift and rotation functions. The following array records these models.

var cubeArr=[[6,7,12,13],[7,8,11,12],[6,7,11,12],[7,12,17,8],[7,12,16,17],[7,12,17,22],[7,11,12,13]];

Ideas:

A 5*5 table, numbered starting from 0. The point numbered 12 is the center point. Each model is recorded with its label, for example, the first model is [6,7,12,13].

Taking the upper left corner of the table as the reference point, there is such a rule: assuming that the number in the table is value, the remainder of value divided by 5 is the x offset of the point relative to the reference point, and the integer part of value divided by 5 is the y offset of the point relative to the reference point. It is also very simple to rotate. By rotating around the center 12, you can also find some patterns.

var movex=cubeNow[i]%5;
var movey=Math.floor(cubeNow[i]/5);

Draw a model with a loop

function drawEle(color)
    {
        ctx.fillStyle=color;
        ctx.strokeStyle='#fff';
        for(var i=0;i<4;i++)
        {
            var movex=downInfor.cubeNow[i]%5;
            var movey=Math.floor(downInfor.cubeNow[i]/5);
            ctx.fillRect(cubeW*(downInfor.point[0]+movex),cubeW*(downInfor.point[1]+movey),cubeW,cubeW);
            ctx.strokeRect(cubeW*(downInfor.point[0]+movex),cubeW*(downInfor.point[1]+movey),cubeW,cubeW)
        }
} 

Rotate the model:

The relationship between the current position and the next rotation position can be easily realized through this array to rotate the model. For example, if the position is numbered 0 and we rotate clockwise, the next position is 4. The position numbered 6, the next position is 8. The following array can find the next position from the previous position.

var rotateArr=[4,9,14,19,24,3,8,13,18,23,2,7,12,17,22,1,6,11,16,21,0,5,10,15,20];

Code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Tetris</title>
</head>
<body>
<div>
    <div style="display:inline-block">
     <canvas id="can" height="480" width="300" style="border:3px solid black;"></canvas>
    </div>
    <div id="info" style="display:inline-block;height:600px;vertical-align: top;font-family: tmb; font-size:14pt; color:green;">
    <span>Score:</span><span id="score">0</span>
     </div>
</div>
<script type="text/javascript">
    var cubeW=20;
    var cubeArr=[[6,7,12,13],[7,8,11,12],[6,7,11,12],[7,12,17,8],[7,12,16,17],[7,12,17,22],[7,11,12,13]];
    var colorArr=['#ffc0cb','#dda0dd','#9370db','#6495ed','#fa8072','#ff8c00','#008000'];
    var rotateArr=[4,9,14,19,24,3,8,13,18,23,2,7,12,17,22,1,6,11,16,21,0,5,10,15,20];
    var canvas = document.getElementById('can');
    var ctx = canvas.getContext('2d');
    var score = document.getElementById('score');
    var canWidth=canvas.width;
    var canHeight=canvas.height;
    var downInfor={}, staticCube=[];
    var myinter;
    window.initialize = function() //initialize {
        drawline();
        for(var i=0;i<(canWidth/cubeW);i++)
        {
            staticCube[i]=[];
            for(var j=0;j<(canHeight/cubeW);j++)
            {
                staticCube[i][j]=0;
            }
        }
        initCube();
        myinter=setInterval('movedown()',200); //The first parameter should be quoted}
    function drawline()
    {
        ctx.lineWidth=1;
        ctx.strokeStyle='#ddd';
        for(var i=0;i<(canWidth/cubeW);i++)
        {
          ctx.moveTo(cubeW*i,0);
          ctx.lineTo(cubeW*i,canHeight);
        }
        ctx.stroke();
        for(var j=0;j<(canHeight/cubeW);j++)
        {
            ctx.moveTo(0,cubeW*j);
            ctx.lineTo(canHeight,cubeW*j);
        }
        ctx.stroke();
    }
    function initCube()
    {
           var index=Math.floor(Math.random()*cubeArr.length);//randomly generate downInfor.cubeNow=cubeArr[index].concat();
            downInfor.index=index;
            downInfor.prepoint=[5,-1];
            downInfor.point=[5,-1];
            drawEle(colorArr[downInfor.index]);
    }
    function movedown()
    {
        //Judge whether the next position is reasonablevar length,isempty=true,px,py,movex,movey,max=0;
        for(var i=0;i<4;i++)
        {
            if(max<downInfor.cubeNow[i])
                max=downInfor.cubeNow[i];
        }
        length=Math.ceil(max/5);
        for(var i=0;i<4;i++)
        {
            px=downInfor.point[0]+downInfor.cubeNow[i]%5;
            py=downInfor.point[1]+1+Math.floor(downInfor.cubeNow[i]/5);
            if(staticCube[px][py]==1)
            {
                isempty=false;
                break;
            }
        }
        if((downInfor.point[1]+length)<(canHeight/cubeW)&&isempty)
        {
            downInfor.prepoint=downInfor.point.concat(); //Note the reference type downInfor.point[1]=downInfor.point[1]+1;
            clearEle();
            drawEle(colorArr[downInfor.index]);
        }
        else //When unable to move {
            for(var i=0;i<4;i++)
            {
                px=downInfor.point[0]+downInfor.cubeNow[i]%5;
                py=downInfor.point[1]+Math.floor(downInfor.cubeNow[i]/5);
                staticCube[px][py]=1;
            }
            drawEle('#555');
            checkfullLine();
        }
 
    }
    function moveLeft()
    {
        var leftroom=4,isempty=true,px,py,movex,movey;
        for(var i=0;i<4;i++)
        {
            px=downInfor.point[0]-1+downInfor.cubeNow[i]%5;
            py=downInfor.point[1]+Math.floor(downInfor.cubeNow[i]/5);
            if((downInfor.cubeNow[i]%5)<leftroom)
                leftroom=downInfor.cubeNow[i]%5;
            if(staticCube[px][py]==1)
            {
                isempty=false;
                break;
            }
        }
        if((downInfor.point[0]+leftroom)>=0&&isempty)
        {
            downInfor.prepoint=downInfor.point.concat();
            downInfor.point[0]=downInfor.point[0]-1;
            clearEle();
            drawEle(colorArr[downInfor.index]);
        }
    }
    function moveRight()
    {
        var rightroom=0,isempty=true,px,py,movex,movey;
        for(var i=0;i<4;i++)
        {
            px=downInfor.point[0]+1+downInfor.cubeNow[i]%5;
            py=downInfor.point[1]+Math.floor(downInfor.cubeNow[i]/5);
            if((downInfor.cubeNow[i]%5)>rightroom)
                rightroom=downInfor.cubeNow[i]%5;
            if(staticCube[px][py]==1)
            {
                isempty=false;
                break;
            }
        }
        if((downInfor.point[0]+rightroom+1)<(canWidth/cubeW)&&isempty)
        {
            downInfor.prepoint=downInfor.point.concat();
            downInfor.point[0]=downInfor.point[0]+1;
            clearEle();
            drawEle(colorArr[downInfor.index]);
        }
    }
    function moverotate()//process rotation {
        var isempty=true,px,py,movex,movey, tempRotate=[0,0,0,0];
        for(var i=0;i<4;i++)
        {
            tempRotate[i]=rotateArr[downInfor.cubeNow[i]];
        }
        for(var i=0;i<4;i++)
        {
            px=downInfor.point[0]+tempRotate[i]%3;
            py=downInfor.point[1]+Math.floor(tempRotate[i]/3);
            if(staticCube[px][py]==1)
            {
                isempty=false;
                break;
            }
        }
        if(isempty)
        {
            downInfor.prepoint=downInfor.point.concat();
            clearEle();
            downInfor.cubeNow=tempRotate.concat();
            drawEle(colorArr[downInfor.index]);
        }
 
    }
    function drawEle(color)
    {
        ctx.fillStyle=color;
        ctx.strokeStyle='#fff';
        for(var i=0;i<4;i++)
        {
            var movex=downInfor.cubeNow[i]%5;
            var movey=Math.floor(downInfor.cubeNow[i]/5);
            ctx.fillRect(cubeW*(downInfor.point[0]+movex),cubeW*(downInfor.point[1]+movey),cubeW,cubeW);
            ctx.strokeRect(cubeW*(downInfor.point[0]+movex),cubeW*(downInfor.point[1]+movey),cubeW,cubeW)
        }
    }
    function clearEle()
    {
        ctx.lineWidth=1;
        ctx.strokeStyle='#ddd';
        for(var i=0;i<4;i++)
        {
            var movex=downInfor.cubeNow[i]%5;
            var movey=Math.floor(downInfor.cubeNow[i]/5);
            ctx.clearRect(cubeW*(downInfor.prepoint[0]+movex),cubeW*(downInfor.prepoint[1]+movey),cubeW,cubeW);
            ctx.strokeRect(cubeW*(downInfor.prepoint[0]+movex),cubeW*(downInfor.prepoint[1]+movey),cubeW,cubeW)
        }
    }
    function checkfullLine()//Check if there is a full line {
        var isFullLine=true,index=0,changeScore=false;
        for(var i=0;i<canWidth/cubeW;i++)
        {
            if(staticCube[i][0]==1)
            {
                alert('Game over!');
                clearInterval(myinterval);
                return;
            }
        }
        for(var i=canHeight/cubeW-1;i>=0;i--)
        {
            isFullLine=true;
            for(var j=0;j<(canWidth/cubeW);j++)
            {
                if(staticCube[j][i]==0)
                {
                    isFullLine=false;
                }
            }
            if(isFullLine)//Add one point {
                score.innerHTML=parseInt(score.innerText)+1;
                changeScore=true;
                for(var s=i;s>=0;s--) {
                    for (var j = 0; j < (canWidth / cubeW); j++) {
                        (s- 1) >= 0 ? staticCube[j][s] = staticCube[j][s - 1] : staticCube[j][s] = 0;
                    }
                }
            }
        }
        if(changeScore)
        {
            ctx.clearRect(0,0,canWidth,canHeight);
            drawline();
            ctx.fillStyle='555';
            ctx.strokeStyle='#fff';
            for(var i=0;i<(canWidth/cubeW);i++)
            {
                for(var j=0;j<(canHeight/cubeW);j++)
                {
                    if(staticCube[i][j]==1)
                    {
                        ctx.fillRect(cubeW*i,cubeW*j,cubeW,cubeW);
                        ctx.strokeRect(cubeW*i,cubeW*j,cubeW,cubeW);
                    }
                }
            }
        }
        initCube();
    }
    window.onkeydown=function (evt)
    {
       switch(evt.keyCode)
       {
           case 37: //leftmoveLeft();
               break;
           case 38: //moverotate();
               break;
           case 39: //right moveRight();
               break;
           case 40: //movedown();
               break;
       }
    }
</script>
</body>
</html>

Effect:

The above is the full content of this article. I hope it will be helpful for everyone’s study. I also hope that everyone will support 123WORDPRESS.COM.

You may also be interested in:
  • Ideas and methods for implementing Tetris game in javascript
  • JS Tetris, including complete design concept
  • JS Tetris perfectly commented code
  • JAVASCRIPT code to write Tetris web version
  • 60 lines of js code to implement Tetris
  • Use JS code to implement Tetris game
  • JavaScript implements a simple Tetris complete example
  • Analysis and source code sharing of Tetris game implemented in JavaScript
  • A complete example of Tetris game implemented by JS+Canvas
  • js html5 css Tetris game reappearance

<<:  Implementation steps for building a MySQL master-slave replication environment based on Docker

>>:  Detailed steps to upgrade mysql8.0.11 to mysql8.0.17 under win2008

Recommend

CSS Naming: BEM, scoped CSS, CSS modules and CSS-in-JS explained

The scope of css is global. As the project gets b...

Springboot+Vue-Cropper realizes the effect of avatar cutting and uploading

Use the Vue-Cropper component to upload avatars. ...

Detailed process of modifying hostname after Docker creates a container

There is a medicine for regret in the world, as l...

Linux kernel device driver virtual file system notes

/******************** * Virtual File System VFS *...

Building an image server with FastDFS under Linux

Table of contents Server Planning 1. Install syst...

MySQL tutorial data definition language DDL example detailed explanation

Table of contents 1. Introduction to the basic fu...

Teach you to create custom hooks in react

1. What are custom hooks Logic reuse Simply put, ...

MySQL 8.0 Window Function Introduction and Summary

Preface Before MySQL 8.0, it was quite painful to...

CSS3 analysis of the steps for making Douyin LOGO

"Tik Tok" is also very popular and is s...

CocosCreator implements skill cooling effect

CocosCreator realizes skill CD effect There are s...

Why Seconds_Behind_Master is still 0 when MySQL synchronization delay occurs

Table of contents Problem Description Principle A...

Use Shell scripts to batch start and stop Docker services

Table of contents Start Docker Stop Docker Python...

Vue implements a simple shopping cart example

This article example shares the specific code of ...