首页前端开发HTMLHtml5写一个简单的俄罗斯方块小游戏

Html5写一个简单的俄罗斯方块小游戏

时间2024-01-25 01:52:40发布访客分类HTML浏览485
导读:收集整理的这篇文章主要介绍了Html5写一个简单的俄罗斯方块小游戏,觉得挺不错的,现在分享给大家,也给大家做个参考。 导言在一个风和日丽的一天,看完了疯狂html 5+CSS 3+JavaScript讲义,跟着做了书里最后一章的俄...
收集整理的这篇文章主要介绍了Html5写一个简单的俄罗斯方块小游戏,觉得挺不错的,现在分享给大家,也给大家做个参考。

导言

在一个风和日丽的一天,看完了疯狂html 5+CSS 3+JavaScript讲义,跟着做了书里最后一章的俄罗斯方块小游戏,并做了一些改进,作为自己前端学习的第一站。

游戏效果:

制作思路

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

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

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

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

代码实现:

首先是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 canvasfunction 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;
     iTETRIS_COLS;
 i++){
            canvasCtx.moveTo(i*CELL_SIZE, 0);
            canvasCtx.lineto(i*CELL_SIZE, myCanvas.height);
    }
        for(let i=1;
     iTETRIS_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 blocksfunction drawBlocks(){
        //清空地图    for(let i=0;
     iTETRIS_ROWS;
i++){
            for(let j=0;
    jTETRIS_COLS;
    j++)            canvasCtx.clearRect(j*CELL_SIZE+1, i*CELL_SIZE+1, CELL_SIZE-2, CELL_SIZE-2);
    }
        //绘制地图    for(let i=0;
     iTETRIS_ROWS;
i++){
            for(let j=0;
    jTETRIS_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;
    icurrentFall.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;
    icurrentFall.length;
    i++)            tetris_status[currentFall[i].y][currentFall[i].x]=HAVE_BLOCK;
            //判断有没有满行的        for(let j=0;
    jcurrentFall.length;
j++){
                for(let i=0;
    iTETRIS_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;
    jTETRIS_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;
    icurrentFall.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;
    icurrentFall.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;
    icurrentFall.length;
    i++)        currentFall[i].x++;
        localStorage.setItem("currentFall", JSON.stringify(currentFall));
        return;
}
//左移function moveLeft(){
        for(let i=0;
    icurrentFall.length;
i++){
            if(currentFall[i].x-10 || tetris_status[currentFall[i].y][currentFall[i].x-1]!=NO_BLOCK)            return;
    }
        for(let i=0;
    icurrentFall.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 0function moveDown(){
        for(let i=0;
    icurrentFall.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;
    icurrentFall.length;
    i++)        currentFall[i].y+=1;
        return false;
}
    上下左右移动
function gameKeyEvent(evt){
    switch(evt.keyCode){
            //向下        case 40://↓        case 83://S            next();
                drawBlocks();
                break;
            //向左        case 37://←        case 65://A            moveLeft();
                drawBlocks();
                break;
            //向右        case 39://→        case 68://D            moveRight();
                drawBlocks();
                break;
            //旋转        case 38://↑        case 87://W            rotate();
                drawBlocks();
                break;
    }
}
    keydown事件监听

keydown事件监听

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

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

//game endfunction 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;
     iTETRIS_ROWS;
i++){
                for(let j=0;
    jTETRIS_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插件。要引入几个文件。

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下方添加文字——按任意键重新开始游戏.    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;
     iTETRIS_ROWS;
    i++)        for(let j=0;
    jTETRIS_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;
     i250;
 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;
     il;
 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;
     jcircles.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写一个简单的俄罗斯方块小游戏,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

htmlhtml5俄罗斯方块

若转载请注明出处: Html5写一个简单的俄罗斯方块小游戏
本文地址: https://pptw.com/jishu/586005.html
关于html字符串正则判断和匹配的具体使用 吃透移动端 1px的具体用法

游客 回复需填写必要信息