canvas随笔之2048小游戏

HTML:

<!DOCTYPE HTML>
<html>
<head>
    <title>2048小游戏</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=2.0, user-scalable=yes" />
    <style>
        html,body{
            width:100%;
            margin:0;
            padding:0;
        }
        #scoreBoard{
            width:100%;
            text-align: center;
            font-size: 15px;
            font-weight: bold;
        }
        #scoreBoard .title{
            color:#666;
        }
        #score{
            color:#ff6633;
        }
        #canvas{
            /*background-image: url("images/background1.png");*/
            position: absolute;
            left:0;top:0;right:0;bottom:0;
            margin:auto;
        }
        #again{
            position: absolute;
            z-index:2;
            bottom:0;
            width:100%;
            text-align: center;
        }
        #again input{
            width:280px;
            height:40px;
            color:white;
            border: 0;
            background-color: #ff6633;
        }
    </style>

</head>
<body>
    <div id="content">
        <div id="scoreBoard"><br>
            <span class="title">分数:</span><span id="score"> 0</span>
        </div>
        <div id="Game">
            <canvas id="canvas">您的浏览器不支持canvas</canvas>
        </div>
        <div id="again">
            <input type=button value="再 玩 儿 一 次" id="moreTime">
        </div>
    </div>
    <script type="text/javascript" src="js/animation.js"></script>
    <script type="text/javascript" src="js/img.js"></script>
    <script type="text/javascript" src="js/handle.js"></script>
    <script type="text/javascript" src="js/game.js"></script>
</body>
</html>
animation.js:
/**
 * redefine requestAnimationFrame
 * @return requestAnimationFrame function
 */
window.requestAnimationFrame = (function(){
    return window.requestAnimationFrame      ||
           window.webkitRequestAnimationFrame||
           window.mozRequestAnimationFrame   ||
           function(callback){
                window.setTimeout(callback,10);
           };
})();
img.js

//图片对象
var Img = (function(){
    function obj(position,value){
        var me = this;
        me.colors = ["#EEE4DA","#EFE0CB","#F3B079","#F59565","#F75E3E","#FF6600","#EDCE71","#EDCD60","#FFCC33","#FF6633","#FFFF00"];
        me.txtColors = ["#666","#666","#fff","#fff","#fff","#fff","#fff","#fff","#fff","#fff","#fff"];
        me.sPosition = position; //图片开始的位置
        me.ePosition = {x:null,y:null};//图片移动的目标位置
        me.step = 0;
        me.arrived = false;//false表示需要移动,true表示不需要再移动
        me.value = value;
        me.img = new Image();
        me.img.src = "images/" + me.value + ".png";
    }
    //按坐标绘制图片,参数:ctx为canvas上下文; width:图片宽度,height:图片高度
    obj.prototype.drawByPos = function(ctx,width,height){
        var me = this;
        var x = me.sPosition.x * width + 15,
            y = me.sPosition.y *height + 15;
        ctx.fillStyle = me.colors[me.value];
        ctx.fillRect(x, y , width - 10 ,height - 10);
        me.drawText(ctx, width, height, x, y);
    };
    obj.prototype.drawText = function(ctx,width,height,x,y){
        var me = this;
        ctx.fillStyle = me.txtColors[me.value];
        ctx.font = width * 0.3 + "px sans-serif";
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillText(Math.pow(2,me.value),x + width * 0.4, y + height * 0.4 );
    };
    //改变图片位置
    obj.prototype.move = function(ctx,width,height,der){
        var me = this,
            x = me.sPosition.x * width + 15,
            y = me.sPosition.y * height + 15;
        me.step += 20;
        switch(der){
        case 37:{//向左,x减小
            x = x - me.step;
            if(x <= me.ePosition.x * width + 15){
                x = me.ePosition.x * width + 15;
                me.sPosition.x = 0;
                me.step = 0;
                me.arrived = true;
            }
        }break;
        case 38:{//向上,y减小
            y = y - me.step;
            if(y <= me.ePosition.y * height + 15){
                y = me.ePosition.y * height + 15;
                me.sPosition.y = 0;
                me.step = 0;
                me.arrived = true;
            }
        }break;
        case 39:{//向右,x增加
            x = x + me.step;
            if(x >= me.ePosition.x * width + 15){
                x = me.ePosition.x * width + 15;
                me.sPosition.x = me.ePosition.x;
                me.step = 0;
                me.arrived = true;
            }
        }break;
        case 40:{//向下,y增加
            y = y + me.step;
            if(y >= me.ePosition.y * height + 15){
                y = me.ePosition.y * height + 15;
                me.sPosition.y = me.ePosition.y;
                me.step = 0;
                me.arrived = true;
            }
        }break;
        }
        ctx.fillStyle = me.colors[me.value];
        ctx.fillRect( x, y, width - 10 ,height - 10);
        me.drawText(ctx, width, height, x, y);
    };
    return obj;
})();

//背景对象
var Background = (function(){
    function obj(){
    }
    obj.prototype.draw = function(ctx,cvs){
        ctx.fillStyle = "#BCAD9D";
        ctx.fillRect(0,0,gl.cvs.width,gl.cvs.height);
        this.addRect(ctx,cvs);
    };
    obj.prototype.addRect = function(ctx,cvs){
        gl.stdWidth = (cvs.width / 4 - 5 ) | 0;
        gl.stdHeight = gl.stdWidth;
        for(var i = 0; i < 4; i++){
            for(var j = 0; j < 4; j++){
                ctx.fillStyle = "#CDBFB5";
                ctx.fillRect(i * gl.stdWidth + 15,j * gl.stdHeight + 15,gl.stdWidth-10,gl.stdHeight-10);
            }
        }
    };
    return obj;
})();

handle.js

//产生一个新方块儿,参数为方块儿的值
function getNewData(value){
    var xx = (Math.random() * 40 | 0 ) % 4;
    var yy = (Math.random() * 40 | 0 ) % 4;
    if(!gl.map[xx][yy].Img){
        gl.map[xx][yy].value = value;
        gl.map[xx][yy].Img = new Img({x:xx,y:yy},value);
        gl.map[xx][yy].Img.drawByPos(gl.ctx,gl.stdWidth, gl.stdHeight);
    }else{
        getNewData(value);
    }
}

//画背景
function drawBack(){
    var back = new Background();
    back.draw(gl.ctx,gl.cvs);
}

//创建4 * 4地图
function initMap(){
    gl.map = [];
    gl.gameScore = 0;
    gl.gameover = false;
    document.getElementById("score").innerHTML = gl.gameScore;
    for(var i = 0; i < 4; i++){
        gl.map[i] = new Array();
        for(var j = 0; j < 4; j++){
            gl.map[i][j] = new Object();
            gl.map[i][j].value = 0;
            gl.map[i][j].flag = false;//标记是否合并过
        }
    }
    drawBack();
    getNewData(1);
    getNewData(1);
}

//查询已有方块儿
function searchRect(der){
    gl.queue.length = 0;
    for(var i = 0; i < 4; i++){
        for(var j = 0; j < 4; j++){
            if(gl.map[i][j].Img){
                gl.map[i][j].Img.move(gl.ctx,gl.stdWidth, gl.stdHeight,der);
                gl.queue.push(gl.map[i][j].Img);
            }
        }
    }
}

//检查队列里的方块儿是否都移动完成
function checkQueue(){
    for(var i = 0, len = gl.queue.length; i < len; i++){
        if(!gl.queue[i].arrived){
            return false; //队列里还有未完成的方块儿
        }
    }
    return true;
}

//移动完成后,重绘地图
function reDrawMap(){
    gl.ctx.clearRect(0,0,gl.cvs.width,gl.cvs.height);
    var back = new Background();
        back.draw(gl.ctx,gl.cvs);
    for(var m = 0; m < 4; m++){
        for(var p = 0; p < 4; p++){
            gl.map[m][p].flag = false;
            if(gl.map[m][p].value > 0){
                gl.map[m][p].Img = new Img({x:m,y:p},gl.map[m][p].value);
                gl.map[m][p].Img.drawByPos(gl.ctx,gl.stdWidth, gl.stdHeight);
            }else{
                if(gl.map[m][p].Img){
                    delete gl.map[m][p].Img;
                }
            }
        }
    }
    document.getElementById("score").innerHTML = gl.gameScore;
}

//设置方块儿终点坐标
function setOldPos(i,j,ki,kj){
    gl.map[i][j].value = 0;
    gl.map[i][j].Img.ePosition.x = ki;
    gl.map[i][j].Img.ePosition.y = kj;
}

//向上运动时,遍历地图,寻找方块儿的终点目标(img.ePosition),以便移动,参数:test用于标记是否需要移动
function searchMapUp(test){
    var flagMove = false;
    for(var i = 0; i < 4; i++){
        for(var j = 0; j < 4; j++){
            if(gl.map[i][j].value > 0 ){//方块儿有值时
                var t = gl.map[i][j].value;
                if(j == 0){
                    setOldPos(i,j,i,j);
                    gl.map[i][j].value = t;
                }
                for(var k = j - 1; k >= 0; k--){
                    if(gl.map[i][k].value > 0){//当上一个方块儿有值时
                        //当方块儿的值与上一个方块儿的值相等时,合并
                        if(gl.map[i][k].value == t && !gl.map[i][k].flag){
                            if(test == 1){ //若只是检测能否移动,不改变终点目标
                                setOldPos(i,j,i,k);
                                gl.gameScore += Math.pow(2,gl.map[i][k].value);
                                gl.map[i][k].value++;
                                gl.map[i][k].flag = true;
                                if(gl.map[i][k].value == 11)//出现2048游戏通关
                                    gl.gameWin = true;
                            }
                            flagMove = true;
                        }else{//上方有方块儿,但2个方块儿的值不相同
                            if(test == 1){
                                setOldPos(i,j,i,k+1);
                                gl.map[i][k+1].value = t;
                            }
                            if(j != k + 1)
                                flagMove = true;
                        }
                        break;
                    }
                    if(k == 0){
                        if(test == 1){
                            setOldPos(i,j,i,k);
                            gl.map[i][k].value = t;
                        }
                        flagMove = true;
                    }
                }
            }
        }
    }
    return flagMove;
}

//向下运动时
function searchMapDown(test){
    var flagMove = false;
    for(var i = 0; i < 4; i++){
        for(var j = 3; j >= 0; j--){
            if(gl.map[i][j].value > 0){
                var t = gl.map[i][j].value;
                if(j == 3){
                    setOldPos(i,j,i,j);gl.map[i][j].value = t;
                }
                for(var k = j + 1; k < 4; k++){
                    if(gl.map[i][k].value > 0){
                        if(gl.map[i][k].value == t && !gl.map[i][k].flag){
                            if(test == 1){
                                setOldPos(i,j,i,k);
                                gl.gameScore += Math.pow(2,gl.map[i][k].value);
                                gl.map[i][k].value++;
                                gl.map[i][k].flag = true;
                                if(gl.map[i][k].value == 11)//出现2048游戏通关
                                    gl.gameWin = true;
                            }
                            flagMove = true;
                        }else{
                            if(test == 1){
                                setOldPos(i,j,i,k-1);
                                gl.map[i][k-1].value = t;
                            }
                            if(j != k - 1)
                                flagMove = true;
                        }
                        break;
                    }
                    if(k == 3){
                        if(test == 1){
                            setOldPos(i,j,i,k);
                            gl.map[i][k].value = t;
                        }
                        flagMove = true;
                    }
                }
            }
        }
    }
    return flagMove;
}

//向左运动时
function searchMapLeft(test){
    var flagMove = false;
    for(var j = 0; j < 4; j++){
        for(var i = 0; i < 4; i++){
            if(gl.map[i][j].value > 0){
                var t = gl.map[i][j].value;
                if(i == 0){
                    setOldPos(i,j,i,j);
                    gl.map[i][j].value = t;
                }
                for(var k = i - 1; k >= 0; k--){
                    if(gl.map[k][j].value > 0){
                        if(gl.map[k][j].value == t && !gl.map[k][j].flag){
                            if(test == 1){
                                setOldPos(i,j,k,j);
                                gl.gameScore += Math.pow(2,gl.map[k][j].value);
                                gl.map[k][j].value++;
                                gl.map[k][j].flag = true;
                                if(gl.map[k][j].value == 11)//出现2048游戏通关
                                    gl.gameWin = true;
                            }
                            flagMove = true;
                        }else{
                            if(test == 1){
                                setOldPos(i,j,k+1,j);
                                gl.map[k+1][j].value = t;
                            }
                            if(i != k + 1)
                                flagMove = true;
                        }
                        break;
                    }
                    if(k == 0){
                        if(test == 1){
                            setOldPos(i,j,k,j);
                            gl.map[k][j].value = t;
                        }
                        flagMove = true;
                    }
                }
            }
        }
    }
    return flagMove;
}

//向右运动时
function searchMapRight(test){
    var flagMove = false;
    for(var j = 0; j < 4; j++){
        for(var i = 3; i >= 0; i--){
            if(gl.map[i][j].value > 0){
                var t = gl.map[i][j].value;
                if(i == 3){
                    setOldPos(i,j,i,j);
                    gl.map[i][j].value = t;
                }
                for(var k = i + 1; k < 4; k++){
                    if(gl.map[k][j].value > 0){
                        if(gl.map[k][j].value == t && !gl.map[k][j].flag){
                            if(test == 1){
                                setOldPos(i,j,k,j);
                                gl.gameScore += Math.pow(2,gl.map[k][j].value);
                                gl.map[k][j].value++;
                                gl.map[k][j].flag = true;
                                if(gl.map[k][j].value == 11)//出现2048游戏通关
                                    gl.gameWin = true;
                            }
                            flagMove = true;
                        }else{
                            if(test == 1){
                                setOldPos(i,j,k-1,j);
                                gl.map[k-1][j].value = t;
                            }
                            if(i != k - 1)
                                flagMove = true;
                        }
                        break;
                    }
                    if(k == 3){
                        if(test == 1){
                            setOldPos(i,j,k,j);
                            gl.map[k][j].value = t;
                        }
                        flagMove = true;
                    }
                }
            }
        }
    }
    return flagMove;
}

game.js

//全局变量
var gl = {
    cvs:null,
    ctx:null,
    stdWidth:0,//方块儿宽度
    stdHeight:0,
    gameScore:0,//游戏总分
    gameWin:false,//是否通关
    map:[],//地图
    queue:[]//动画队列
};
(function(){
    gl.cvs = document.getElementById(‘canvas‘);
    gl.ctx = gl.cvs.getContext(‘2d‘);
    setCanvasSize();
    initMap();
    //再玩儿一次
    document.getElementById("moreTime").onclick = function(){
        initMap();
    };

    document.onkeydown = function(event){
        var e = event || window.event;
        derection(e.keyCode);
    };

    function setCanvasSize(){
        if(window.innerWidth < window.innerHeight){
            gl.cvs.width = window.innerWidth * 0.8;
            gl.cvs.height = gl.cvs.width;
        }else{
            gl.cvs.height = window.innerHeight * 0.8;
            gl.cvs.width = gl.cvs.height;
        }
        if(gl.cvs.width < 250){
            gl.cvs.width = 250;
            gl.cvs.height = 250;
        }else if(gl.cvs.width > 400){
            gl.cvs.width = 400;
            gl.cvs.height = 400;
        }
    }

    function derection(der){
        if(!checkQueue()) //一次滑动完成之前不能继续滑动
            return;
        gameover();//判断游戏是否结束
        var flagMove = false;
        switch(der){
            case 37:flagMove = searchMapLeft(1);break;
            case 38:flagMove = searchMapUp(1);break;
            case 39:flagMove = searchMapRight(1);break;
            case 40:flagMove = searchMapDown(1);break;
            default:break;
        }
        if(flagMove)
            slide(der);
    }
    function slide(der){
        gl.ctx.clearRect(0,0,gl.cvs.width,gl.cvs.height);
        var back = new Background();
        back.draw(gl.ctx,gl.cvs);
        searchRect(der);
        if(checkQueue()){
            if(!gameWin()){
                reDrawMap();
                getNewData(1);
            }
        }else{
            window.requestAnimationFrame(function(){slide(der);});
        }
    }

    function gameover(){
        var f1 = searchMapLeft(0),
            f2 = searchMapUp(0),
            f3 = searchMapRight(0),
            f4 = searchMapDown(0);
        if( f1 || f2 || f3 || f4){
            return false;
        }else{
            document.onkeydown = null;
            document.removeEventListener("touchstart",touchstart,false);
            document.removeEventListener("touchmove",touchmove,false);
            document.removeEventListener("touchend",touchend,false);
            alert("游戏结束啦!");
            return true;
        }
    }

    function gameWin(){
        if(gl.gameWin){
            alert("恭喜你,通过成功!!!");
            document.onkeydown = null;
            document.removeEventListener("touchstart",touchstart,false);
            document.removeEventListener("touchmove",touchmove,false);
            document.removeEventListener("touchend",touchend,false);
            return true;
        }
        return false;
    }

    var startPos = {x:null,y:null}, endPos = {x: null,y: null};
    document.addEventListener("touchstart",touchstart,false);
    function touchstart(event){
        var touch = event.touches[0];
        startPos.x = touch.pageX;
        startPos.y = touch.pageY;
        document.addEventListener("touchmove",touchmove,false);
        document.addEventListener("touchend",touchend,false);
    }    

    function touchmove(event){
        var touch = event.touches[0];
        endPos.x = touch.pageX;
        endPos.y = touch.pageY;
        computeDirection();
    }

    function touchend(){
        document.removeEventListener("touchmove",touchmove,false);
        document.addEventListener("touchend",touchend,false);
    }

    //计算滑动方向
    function computeDirection(){
        if(!checkQueue()) //一次滑动完成之前不能继续滑动
            return;
        gameover();//判断游戏是否结束

        var flagMove = false;
        var x0 = endPos.x - startPos.x,
            y0 = endPos.y - startPos.y;
        if(x0 > 0 && Math.abs(x0) > Math.abs(y0)
        || x0 > 0 && Math.abs(x0) > Math.abs(y0)){
            flagMove = searchMapRight(1);//alert("right");
            der = 39;
        }else if(y0 < 0 && Math.abs(x0) < Math.abs(y0)
              || y0 < 0 && Math.abs(x0) < Math.abs(y0)){
            flagMove = searchMapUp(1);//alert("top");
            der = 38;
        }else if(x0 < 0 && Math.abs(x0) > Math.abs(y0)
              || x0 < 0 && Math.abs(x0) > Math.abs(y0)){
            flagMove = searchMapLeft(1);//alert("left");
            der = 37;
        }else if(y0 > 0 && Math.abs(x0) < Math.abs(y0)
                || y0 > 0 && Math.abs(x0) < Math.abs(y0)){
            flagMove = searchMapDown(1);//alert("down");
            der = 40;
        }
        if(flagMove)
            slide(der);
    }
})();
 
时间: 2024-10-22 10:53:04

canvas随笔之2048小游戏的相关文章

使用JS实现2048小游戏

JS实现2048小游戏源码 效果图: 代码如下,复制即可使用: (适用浏览器:360.FireFox.Chrome.Opera.傲游.搜狗.世界之窗. 不支持Safari.IE8及以下浏览器.) <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name=&

.NET手撸2048小游戏

.NET手撸2048小游戏 2048是一款益智小游戏,得益于其规则简单,又和2的倍数有关,因此广为人知,特别是广受程序员的喜爱. 本文将再次使用我自制的"准游戏引擎"FlysEngine,从空白窗口开始,演示如何"手撸"2048小游戏,并在编码过程中感受C#的魅力和.NET编程的快乐. 说明:FlysEngine是封装于Direct2D,重复本文示例,只需在.NET Core 3.0下安装NuGet包FlysEngine.Desktop即可. 并不一定非要做一层封装

C# 开发2048小游戏

这应该是几个月前,闲的手痒,敲了一上午代码搞出来的,随之就把它丢弃了,当时让别人玩过,提过几条更改建议,但是时至今日,我也没有进行过优化和更改(本人只会作案,不会收场,嘎嘎),下面的建议要给代码爱好的童鞋完成了. 更改建议: a.当数字超过四位数时,显示的时候有部分被它的容器TextBox遮挡了,能不能把显示的数值变小点?答案是可以的.代码里有一段通过矩阵数据填充TextBox值的操作,可以在填充时,判断下数值长度,然后修改TextBox的文字大小. b.玩游戏的时候,使用方向键移动时,焦点可能

2048小游戏(C语言版)

1 #include <climits> 2 #include <cstdio> 3 #include <cstring> 4 #include <stack> 5 #include <string> 6 #include <map> 7 #include <vector> 8 #include <cmath> 9 10 11 /* 12 玩法说明:编译运行后,输入一个整数n,表示自己想玩的游戏中是n*n的表格

如何在CentOS上安装一个2048小游戏

如何在centos上安装一个2048小游戏 最近在学习CentOS系统,就琢磨着玩点什么,然后我看到有人在玩2048小游戏,所有我就在想,为啥不装一个2048小游戏搞一下嘞,于是乎,我就开始工作啦 由于我个人的编程能力不强,所以我就在网上找到了一个C语言版的2048游戏小程序,我把它放到我的百度网盘上,可以供大家下载(链接:http://pan.baidu.com/s/1jIutb3g 密码:mu9z),然后我们把这个程序给复制到CentOS系统下,在进行下一步的工作.我们可以在CentOS上安

Swift实战之2048小游戏

上周在图书馆借了一本Swift语言实战入门,入个门玩一玩^_^正好这本书的后面有一个2048小游戏的实例,笔者跟着实战了一把. 差不多一周的时间,到今天,游戏的基本功能已基本实现,细节我已不打算继续完善,就这么整理一下过程中一些值得记录的点吧. 用的Swift版本是2.0,原书中的Swift版本会低一些,所以实践起来有些地方语法并不一样. 一.开始页面   在程序的第一张页面(Main.storyboard)上,只放了一个“开始游戏”按钮,点击按钮,弹出一个提示对话框,确认后,进入游戏页面. 1

2048小游戏-JS实现(BUG调试中)

刚刚学习JS的菜鸟,游戏没有实现滑动效果.希望有前辈能指点一下······ 定义的主要方法: 1.fuzhi()生成一对随机数,然后根据这对随机数取得一个随机单元格,先判断其是否为空,不为空,对其进行赋值为2的操作:为空,则再次调用fuzhi(). 2.secai()遍历表格,根据单元格的数值改变单元格的背景颜色. 3.score()遍历单元格,计算实时总得分. 4.keyDown()主要方法,根据用户按上下左右键来进行不同的数值相加.消除动作.这一段代码写得很冗余····· 1 <!DOCTY

2048小游戏用例图

用例图主要用来描述"用户.需求.系统功能单元"之间的关系.它展示了一个外部用户能够观察到的系统功能模型图. 下面是我们2048小游戏的用例图:

2048小游戏(变态版哦)

近日,由于博主同学暑假有个作业是写个2048小游戏,我一听挺好玩的..然后就开始了.. 首先,2048在移动过程中的规则其实也没有特别难,但是感觉也不是一句话就能说完的.(不过玩的多--感觉总是有的) 废话不多说,其实博主同学提供了pdf描述了2048的算法. 各位筒子入坑前请先过几眼这个规则,以及其算法(当然我觉得算法第二点有点问题,后述) 那么在游戏的编写前,可以先对细枝末节做一些准备. 1.出现数字2/4的概率 int getRand() { int i = rand() % 10; if