温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何使用Html5写一个简单的俄罗斯方块小游戏

发布时间:2021-05-22 14:17:37 来源:亿速云 阅读:352 作者:小新 栏目:web开发

小编给大家分享一下如何使用Html5写一个简单的俄罗斯方块小游戏,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

游戏效果:

如何使用Html5写一个简单的俄罗斯方块小游戏

如何使用Html5写一个简单的俄罗斯方块小游戏

制作思路

如何使用Html5写一个简单的俄罗斯方块小游戏

因为书里的俄罗斯方块比较普通,太常规了,不是很好看,所以我在网上找了上面那张图片,打算照着它来做。(请无视成品和原图的差距)

然后便是游戏界面和常规的俄罗斯方块游戏逻辑。

接着便是游戏结束界面了。

原本想做个弹出层,但觉得找图片有点麻烦,所以就在网上找了文字特效,套用了一下。

代码实现:

首先是html文件和css文件,主要涉及了布局方面。作为新手,在上面真的是翻来覆去的踩坑。o(╥﹏╥)o

index.html

<!DOCTYPE html> <html> <head>     <title>俄罗斯方块</title>     <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>     <link rel=stylesheet type="text/css" href="teris.css">     <style type="text/css">         /*导入外部的字体文件*/         @font-face{             font-family:tmb;/*为字体命名为tmb*/             src:url("DS-DIGIB.TTF") format("TrueType");/*format为字体文件格式,TrueType为ttf*/         }         div>span{             font-family:tmb;             font-size:18pt;             color:green;         }     </style> </head> <body>     <div id="container" class="bg">         <!--ui-->         <div class="ui_bg">             <div style="float:left;margin-right:4px;">                 速度:<span id="cur_speed">1</span>             </div>             <div style="float:left;">                 当前分数:<span id="cur_points">0</span>             </div>             <div style="float:right;">                 最高分数:<span id="max_points">0</span>             </div>         </div>         <canvas id="text" width="500" height="100" style="position:absolute;"></canvas>         <canvas id="stage" width="500" height="100" style="position:absolute;"></canvas>     </div>     <script src='EasePack.min.js'></script>     <script src='TweenLite.min.js'></script>     <script src='easeljs-0.7.1.min.js'></script>     <script src='requestAnimationFrame.js'></script>     <script type="text/javascript" src="jquery-3.4.1.min.js"></script>     <script type="text/javascript" src="teris.js"></script> </body> </html>

teris.css

*{     margin:0;     padding:0; } html, body{     width:100%;     height:100%; } .bg{     font-size:13pt;     background-color:rgb(239, 239, 227);     /*好看的渐变色*/     background-image:radial-gradient(rgb(239, 239, 227), rgb(230, 220, 212));     /*阴影*/     box-shadow:#cdc8c1 -1px -1px 7px 0px;     padding-bottom:4px; } .ui_bg{     border-bottom:1px #a69e9ea3 solid;     padding-bottom:2px;     overflow:hidden;/*没有这句的话因为子div都设置了float,所以是浮在网页上的,所以父div就没有高度,这句清除了浮动,让父div有了子div的高度*/ }

然后是重头戏,teris.js

游戏变量

//游戏设定 var TETRIS_ROWS = 20; var TETRIS_COLS = 14; var CELL_SIZE = 24; var NO_BLOCK=0; var HAVE_BLOCK=1; // 定义几种可能出现的方块组合 var blockArr = [     // Z     [         {x: TETRIS_COLS / 2 - 1 , y:0},         {x: TETRIS_COLS / 2 , y:0},         {x: TETRIS_COLS / 2 , y:1},         {x: TETRIS_COLS / 2 + 1 , y:1}     ],     // 反Z     [         {x: TETRIS_COLS / 2 + 1 , y:0},         {x: TETRIS_COLS / 2 , y:0},         {x: TETRIS_COLS / 2 , y:1},         {x: TETRIS_COLS / 2 - 1 , y:1}     ],     // 田     [         {x: TETRIS_COLS / 2 - 1 , y:0},         {x: TETRIS_COLS / 2 , y:0},         {x: TETRIS_COLS / 2 - 1 , y:1},         {x: TETRIS_COLS / 2 , y:1}     ],     // L     [         {x: TETRIS_COLS / 2 - 1 , y:0},         {x: TETRIS_COLS / 2 - 1, y:1},         {x: TETRIS_COLS / 2 - 1 , y:2},         {x: TETRIS_COLS / 2 , y:2}     ],     // J     [         {x: TETRIS_COLS / 2  , y:0},         {x: TETRIS_COLS / 2 , y:1},         {x: TETRIS_COLS / 2  , y:2},         {x: TETRIS_COLS / 2 - 1, y:2}     ],     // □□□□     [         {x: TETRIS_COLS / 2 , y:0},         {x: TETRIS_COLS / 2 , y:1},         {x: TETRIS_COLS / 2 , y:2},         {x: TETRIS_COLS / 2 , y:3}     ],     // ┴     [         {x: TETRIS_COLS / 2 , y:0},         {x: TETRIS_COLS / 2 - 1 , y:1},         {x: TETRIS_COLS / 2 , y:1},         {x: TETRIS_COLS / 2 + 1, y:1}     ] ]; // 记录当前积分 var curScore=0; // 记录曾经的最高积分 var maxScore=1; var curSpeed=1; //ui元素 var curSpeedEle=document.getElementById("cur_speed"); var curScoreEle=document.getElementById("cur_points"); var maxScoreEle=document.getElementById("max_points"); var timer;//方块下落控制 var myCanvas; var canvasCtx; var tetris_status;//地图数据 var currentFall;//当前下落的block

游戏界面的完善

//create canvas function createCanvas(){     myCanvas=document.createElement("canvas");     myCanvas.width=TETRIS_COLS*CELL_SIZE;     myCanvas.height=TETRIS_ROWS*CELL_SIZE;     //绘制背景     canvasCtx=myCanvas.getContext("2d");     canvasCtx.beginPath();     //TETRIS_COS     for(let i=1; i<TETRIS_COLS; i++){         canvasCtx.moveTo(i*CELL_SIZE, 0);         canvasCtx.lineTo(i*CELL_SIZE, myCanvas.height);     }     for(let i=1; i<TETRIS_ROWS; i++){         canvasCtx.moveTo(0, i*CELL_SIZE);         canvasCtx.lineTo(myCanvas.width, i*CELL_SIZE);     }     canvasCtx.closePath();     canvasCtx.strokeStyle="#b4a79d";     canvasCtx.lineWidth=0.6;     canvasCtx.stroke();     //第一行,最后一行,第一列,最后一列粗一点。     canvasCtx.beginPath();     canvasCtx.moveTo(0, 0);     canvasCtx.lineTo(myCanvas.width, 0);     canvasCtx.moveTo(0, myCanvas.height);     canvasCtx.lineTo(myCanvas.width, myCanvas.height);     canvasCtx.moveTo(0, 0);     canvasCtx.lineTo(0, myCanvas.height);     canvasCtx.moveTo(myCanvas.width, 0);     canvasCtx.lineTo(myCanvas.width, myCanvas.height);     canvasCtx.closePath();     canvasCtx.strokeStyle="#b4a79d";     canvasCtx.lineWidth=4;     canvasCtx.stroke();     //设置绘制block时的style     canvasCtx.fillStyle="#201a14"; } draw canvas
function changeWidthAndHeight(w, h){     //通过jquery设置css     h+=$("ui_bg").css("height")+$("ui_bg").css("margin-rop")+$("ui_bg").css("margin-bottom")+$("ui_bg").css("padding-top")+$("ui_bg").css("padding-bottom");     $(".bg").css({         "width":w,         "height":h,         "top":0, "bottom":0, "right":0, "left":0,         "margin":"auto"     }); } change width and height
//draw blocks function drawBlocks(){     //清空地图     for(let i=0; i<TETRIS_ROWS;i++){         for(let j=0;j<TETRIS_COLS;j++)             canvasCtx.clearRect(j*CELL_SIZE+1, i*CELL_SIZE+1, CELL_SIZE-2, CELL_SIZE-2);     }     //绘制地图     for(let i=0; i<TETRIS_ROWS;i++){         for(let j=0;j<TETRIS_COLS;j++){             if(tetris_status[i][j]!=NO_BLOCK)                 canvasCtx.fillRect(j*CELL_SIZE+1, i*CELL_SIZE+1, CELL_SIZE-2, CELL_SIZE-2);//中间留点缝隙         }     }     //绘制currentFall     for(let i=0;i<currentFall.length;i++)         canvasCtx.fillRect(currentFall[i].x*CELL_SIZE+1, currentFall[i].y*CELL_SIZE+1, CELL_SIZE-2,CELL_SIZE-2); } draw block

游戏逻辑

function rotate(){     // 定义记录能否旋转的旗标     var canRotate = true;     for (var i = 0 ; i < currentFall.length ; i++)     {         var preX = currentFall[i].x;         var preY = currentFall[i].y;         // 始终以第三个方块作为旋转的中心,         // i == 2时,说明是旋转的中心         if(i != 2)         {             // 计算方块旋转后的x、y坐标             var afterRotateX = currentFall[2].x + preY - currentFall[2].y;             var afterRotateY = currentFall[2].y + currentFall[2].x - preX;             // 如果旋转后所在位置已有方块,表明不能旋转             if(tetris_status[afterRotateY][afterRotateX + 1] != NO_BLOCK)             {                 canRotate = false;                 break;             }             // 如果旋转后的坐标已经超出了最左边边界             if(afterRotateX < 0 || tetris_status[afterRotateY - 1][afterRotateX] != NO_BLOCK)             {                 moveRight();                 afterRotateX = currentFall[2].x + preY - currentFall[2].y;                 afterRotateY = currentFall[2].y + currentFall[2].x - preX;                 break;             }             if(afterRotateX < 0 || tetris_status[afterRotateY-1][afterRotateX] != NO_BLOCK)             {                 moveRight();                 break;             }             // 如果旋转后的坐标已经超出了最右边边界             if(afterRotateX >= TETRIS_COLS - 1 ||                  tetris_status[afterRotateY][afterRotateX+1] != NO_BLOCK)             {                 moveLeft();                 afterRotateX = currentFall[2].x + preY - currentFall[2].y;                 afterRotateY = currentFall[2].y + currentFall[2].x - preX;                 break;             }             if(afterRotateX >= TETRIS_COLS - 1 ||                  tetris_status[afterRotateY][afterRotateX+1] != NO_BLOCK)             {                 moveLeft();                 break;             }         }     }     if(canRotate){         for (var i = 0 ; i < currentFall.length ; i++){             var preX = currentFall[i].x;             var preY = currentFall[i].y;             if(i != 2){                 currentFall[i].x = currentFall[2].x +                      preY - currentFall[2].y;                 currentFall[i].y = currentFall[2].y +                      currentFall[2].x - preX;             }         }         localStorage.setItem("currentFall", JSON.stringify(currentFall));     } } 旋转
//按下 下 或 interval到了 function next(){     if(moveDown()){         //记录block         for(let i=0;i<currentFall.length;i++)             tetris_status[currentFall[i].y][currentFall[i].x]=HAVE_BLOCK;         //判断有没有满行的         for(let j=0;j<currentFall.length;j++){             for(let i=0;i<TETRIS_COLS; i++){                 if(tetris_status[currentFall[j].y][i]==NO_BLOCK)                     break;                 //最后一行满了                 if(i==TETRIS_COLS-1){                     //消除最后一行                     for(let i=currentFall[j].y; i>0;i--){                         for(let j=0;j<TETRIS_COLS;j++)                             tetris_status[i][j]=tetris_status[i-1][j];                     }                     //分数增加                     curScore+=5;                     localStorage.setItem("curScore", curScore);                     if(curScore>maxScore){                         //超越最高分                         maxScore=curScore;                         localStorage.setItem("maxScore", maxScore);                     }                     //加速                     curSpeed+=0.1;                     localStorage.setItem("curSpeed", curSpeed);                     //ui输出                     curScoreEle.innerHTML=""+curScore;                     maxScoreEle.innerHTML=""+maxScore;                     curSpeedEle.innerHTML=curSpeed.toFixed(1);//保留两位小数                     clearInterval(timer);                     timer=setInterval(function(){                         next();                     }, 500/curSpeed);                 }             }         }         //判断是否触顶         for(let i=0;i<currentFall.length;i++){             if(currentFall[i].y==0){                 gameEnd();                 return;             }         }         localStorage.setItem("tetris_status", JSON.stringify(tetris_status));         //新的block         createBlock();         localStorage.setItem("currentFall", JSON.stringify(currentFall));     }     drawBlocks(); } //右移 function moveRight(){     for(let i=0;i<currentFall.length;i++){         if(currentFall[i].x+1>=TETRIS_ROWS || tetris_status[currentFall[i].y][currentFall[i].x+1]!=NO_BLOCK)             return;     }     for(let i=0;i<currentFall.length;i++)         currentFall[i].x++;     localStorage.setItem("currentFall", JSON.stringify(currentFall));     return; } //左移 function moveLeft(){     for(let i=0;i<currentFall.length;i++){         if(currentFall[i].x-1<0 || tetris_status[currentFall[i].y][currentFall[i].x-1]!=NO_BLOCK)             return;     }     for(let i=0;i<currentFall.length;i++)         currentFall[i].x--;     localStorage.setItem("currentFall", JSON.stringify(currentFall));     return; } //judge can move down and if arrive at end return 1, if touch other blocks return 2, else, return 0 function moveDown(){     for(let i=0;i<currentFall.length;i++){         if(currentFall[i].y>=TETRIS_ROWS-1 || tetris_status[currentFall[i].y+1][currentFall[i].x]!=NO_BLOCK)             return true;     }     for(let i=0;i<currentFall.length;i++)         currentFall[i].y+=1;     return false; } 上下左右移动
function gameKeyEvent(evt){     switch(evt.keyCode){         //向下         case 40://&darr;         case 83://S             next();             drawBlocks();             break;         //向左         case 37://&larr;         case 65://A             moveLeft();             drawBlocks();             break;         //向右         case 39://&rarr;         case 68://D             moveRight();             drawBlocks();             break;         //旋转         case 38://&uarr;         case 87://W             rotate();             drawBlocks();             break;     } } keydown事件监听

keydown事件监听

其他的详细情况可以看源代码,我就不整理了。

接下来我们看游戏结束时的特效。因为我也不是很懂,所以在这里整理的会比较详细。当做学习。

//game end function gameEnd(){     clearInterval(timer);     //键盘输入监听结束     window.onkeydown=function(){         //按任意键重新开始游戏         window.onkeydown=gameKeyEvent;         //初始化游戏数据         initData();         createBlock();         localStorage.setItem("currentFall", JSON.stringify(currentFall));         localStorage.setItem("tetris_status", JSON.stringify(tetris_status));         localStorage.setItem("curScore", curScore);         localStorage.setItem("curSpeed", curSpeed);         //绘制         curScoreEle.innerHTML=""+curScore;         curSpeedEle.innerHTML=curSpeed.toFixed(1);//保留两位小数         drawBlocks();         timer=setInterval(function(){             next();         }, 500/curSpeed);         //清除特效         this.stage.removeAllChildren();         this.textStage.removeAllChildren();     };     //特效,游戏结束     setTimeout(function(){         initAnim();         //擦除黑色方块         for(let i=0; i<TETRIS_ROWS;i++){             for(let j=0;j<TETRIS_COLS;j++)                 canvasCtx.clearRect(j*CELL_SIZE+1, i*CELL_SIZE+1, CELL_SIZE-2, CELL_SIZE-2);         }     }, 200);     //推迟显示Failed     setTimeout(function(){         if(textFormed) {             explode();             setTimeout(function() {                 createText("FAILED");             }, 810);         } else {             createText("FAILED");         }     }, 800); }

上面代码里的localstorage是html5的本地数据存储。因为不是运用很难,所以具体看代码。

整个特效是运用了createjs插件。要引入几个文件。

如何使用Html5写一个简单的俄罗斯方块小游戏

easeljs-0.7.1.min.js,EasePacj.min.js,requestAnimationFrame.js和TweenLite.min.js 游戏重新开始就要清除特效。我看api里我第一眼望过去最明显的就是removeAllChildren(),所以就选了这个。其他的改进日后再说。

 //清除特效         this.stage.removeAllChildren();         this.textStage.removeAllChildren();
function initAnim() {     initStages();     initText();     initCircles();     //在stage下方添加文字&mdash;&mdash;按任意键重新开始游戏.     tmp = new createjs.Text("t", "12px 'Source Sans Pro'", "#54555C");     tmp.textAlign = 'center';     tmp.x = 180;     tmp.y=350;     tmp.text = "按任意键重新开始游戏";     stage.addChild(tmp);     animate(); } initAnim

上面初始化了一个stage,用于存放特效,一个textstage,用于形成“FAILED”的像素图片。还有一个按任意键重新游戏的提示。同时开始每隔一段时间就刷新stage。

根据block的位置来初始化小圆点。

function initCircles() {     circles = [];     var p=[];     var count=0;     for(let i=0; i<TETRIS_ROWS;i++)         for(let j=0;j<TETRIS_COLS;j++)             if(tetris_status[i][j]!=NO_BLOCK)                 p.push({'x':j*CELL_SIZE+2, 'y':i*CELL_SIZE+2, 'w':CELL_SIZE-3, 'h':CELL_SIZE-4});     for(var i=0; i<250; i++) {         var circle = new createjs.Shape();         var r = 7;         //x和y范围限定在黑色block内         var x = p[count]['x']+p[count]['w']*Math.random();         var y = p[count]['y']+p[count]['h']*Math.random();         count++;         if(count>=p.length)             count=0;         var color = colors[Math.floor(i%colors.length)];         var alpha = 0.2 + Math.random()*0.5;         circle.alpha = alpha;         circle.radius = r;         circle.graphics.beginFill(color).drawCircle(0, 0, r);         circle.x = x;         circle.y = y;         circles.push(circle);         stage.addChild(circle);         circle.movement = 'float';         tweenCircle(circle);     } } initCircles

然后再讲显示特效Failed的createText()。先将FAILED的text显示在textstage里,然后ctx.getImageData.data获取像素数据,并以此来为每个小圆点定义位置。

function createText(t) {     curText=t;     var fontSize = 500/(t.length);     if (fontSize > 80) fontSize = 80;     text.text = t;     text.font = "900 "+fontSize+"px 'Source Sans Pro'";     text.textAlign = 'center';     text.x = TETRIS_COLS*CELL_SIZE/2;     text.y = 0;     textStage.addChild(text);     textStage.update();     var ctx = document.getElementById('text').getContext('2d');     var pix = ctx.getImageData(0,0,600,200).data;     textPixels = [];     for (var i = pix.length; i >= 0; i -= 4) {         if (pix[i] != 0) {             var x = (i / 4) % 600;             var y = Math.floor(Math.floor(i/600)/4);             if((x && x%8 == 0) && (y && y%8 == 0)) textPixels.push({x: x, y: y});         }     }     formText();     textStage.clear();//清楚text的显示 } CreateText

跟着代码的节奏走,我们现在来到了formtext.

function formText() {     for(var i= 0, l=textPixels.length; i<l; i++) {         circles[i].originX = offsetX + textPixels[i].x;         circles[i].originY = offsetY + textPixels[i].y;         tweenCircle(circles[i], 'in');     }     textFormed = true;     if(textPixels.length < circles.length) {         for(var j = textPixels.length; j<circles.length; j++) {             circles[j].tween = TweenLite.to(circles[j], 0.4, {alpha: 0.1});         }     } } formtext

explode()就是讲已组成字的小圆点给重新遣散。

动画实现是使用了tweenlite.

function tweenCircle(c, dir) {     if(c.tween) c.tween.kill();     if(dir == 'in') {         /*TweenLite.to 改变c实例的x坐标,y坐标,使用easeInOut弹性函数,透明度提到1,改变大小,radius,总用时0.4s*/         c.tween = TweenLite.to(c, 0.4, {x: c.originX, y: c.originY, ease:Quad.easeInOut, alpha: 1, radius: 5, scaleX: 0.4, scaleY: 0.4, onComplete: function() {             c.movement = 'jiggle';/*轻摇*/             tweenCircle(c);         }});     } else if(dir == 'out') {         c.tween = TweenLite.to(c, 0.8, {x: window.innerWidth*Math.random(), y: window.innerHeight*Math.random(), ease:Quad.easeInOut, alpha: 0.2 + Math.random()*0.5, scaleX: 1, scaleY: 1, onComplete: function() {             c.movement = 'float';             tweenCircle(c);         }});     } else {         if(c.movement == 'float') {             c.tween = TweenLite.to(c, 5 + Math.random()*3.5, {x: c.x + -100+Math.random()*200, y: c.y + -100+Math.random()*200, ease:Quad.easeInOut, alpha: 0.2 + Math.random()*0.5,                 onComplete: function() {                     tweenCircle(c);                 }});         } else {             c.tween = TweenLite.to(c, 0.05, {x: c.originX + Math.random()*3, y: c.originY + Math.random()*3, ease:Quad.easeInOut,                 onComplete: function() {                     tweenCircle(c);                 }});         }     } }

TweenLite.to函数第一个参数,要做动画的实例,第二个参数,事件,第三个参数,动画改变参数。

Quad.easeInOut()意思是在动画开始和结束时缓动。onComplete动画完成时调用的函数。

以上是“如何使用Html5写一个简单的俄罗斯方块小游戏”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI