2048小游戏

2048JS

实现思路

  1. 先创建一个map矩阵,记录下标(同时要在每个块设置自定义属性)
  2. 封装一个随机产生块的函数(在这个函数里要进行游戏结束的判断)
  3. 根据键盘上下左右的按键控制移动方向(移动端可以通过触屏移动)
  4. 开始移动(下面是移动的步骤)
  5. 先全部往同一个方向移动
  6. 第一次移动完合并(每次根据不同的方向按不同规则来移动,合并的同时记录当前合并的块的数值及总分,并根据数值改变块的颜色)
  7. 合并完再执行一次上面说的移动的函数
  8. 封装改变颜色的函数

实现代码

(为了更好的理解代码,代码没有进行封装,有些地方有冗余的代码,有兴趣的小伙伴可以自行封装)

1.先创建需要使用的变量及初始化map
let wrap = document.querySelector('.wrap');//获取包裹的wrap
    let map = [];//存储记录2048的数值及位置
    let direction = null; //移动的方向
    let scroe = 0;
    //初始化map
    function mapInit(map) {
        for (var i = 0; i < 4; i++) {
            map[i] = [];
            for (var j = 0; j < 4; j++) {
                map[i][j] = 0;
            }
        }
        return map;
    }

    map = mapInit(map); //存储生成块的位置(初始化)
2.封装生成块的函数
function randomBlock(wrap) {
        let span = wrap.querySelectorAll('span');
        if (span.length == 16) {
            alert('游戏结束');
            return false;
        }
        //随机生成x,y,如果生成的位置已有重新生成
        do {
            var x = parseInt(Math.random() * 4);
            var y = parseInt(Math.random() * 4);
        } while (map[x][y]);
        //在当前生成的块位置设置一个初始值为2
        map[x][y] = 2;
        //新增一个span
        var box = document.createElement('span');
        //给span添加自定义属性x,y,及对应的count值
        box.setAttribute('x', x);
        box.setAttribute('y', y);
        box.setAttribute('num', 2);
        //生成块的位置
        x = (x * 100) + 14 + (12 * x);
        y = (y * 100) + 14 + (12 * y);
        box.style.left = y + 'px';
        box.style.top = x + 'px';
        box.innerText = 2;
        //添加进wrap里
        wrap.appendChild(box);
    }
3.全部往同一个方向移动
  //根据方向全部往一个方向移动
    function moveBox(direction) {
        let span = wrap.querySelectorAll('span');
        //遍历每个span块
        for (let i = 0; i < span.length; i++) {
            //获取每个span的x,y
            let x = Number(span[i].getAttribute('x'));
            let y = Number(span[i].getAttribute('y'));
            switch (direction) {
                case 'left':
                    //如果当前块位于最左边则
                    if (y == 0) {
                        continue;
                    } else {
                        //获取当前块左边有多少个块
                        let xBox = 0;
                        for (var j = 0; j < y; j++) {
                            if (map[x][j] != 0) {
                                xBox++;
                            }
                        }
                        //设置自定义属性y
                        span[i].setAttribute('y', xBox);
                        //移动
                        span[i].style.left = 14 + xBox * (100 + 12) + 'px';
                    }
                    break;
                case 'right':
                    //如果当前块位于最右边则
                    if (y == 3) {
                        continue;
                    } else {
                        //获取当前块右边有多少个块
                        let xBox = 0;
                        for (var j = 3; j > y; j--) {
                            if (map[x][j] != 0) {
                                xBox++;
                            }
                        }
                        //改变自定义属性y
                        span[i].setAttribute('y', 3 - xBox);
                        //移动
                        span[i].style.left = 14 + (3 - xBox) * (100 + 12) + 'px';
                    }
                    break;
                case 'top':
                    //如果当前块位于最上边则
                    if (x == 0) {
                        continue;
                    } else {
                        //获取当前块上边有多少个块
                        let yBox = 0;
                        for (var j = 0; j < x; j++) {
                            if (map[j][y] != 0) {
                                yBox++;
                            }
                        }
                        //改变自定义属性x
                        span[i].setAttribute('x', yBox);
                        //移动
                        span[i].style.top = 14 + yBox * (100 + 12) + 'px';
                    }
                    break;
                case 'bottom':
                    //如果当前块位于最下边则
                    if (x == 3) {
                        continue;
                    } else {
                        //获取当前块上边有多少个块
                        let yBox = 0;
                        for (var j = 3; j > x; j--) {
                            if (map[j][y] != 0) {
                                yBox++;
                            }
                        }
                        //改变自定义属性x
                        span[i].setAttribute('x', 3 - yBox);
                        //移动
                        span[i].style.top = 14 + (3 - yBox) * (100 + 12) + 'px';
                    }
                    break;
            }
        }
        //根据改变位置后的块修改map矩阵
        map = resetMap();
    }
4.合并及改变颜色的函数

(合并要根据方向进行不同的方式的合并)

//合并
    function merge(direction) {
        let span = wrap.querySelectorAll('span');
        for (let i = 0; i < map.length; i++) {
            //如果是左边
            if (direction == 'left') {
                for (let j = 0; j < map[i].length - 1; j++) {
                    if (map[i][j] != 0 && map[i][j] == map[i][j + 1]) {

                        for (let k = 0; k < span.length; k++) {
                            //移除这个块
                            if (Number(span[k].getAttribute('x')) == i && Number(span[k].getAttribute('y')) == (j + 1)) {
                                wrap.removeChild(span[k]);
                            }
                            //改变这个块的数字及标签上对应的坐标
                            if (Number(span[k].getAttribute('x')) == i && Number(span[k].getAttribute('y')) == j) {
                                //计算块相加得到的数字
                                let numAdd = Number(map[i][j + 1]) + Number(map[i][j]);
                                //改变自定义属性num的值
                                span[k].setAttribute('num', numAdd)
                                span[k].innerText = numAdd;
                                //改变颜色
                                changeColor(span[k]);
                                //计算总分
                                scroe += numAdd;
                            }
                        }
                        j++;
                    }
                }
            }
            //如果是右边
            if (direction == 'right') {
                for (let j = 3; j >= 1; j--) {
                    if (map[i][j] != 0 && map[i][j] == map[i][j - 1]) {

                        for (let k = 0; k < span.length; k++) {
                            //移除这个块
                            if (Number(span[k].getAttribute('x')) == i && Number(span[k].getAttribute('y')) == (j - 1)) {
                                wrap.removeChild(span[k]);
                            }
                            //改变这个块的数字及标签上对应的坐标
                            if (Number(span[k].getAttribute('x')) == i && Number(span[k].getAttribute('y')) == j) {
                                let numAdd = Number(map[i][j - 1]) + Number(map[i][j]);
                                span[k].setAttribute('num', numAdd)
                                span[k].innerText = numAdd;
                                changeColor(span[k]);
                                scroe += numAdd;
                            }
                        }
                        j--;
                    }
                }
            }
            //如果是上边
            if (direction == 'top') {
                for (let j = 0; j < map.length - 1; j++) {

                    if (map[j][i] != 0 && map[j][i] == map[j + 1][i]) {

                        for (let k = 0; k < span.length; k++) {
                            //移除这个块
                            if (Number(span[k].getAttribute('x')) == j + 1 && Number(span[k].getAttribute('y')) == i) {
                                wrap.removeChild(span[k]);
                            }
                            //改变这个块的数字及标签上对应的坐标
                            if (Number(span[k].getAttribute('x')) == j && Number(span[k].getAttribute('y')) == i) {
                                let numAdd = Number(map[j + 1][i]) + Number(map[j][i]);
                                span[k].setAttribute('num', numAdd)
                                span[k].innerText = numAdd;
                                changeColor(span[k]);
                                scroe += numAdd;
                            }
                        }
                        j++;
                    }
                }
            }
            //如果是下边
            if (direction == 'bottom') {
                for (let j = 3; j >= 1; j--) {

                    if (map[j][i] != 0 && map[j - 1][i] == map[j][i]) {

                        for (let k = 0; k < span.length; k++) {
                            //移除这个块
                            if (Number(span[k].getAttribute('x')) == j - 1 && Number(span[k].getAttribute('y')) == i) {
                                wrap.removeChild(span[k]);
                            }
                            //改变这个块的数字及标签上对应的坐标
                            if (Number(span[k].getAttribute('x')) == j && Number(span[k].getAttribute('y')) == i) {
                                let numAdd = Number(map[j - 1][i]) + Number(map[j][i]);
                                span[k].setAttribute('num', numAdd)
                                span[k].innerText = numAdd;
                                changeColor(span[k]);
                                scroe += numAdd;
                            }
                        }
                        j--;
                    }
                }
            }
        }
        map = resetMap();
        moveBox(direction);
        document.querySelector('.score em').innerText = scroe;
        randomBlock(wrap);

    }
    //改变颜色
    function changeColor(dom) {
        let num = dom.getAttribute('num');
        switch (num) {
            case '2': dom.style.backgroundColor = '#eee4da'; break;
            case '4': dom.style.backgroundColor = '#ede0c8'; break;
            case '8': dom.style.backgroundColor = '#f2b179'; break;
            case '16': dom.style.backgroundColor = '#f59563'; break;
            case '32': dom.style.backgroundColor = '#f67c5f'; break;
            case '64': dom.style.backgroundColor = '#f65e3b'; break;
            case '128': dom.style.backgroundColor = '#edcf72'; break;
            case '256': dom.style.backgroundColor = '#edcc61'; break;
            case '512': dom.style.backgroundColor = '#edc850'; break;
            case '1024': dom.style.backgroundColor = '#edc53f'; break;
            case '2048': dom.style.backgroundColor = '#edc22e'; break;

        }
    }
5.重构map矩阵的函数
//根据标签对应的位置重构map
    function resetMap() {
        let span = wrap.querySelectorAll('span');
        map = mapInit(map);
        for (var i = 0; i < span.length; i++) {
            let x = Number(span[i].getAttribute('x'));
            let y = Number(span[i].getAttribute('y'));
            map[x][y] = Number(span[i].getAttribute('num'));
        }
        return map;
    }
6.根据按键进行移动
//触屏控制移动方向
    document.ontouchstart = function (e) {
        var e = e || window.event;
        //获取开始触碰的坐标
        let xStart = e.touches[0].clientX;
        let yStart = e.touches[0].clientY;

        document.ontouchend = function (e) {
            var e = e || window.event;
            //获取触碰结束的坐标
            let xEnd = e.changedTouches[0].clientX;
            let yEnd = e.changedTouches[0].clientY;
            //坐标差
            let xDif = xEnd - xStart;
            let yDif = yEnd - yStart;
            //根据坐标差移动
            if (xDif >= 50) { direction = 'right'; toMove('right'); }
            else if (xDif <= -50) { direction = 'left'; toMove('left') }
            else if (yDif >= 50) { direction = 'bottom'; toMove('bottom') }
            else if (yDif <= -50) { direction = 'top'; toMove('top') }
        }

    }
    //键盘控制移动
    document.onkeydown = function (e) {
        var e = e || window.event;
        //获取span块的dom元素
        switch (e.keyCode) {
            case 37:
                direction = 'left';
                toMove('left');
                break;
            case 38:
                direction = 'top';
                toMove('top');
                break;
            case 39:
                direction = 'right';
                toMove('right');
                break;
            case 40:
                direction = 'bottom';
                toMove('bottom');
                break;
        }
    }
    //执行移动
    function toMove(direction) {
        //获取span块的dom元素
        switch (direction) {
            case 'left':
                moveBox('left');
                merge(direction);
                break;
            case 'top':
                moveBox('top');
                merge(direction);
                break;
            case 'right':
                moveBox('right');
                merge(direction);
                break;
            case 'bottom':
                moveBox('bottom');
                merge(direction);
                break;
        }
    }
html代码
<p>
        <span class="title">2048</span>
        <span class="score">
            <i>SCORE</i>
            <em>0</em>
        </span>
    </p>

    <div class="wrap">
        <ul>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
        </ul>
    </div>
css样式
h1,h2,h3,h4,p,ul,li,body{
    margin: 0;
    padding: 0;
}

li{
    float: left;
    list-style: none;
}

i,em{
    font-style: normal;
}

p{
    display: flex;
    width: 456px;
    margin: 25px auto;
    justify-content: space-between;
    align-items: center;
}
p .title{
    font-size: 60px;
    color: #736d63;
    font-weight: 550;
}
p .score{
    width: 100px;
    height: 55px;
    display: flex;
    justify-content: center;
    flex-direction: column;
    text-align: center;
    background: #b5aa9c;
    color: white;
}
p .score i{
    font-size: 12px;
}
p .score em{
    font-size: 20px;
    font-weight: 550;
}
.wrap{
    width: 448px;
    height: 448px;
    padding: 8px;
    background: #bdae9c;
    margin: auto;
    border-radius: 10px;
    position: relative;
}
.wrap ul{
    width: 100%;
    height: 100%;
}
.wrap ul li{
    width: 100px;
    height: 100px;
    border-radius: 8px;
    margin: 6px;
    background: #c5baad;
}
.wrap span{
    width: 100px;
    height: 100px;
    position: absolute;
    background: #eee4da;
    border-radius: 8px;
    line-height: 100px;
    text-align: center;
    font-size: 50px;
    font-weight: 550;
    transition: all 0.2s;
}
游戏截图

在这里插入图片描述

体验地址

http://39.106.60.168/www/2048/

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值