(四)使用HTML5实现的俄罗斯方块

俄罗斯方块
placeholder image
admin 发布于:2016-04-10 01:14:42
阅读:loading

实现介绍

从java版俄罗斯方块之后,总想着使用html5的canvas也来实现一下,除了一些运算之外就剩下一些表格的绘制了,所以觉得html5写一个出来是可行的,这不最近终于开始写了。

首先分析一下本次实现的功能点:

1、游戏布局面板格子绘制;

2、 7种方块的绘制;

3、方块的上、下、左、右 (字母WASD键)控制,上为旋转,左右下分别为移动,空格键为快速下落(下落到最底部);

4、方块最终的可下落位置预览提示;

5、方块的消行操作(本次消除N行);

以上这些功能为核心功能,与之前java版的功能实现相比,少了下列几个功能:

1)消行计分,等级提升,背景切换;

2)方块自动下落,按照级别方块下落速度的提升;

3)游戏开始的手动执行,结束提示提示,游戏暂停,继续等;

4)下一个方块的提前预览;

。。。。。。突然发现以前的这个功能实现还是稍微有点完善的。

参考效果图

image.png

image.png

源码参考(最新的代码已经有更改,以附件为准)

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>俄罗斯方块</title>

<style type="text/css">

/*整个画布*/

#tetris{

    border6px solid grey;

}

/*游戏面板*/

 

</style>

</head>

<body>

    <canvas id="tetris" width="565" height="576"></canvas>

    <script type="text/javascript">

        var canvas = document.getElementById("tetris");

        var context = canvas.getContext("2d");

        var padding = 6 , size = 32 , minX = 0 , maxX = 10 , minY = 0 , maxY = 18 , score = 0 , level = 1;

        var gameMap = new Array();//游戏地图,二维数组

        initGameMap();

        //绘制垂直线条

        drawGrid();

       

        var arrays = basicBlockType();

        var blockIndex = getRandomIndex();

        //随机画一个方块意思意思

        var block = getPointByCode(blockIndex);

        context.fillStyle = getBlockColorByIndex(blockIndex);

        drawBlock(block);

       

        /**

         * 初始化游戏地图

         */

        function initGameMap(){

            for(var i=0 ; i < maxY ; i++){

                var row = new Array();

                for(var j=0 ; j < maxX ; j++){

                    row[j] = false;

                }

                gameMap[i] = row;

            }

        }

       

        /**

         * 方块旋转

         * 顺时针:

         * A.x = O.y + O.x B.y

         * A.y = O.y O.x + B.x

         */

        function round(){

            //正方形的方块不响应旋转   

            if(blockIndex == 4){

                return;

            }

            //循环处理当前的方块,找新的旋转点

            for(var i=1 ; i < block.length; i++){

                var o = block[0];

                var point = block[i];

                //旋转后的位置不能与现有格子的方块冲突

                var tempX = o.y + o.x - point.y;

                var tempY = o.y - o.x + point.x;

                if(isOverZone(tempX , tempY)){

                    return;//不可旋转

                }

            }

 

            clearBlock();

            //可以旋转,设置新的旋转后的坐标

            for(var i=1 ; i < block.length; i++){

                var o = block[0];

                var point = block[i];

                //旋转后的位置不能与现有格子的方块冲突

                var tempX = o.y + o.x - point.y;

                var tempY = o.y - o.x + point.x;

                block[i] = {x: tempX , y: tempY};

            }

            drawBlock();

        }

       

        function moveDown(){

           

            var flag = moveTo(0 , 1);

            //如果可以移动,则继续移动

            if(flag){

                return ;

            }

            //如果不能向下移动了,将当前的方块坐标载入地图

            add2GameMap();

            //进行消行动作

            clearLines();

            //清除游戏区域内的不同颜色的格子,使用单一颜色重新绘制地图堆积物

            redrawGameMap();

            //如果不能向下移动,则继续下一个方块

            nextBlock();

        }

       

        /**

         * 消行动作,返回消除的行数

         */

        function clearLines(){

            var clearRowList = new Array();

            for(var i=0 ; i < maxY ; i++){

                var flag = true;

                for(var j=0 ; j < maxX ; j++){

                    if(gameMap[i][j] == false){

                        flag = false;

                        break;

                    }

                }

                if(flag){

                    clearRowList.push(i);//记录消除行号的索引

                }

            }

           

            var clearRows = clearRowList.length;

            //所谓的消行就是将待消除行的索引,下方所有的格子上移动

            for(var x=0 ; x < clearRows ; x++){

                var index = clearRowList[x];

                for(var i=index ; i > 0 ; i--){

                    for(var j=0 ; j < maxX; j++){

                        gameMap[i][j] = gameMap[i-1][j];

                    }

                }

            }

            if(clearRows > 0){

                for(var i=0 ; i < maxY ; i++){

                    //此处可以限制满足相关条件的方块进行清除操作 && j < clearRowList[clearRows - 1]

                    for(var j=0 ; j < maxX ; j++){

                        if(gameMap[i][j] == false){

                            clearBlockByPoint(i, j);

                        }

                    }

                }

            }

        }

 

        /**

         * 重绘游戏地图

         */

        function redrawGameMap(){

            drawGrid();

            for(var i=0 ; i < maxY ; i++){

                for(var j=0 ; j < maxX ; j++){

                    if(gameMap[i][j]){

                        roadBlock(j , i);

                    }

                }

            }

        }

       

        /**

         * 打印阴影地图

         */

        function drawShadowBlock(){

            var currentBlock = block;

            var shadowPoints = getCanMoveDown();

            if(shadowPoints != null && shadowPoints.length > 0){

                for (var i=0 ; i < shadowPoints.length ; i++) {

                    var point = shadowPoints[i];

                    if(point == null){

                        continue;

                    }

                    var start = point.x * size;

                    var end = point.y * size;

                    context.fillStyle = "chartreuse";

                    context.fillRect(start , end , size ,size);

                    context.strokeStyle = "black";

                    context.strokeRect(start , end , size ,size);

                }

            }

        }

       

        /**

         * 返回最多可移动到的坐标位置(统计总共可以下落多少步骤)

         * @return 最多可移动到的坐标位置

         */

        function getCanMoveDown(){

            var nps = canMove(0, 1 , block);

            var last = null;

            if(nps != null){

                last = new Array();

                while((nps = canMove(0, 1, nps)) != null){

                    if(nps != null){

                        last = nps;

                    }

                }

            }

            return last;

        }

       

       

        function drawLevelScore(){

           

        }

       

        /**

         * 将不能移动的各种填充至地图

         */

        function add2GameMap(){

            for(var i=0 ; i < block.length; i++){

                var point = block[i];

                var x = point.x;

                var y = point.y;

                var gameMapRow = gameMap[y];//获取到地图的一行

                gameMapRow[x] = true;//将此行中的某个格子标记为堆积物

                gameMap[y] = gameMapRow;//再将行给设置回来

            }

        }

       

        function moveLeft(){

            moveTo(-1 , 0);

        }

       

        function moveRight(){

            moveTo(1 , 0);

        }

       

        function quickDown(){

            while(moveTo(0, 1));

        }

       

        function moveTo(moveX , moveY){

           

            var move = canMove(moveX , moveY , block);//判定是否可以移动

            if(move == null){

                return false;

            }

            clearBlock();

            for(var i=0 ; i < block.length; i++){

                var point = block[i];

                point.x = point.x + moveX;

                point.y = point.y + moveY;

            }

            drawBlock();

            return true;

        }

       

        /**

         * 下一个方块

         */

        function nextBlock(){

            blockIndex = getRandomIndex();

            block = getPointByCode(blockIndex);

            context.fillStyle = getBlockColorByIndex(blockIndex);

            drawBlock();

        }

       

        document.onkeypress = function(evt){

            var key = window .event ? evt.keyCode : evt.which;

            switch(key){

                case 119://向上旋转 W

                    round();

                    break;

                case 115://向下移动  S

                    moveDown();

                break;

                case 97://向左移动 A

                    moveLeft();

                break;

                case 100://向右移动 D

                    moveRight();

                break;

                case 32://空格键快速下落到底

                    quickDown();

                break;

            }

           

        }

       

        /**

         * 判定是否可以移动

         * @param moveX 横向移动的个数

         * @param moveY 纵向移动的个数

         */

        function canMove(moveX , moveY , currentBlock){

            var flag = true;

            var newPoints = new Array();

            for(var i=0 ; i < currentBlock.length; i++){

                var point = currentBlock[i];

                var tempX = point.x + moveX;

                var tempY = point.y + moveY;

                if(isOverZone(tempX , tempY)){

                    flag = false;

                    break;

                }

            }

            if(flag){

                for(var i=0 ; i < currentBlock.length; i++){

                    var point = currentBlock[i];

                    var tempX = point.x + moveX;

                    var tempY = point.y + moveY;

                    newPoints[i] = {x : tempX , y : tempY};

                }

                return newPoints;

            }

            return null;

        }

       

        /**

         * 判定是否可以移动

         * @param 预移动后的横坐标

         * @param 预移动后的纵坐标

         */

        function isOverZone(x , y){

            return x < minX || x >= maxX || y < minY || y >= maxY || gameMap[y][x];

        }

       

        document.body.click();

        /**

         * 初始化方块的基础数据

         */

        function basicBlockType(){

            var arrays = new Array();

            arrays[0] = [{x:4,y:0},{x:3,y:0},{x:5,y:0},{x:6,y:0}];

            arrays[1] = [{x:4,y:0},{x:3,y:0},{x:5,y:0},{x:4,y:1}];

            arrays[2] = [{x:4,y:0},{x:3,y:0},{x:5,y:0},{x:3,y:1}];

            arrays[3] = [{x:4,y:0},{x:5,y:0},{x:3,y:1},{x:4,y:1}];

            arrays[4] = [{x:4,y:0},{x:5,y:0},{x:4,y:1},{x:5,y:1}];

            arrays[5] = [{x:4,y:0},{x:3,y:0},{x:5,y:0},{x:5,y:1}];

            arrays[6] = [{x:4,y:0},{x:3,y:0},{x:4,y:1},{x:5,y:1}];

            return arrays;

        }

       

        function basicBlockColor(){

            return ["#A00000" , "#A05000" , "#A0A000" , "#00A000" , "#00A0A0" , "#0000A0" , "#A000A0"];

        }

       

        function getBlockColorByIndex(typeCodeIndex){

            var arrays = basicBlockColor();

            return arrays[typeCodeIndex];

        }

       

        /**

         * 根据编号返回指定编号的方块

         * @param typeCodeIndex 方块编号索引

         */

        function getPointByCode(typeCodeIndex){

            var arrays = basicBlockType();

            return arrays[typeCodeIndex];

        }

       

        /**

         * 获取随即出现方块的范围值

         * @param lens 随机数的范围

         */

        function getRandomIndex(){

            return parseInt(Math.random() * (arrays.length - 1) ,10);

        }

       

        /**

         * 绘制方块,按格子单个绘制

         */

        function drawBlock(){

            drawGrid();

            for(var i=0 ; i < block.length; i++){

                var point = block[i];

                var start = point.x * size;

                var end = point.y * size;

                context.fillStyle = getBlockColorByIndex(blockIndex);

                context.fillRect(start , end , size , size);

                context.strokeStyle = "black";

                context.strokeRect(start , end , size , size);

            }

            drawShadowBlock();

        }

       

        /**

         * 绘制障碍物

         */

        function roadBlock(x , y){

            context.fillStyle = "darkgray";

            var start = x * size;

            var end = y * size;

            context.fillRect(start , end , size , size);

        }

       

        /**

         * 绘制新的方块先清除之前的方块

         */

        function clearBlock(){

            for(var i=0 ; i < block.length; i++){

                var point = block[i];

                var start = point.x * size;

                var end = point.y * size;

                context.clearRect(start , end , size , size);

            }

        }

       

        /**

         * 初始化一个新的行

         */

        function initGameMapRow(){

            var array = new Array();

            for(var i=0 ; i < maxX ; i++){

                array[i] = false;

            }

            return array;

        }

       

        /**

         * 根据坐标清除指定格子的内容

         * @param 横坐标

         * @param 纵坐标

         */

        function clearBlockByPoint(x , y){

            var start = y * size;

            var end = x * size;

            context.clearRect(start , end , size , size);

        }

       

        /**

         * 清掉所有位置的空白格的绘图

         */

        function clearAllNullPoint(){

            for(var i=0 ; i < maxY ; i++){

                for(var j=0 ; j < maxX ; j++){

                    if(gameMap[i][j] == false){

                        clearBlockByPoint(i, j);

                    }

                }

            }

        }

       

        /**

         * 绘制网格线

         * @param context 绘图对象

         */

        function drawGrid(){

            clearAllNullPoint();//清除掉当前方块下落位置造成的阴影

            context.strokeStyle = "grey";//画笔颜色

            for(var i=0 ; i <= maxX ; i++){

                var start = i * size;

                var end = start + size;

                context.beginPath();

                context.moveTo(start , 0);

                context.lineTo(size * i  , size * maxY);

                context.stroke();

                context.closePath();

            }

            //绘制水平线条

            for(var i=0 ; i <= maxY ; i++){

                var start = i * size;

                var end = start + size;

                context.beginPath();

                context.moveTo(0 , size * i);

                context.lineTo(size * maxX , size * i );

                context.stroke();

                context.closePath();

            }

        }

    </script>

</body>

</html>

运行效果

俄罗斯方块.gif

 点赞


 发表评论

当前回复:作者

 评论列表


留言区