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

导言

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

游戏效果:

Html5写一个简单的俄罗斯方块小游戏_第1张图片

Html5写一个简单的俄罗斯方块小游戏_第2张图片

制作思路

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

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

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

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

代码实现:

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

index.html




    俄罗斯方块
    
    
    



    
速度:1
当前分数:0
最高分数:0

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 
 
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 
 

游戏逻辑

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;i0;i--){
                        for(let j=0;jmaxScore){
                        //超越最高分
                        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=TETRIS_ROWS || tetris_status[currentFall[i].y][currentFall[i].x+1]!=NO_BLOCK)
            return;
    }
    for(let i=0;i=TETRIS_ROWS-1 || tetris_status[currentFall[i].y+1][currentFall[i].x]!=NO_BLOCK)
            return true;
    }

    for(let i=0;i 
 
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 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 
 

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

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

Html5写一个简单的俄罗斯方块小游戏_第3张图片

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; i=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 
 

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写一个简单的俄罗斯方块小游戏,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

你可能感兴趣的