canvas+js实现简单的数字华容道小游戏

数字华容道是个很简单的小游戏,今天就尝试使用canvas+js做个简单的数字华容道小游戏。有关于游戏的具体规则请上网查阅。

一如既往先写个html页面,这里我把游戏的界面水平居中了,这里预留了一个动画,在后面时间跳动的时候使用。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        canvas,
        #time,
        h1 {
            display: block;
            margin: 10px auto;
            width: 400px;
            text-align: center;
        }
        
        #time {
            font-size: 20px;
            text-align: center;
        }
        #time::after {
            content: "s";
        }
        .animate {
            animation: fontChange 0.4s linear 0s infinite none;
        }
        
        @keyframes fontChange {
            0% {
                transform: rotateX(0deg);
            }
            50% {
                transform: rotateX(90deg);
            }
            100% {
                transform: rotateX(0deg);
            }
        }
    </style>
</head>

<body>
    <h1>数字华容道</h1>
    <canvas id="canvas"></canvas>
    <span id="time">0</span>
    <script src="js/main.js"></script>
</body>

</html>

然后我们要设置canvas的宽高以及绘制方法的操作对象,还设置了一个用以判断当前是否完成游戏的布尔类型变量,这在后面可以用于判断停止计时。

let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 400;
var successFlag = false;

接下来是要将数字存储在对应的位置上,这里我用二维数组来存储各个位置上的数字。而要存储数字,就得给各个位置随机分配不重复的数字。并将生成的数组用一个全局变量arr存储。

//生成所需的二维数组
function arrProduce(n) {
    let arr = Array(4);
    arr.fill(0);
    let index = 1;
    arr = arr.map(item => {
        return Array(4).fill(0); //生成一个二维数组
    });
    while (true) {
        let randomC = Math.floor(Math.random() * Math.pow(16, 1 / 2)); //在随机位置填充数字
        let randomL = Math.floor(Math.random() * Math.pow(16, 1 / 2));
        if (arr[randomC][randomL] > 0) //重复时跳到下一个循环
            continue;
        else {
            arr[randomC][randomL] = index;
            index++;
        }
        if (index === n) //数字填充个数到达时跳出循环
            break;
    }
    return arr;
}

let arr = arrProduce(16);

写好存储位置信息的数组后,接下来就是将内容绘制出来。

//画出圆角正方形
function drawRct(x, y, l, r) {
    context.beginPath();
    context.moveTo(x + r, y);
    context.lineTo(x + l - r, y);
    context.arcTo(x + l, y, x + l, y + r, r);
    context.lineTo(x + l, y + l - r);
    context.arcTo(x + l, y + l, x + l - r, y + l, r);
    context.lineTo(x + r, y + l);
    context.arcTo(x, y + l, x, y + l - r, r);
    context.lineTo(x, y + r);
    context.arcTo(x, y, x + r, y, r);
    context.fillStyle = 'rgba(102,102,102,0.6)';
    context.closePath();
    context.fill();
    context.stroke();
}

//画出数字容器
function drawContainer() {
    drawRct(0, 0, 400, 10);
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            drawRct(10 + 100 * i, 10 + 100 * j, 80, 5);
        }
    }
}

//填充容器内容
function numbersWrite(arr) {
    for (var i = 0; i < arr.length; i++) {
        for (var j = 0; j < arr[i].length; j++) {
            context.beginPath();
            if (arr[i][j] === 0)
                arr[i][j] = '';
            context.textAlign = 'center';
            context.textBaseline = 'middle';
            context.font = '28px Adobe Ming Std';
            context.fillStyle = 'rgb(0,0,0)';
            context.fillText(arr[i][j], 50 + i * 100, 50 + j * 100);
            context.fill();
        }
    }
}

容器绘制好了,接下来就是操作了。这里我先找出空的位置,在按下按键时将块移动到空的位置,而第一次按下按键就开始计时

//找出空的位置
function findSpance(arr) {
    for (var i = 0; i < arr.length; i++) {
        for (var j = 0; j < arr[i].length; j++) {
            if (arr[i][j] === '')
                return { i, j };//返回空的位置
        }
    }
}

//计时
function timeCount(start, time) {
    setInterval(function() {
        if (!successFlag) { //还未成功继续计时
            time.className = 'animate';
            setTimeout(() => { time.innerHTML = Math.floor((new Date().getTime() - start) / 1000) }, 200);//在动画一半的时候改变数字,正好是翻转到水平时
            setTimeout(() => { time.className = '' }, 400);
        }
    }, 1000);
}

//位置移动
function posMove(pos) {
    var flag = false;
    document.onkeydown = event => {
        var e = event || window.event;
        switch (e.keyCode) {
            if (!flag) {//开始计时
                flag = true;
                let start = new Date().getTime();
                let time = document.getElementById('time');
                timeCount(start, time);
            }
            case 38: //上
                if (pos.j !== 3) {
                    arr[pos.i][pos.j] = arr[pos.i][pos.j + 1];
                    arr[pos.i][pos.j + 1] = '';
                    pos.j++;
                }
                break;
            case 37: //左
                if (pos.i !== 3) {
                    arr[pos.i][pos.j] = arr[pos.i + 1][pos.j];
                    arr[pos.i + 1][pos.j] = '';
                    pos.i++;
                }
                break;
            case 39: //右
                if (pos.i !== 0) {
                    arr[pos.i][pos.j] = arr[pos.i - 1][pos.j];
                    arr[pos.i - 1][pos.j] = '';
                    pos.i--;
                }
                break;
            case 40: //下
                if (pos.j !== 0) {
                    arr[pos.i][pos.j] = arr[pos.i][pos.j - 1];
                    arr[pos.i][pos.j - 1] = '';
                    pos.j--;
                }
                break;
        }
        context.clearRect(0, 0, canvas.width, canvas.height);//重置画面
        drawContainer();
        numbersWrite(arr);
        signTrue();//标记当前正确位置的数字,会在下面写出该方法
        if (judgeSuccess()) {//游戏成功,会在下面写出该方法
            alert(`恭喜你成功了 用时${document.getElementById('time').innerHTML}s`);
            successFlag = true;
            document.onkeydown = null;
        }
    }
}

操作写好后,就是写出成功条件了,只要数字全在对应的位置上,就算是游戏通关了,而为了让玩家更好地观察到当前还有几个位置不是正确的数字,我写了个方法将正确的位置用红色标出来。

//成功时的判断
function judgeSuccess() {
    let index = 1;
    for (let i = 0; i < arr.length; i++) {
        for (let j = 0; j < arr.length; j++) {
            if (index === 16)
                return true;
            if (arr[i][j] !== index)
                return false;
            index++;
        }
    }
    return true;
}

//标记正确位置
function signTrue() {
    let index = 1;
    for (let i = 0; i < arr.length; i++) {
        for (let j = 0; j < arr.length; j++) {
            if (arr[i][j] === index) {
                context.beginPath();
                context.textAlign = 'center';
                context.textBaseline = 'middle';
                context.font = '28px Adobe Ming Std';
                context.fillStyle = 'rgb(255,0,0)';
                context.fillText(arr[i][j], 50 + i * 100, 50 + j * 100);
                context.fill();
            }
            index++;
        }
    }
}

最后调用写过的方法。

drawContainer();
numbersWrite(arr);
posMove(findSpance(arr));

大功告成,下面上图看游戏成功结果。

当然这个游戏还有很多可以扩展的内容,比如在方向移动时可以使用动画效果的移动,加个移动的声音,这里是简单的小游戏就不扩展了,大家可以尝试去完成这些功能,相信能学到更多。

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值