数字华容道是个很简单的小游戏,今天就尝试使用canvas+js做个简单的数字华容道小游戏。有关于游戏的具体规则请上网查阅。
一如既往先写个html页面,这里我把游戏的界面水平居中了,这里预留了一个动画,在后面时间跳动的时候使用。
Document
数字华容道
0
然后我们要设置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));
大功告成,下面上图看游戏成功结果。
当然这个游戏还有很多可以扩展的内容,比如在方向移动时可以使用动画效果的移动,加个移动的声音,这里是简单的小游戏就不扩展了,大家可以尝试去完成这些功能,相信能学到更多。