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

MySQL trigger trigger add, delete, modify and query operation example

This article uses examples to describe the add, d...

Tutorial on how to use profile in MySQL

What is a profile? We can use it when we want to ...

W3C Tutorial (6): W3C CSS Activities

A style sheet describes how a document should be ...

Detailed explanation on how to get the IP address of a docker container

1. After entering the container cat /etc/hosts It...

HTML table cross-row and cross-column operations (rowspan, colspan)

Generally, the colspan attribute of the <td>...

Vue elementUI implements tree structure table and lazy loading

Table of contents 1. Achieve results 2. Backend i...

Vue2.x - Example of using anti-shake and throttling

Table of contents utils: Use in vue: explain: Ima...

uniapp Sample code for implementing global sharing of WeChat mini-programs

Table of contents Create a global shared content ...

Nginx try_files directive usage examples

Nginx's configuration syntax is flexible and ...

Centos7 installation of MySQL8 tutorial

MySQL 8 new features: My personal opinion on MySQ...

CSS code to achieve 10 modern layouts

Preface I watched web.dev's 2020 three-day li...

Comparing the performance of int, char, and varchar in MySQL

There are many seemingly true "rumors" ...

HTML Basic Notes (Recommended)

1. Basic structure of web page: XML/HTML CodeCopy...

6 ways to view the port numbers occupied by Linux processes

For Linux system administrators, it is crucial to...

Example of using CSS3 to achieve shiny font effect when unlocking an Apple phone

0. Introduction August 18, 2016 Today, I noticed ...