从七个基本方块中,随机取一个,以600毫秒的速度,匀速从容器顶部开始下落,每次下落 20px。
若方块的底部,触碰到容器的底部,或触碰到其他方块,便停下来并变成灰色。
若下落的方块正好铺满一行,便自动消除该行。
然后再从七个基本方块中,随机取一个继续下落,周而复始。
关键点一:碰到其他方块变成灰色
在check函数里面添加overlap标识是否碰到其他块。这个的原理就是,在方块变成灰色的时候,根据方块变灰在屏幕中的位置组合为键,在container里面存一个标记。这样一来,检查当前下落的方块图形是否碰到其他方块图形就很简单了,只要判断当前方块图形的所有方块的左上角坐标是否在container里面拥有标识,如果check之后当前正在下落的方块图形与存在标识的坐标一致,那么就说明会发送同一个块的左上角坐标重叠,也就是碰撞,此时检查不通过,overlap为true。
完成后的完整代码如下:
<!DOCTYPE html>
<html>
<head>
<title>Snake</title>
<style type="text/css">
.activityModel { margin: 1px; width: 19px; height: 19px;background: red;position: absolute; }
/*.container { margin: 1px; top: 0px;left: 0px; width: 200px; height: 360px; background: black; position: absolute; }*/
.stationaryModel { margin: 1px; width: 19px; height: 19px; background: gray; position: absolute; }
.container { margin: 0px; top: 0px;left: 0px; width: 201px; height: 361px; background: black; position: absolute; }
</style>
<script type="text/javascript">
var SHAPES = [
[0, 1, 1, 1, 2, 1, 3, 1],
[1, 0, 1, 1, 1, 2, 2, 2],
[2, 0, 2, 1, 2, 2, 1, 2],
[0, 1, 1, 1, 1, 2, 2, 2],
[1, 2, 2, 2, 2, 1, 3, 1],
[1, 1, 2, 1, 1, 2, 2, 2],
[0, 2, 1, 2, 1, 1, 2, 2]
];
var x = 3;
var y = 0;
var size = 20;
var shape = [];
var rowCount=18;
var colCount=10;
var speed = 600;
var intervalId;
var intervalId2;
var container = {};
function init(){
create();
show(); //根据shape数组坐标排列组合创建的4个方块为方块图形
//绑定键盘事件
document.onkeydown = function(e){
var e= e || window.event;
switch(e.keyCode){
case 32: //space
quickDown();
break;
case 38: //rotate
rotate(0,-1);
break;
case 40: //down
move(0,1);
break;
case 37: //left
move(-1,0);
break;
case 39: //right
move(1,0);
break;
}
}
// 方块开始下降
intervalId = setInterval("move(0,1)",speed);
}
// 随机获取一个方块数组
var randomShape = function(){
shape = SHAPES[Math.floor(Math.random() * 7)];
}
// 创建4个方块
var create = function(){
randomShape();
for (var i=0;i<4;i++){
var div = document.createElement("div");
div.className = "activityModel";
document.body.appendChild(div);
}
}
//根据shape数组坐标排列组合创建的4个方块为方块图形
var show = function(){
var divs = document.getElementsByClassName("activityModel");
for(var j=0,index=0,len=divs.length;j<len,index<8;j++){
divs[j].style.left = (shape[index++]+x)*size +"px";
divs[j].style.top = (shape[index++]+y)*size +"px";
}
}
// 移动方块
var move = function(a,b){
//如果没有越界就更新相对位置,并按照新的相对位置渲染方块图形
if(check(x+a,y+b,shape)){
x += a;
y += b;
show();
} else {
if ( b == 0 ) return;
fix();
create();
show();
clearInterval(intervalId2);
}
}
// 快速下落
var quickDown = function() {
intervalId2 = setInterval("move(0, 1)", 0);
}
// 旋转方块
var rotate = function() {
newShape = [shape[1], 3 - shape[0], shape[3], 3 - shape[2], shape[5], 3 - shape[4], shape[7], 3 - shape[6]];
if(!check(x,y,newShape)) return;
shape = newShape;
show();
}
// 固定方块,变成灰色
var fix = function(){
var divs = document.getElementsByClassName("activityModel");
for(var i = divs.length -1;i >= 0;i--){
divs[i].className = "stationaryModel";
}
for(var j=0,index=0,len=divs.length;j<len,index<8;j++){
var px = shape[index++]+x;
var py = shape[index++]+y;
// if ( j < 4)
// {
// divs[j].className = "stationaryModel";
// }
container[px + "_" + py] = "has_gray_box";
}
x = 3;
y = 0;
}
var check = function(x,y,shape){
var most_left = colCount;
var most_right = 0;
var most_top = rowCount;
var most_bottom = 0;
var overlap = false;
for(var i=0;i<8;i+=2){
// 记录最左边水平坐标
if(shape[i]<most_left){
most_left=shape[i];
}
// 记录最右边水平坐标
if(shape[i]>most_right){
most_right=shape[i];
}
// 记录最上边垂直坐标
if(shape[i+1]<most_top){
most_top=shape[i+1];
}
// 记录最下边垂直坐标
if(shape[i+1]>most_bottom){
most_bottom=shape[i+1];
}
// 判断方块之间是否重叠,就是判断当前下落的方块每一个左上角坐标是否与已经变成灰色的方块左上角坐标存在重叠
var px = shape[i] + x;
var py = shape[i+1] + y;
if (container[px + "_" + py]){
overlap = true;
}
}
if( (most_right+x+1) > colCount || (most_left+x)< 0 || (most_bottom+y+1)>rowCount || (most_top+y)<0 || overlap ){
return false;
}
return true;
}
</script>
</head>
<body onload="init()">
<div class="container"></div>
</body>
</html>
有个隐藏的bug,就是一直按回车,方块会卡在空中。
关键点二:铺满一行就消除
添加findFull函数
思路就是前面我们在container存储了每个灰色左上角坐标的标识。我们遍历每一行,如果该行左上角坐标有在container对应的标识且达到列数,说明该行每一个方块都是灰色,于是调用removeLine函数清除该行。
注意:两大块的注释代码,我们在前面碰撞检测基础上继续添加消除功能,反而连碰撞检测效果都没了,于是有了两大块的注释代码。可以对比注释部分与非注释部分,比较区别。
完整源码:
<!DOCTYPE html>
<html>
<head>
<title>Snake</title>
<style type="text/css">
.activityModel { margin: 1px; width: 19px; height: 19px;background: red;position: absolute; }
/*.container { margin: 1px; top: 0px;left: 0px; width: 200px; height: 360px; background: black; position: absolute; }*/
.stationaryModel { margin: 1px; width: 19px; height: 19px; background: gray; position: absolute; }
.container { margin: 0px; top: 0px;left: 0px; width: 201px; height: 361px; background: black; position: absolute; }
</style>
<script type="text/javascript">
var SHAPES = [
[0, 1, 1, 1, 2, 1, 3, 1],
[1, 0, 1, 1, 1, 2, 2, 2],
[2, 0, 2, 1, 2, 2, 1, 2],
[0, 1, 1, 1, 1, 2, 2, 2],
[1, 2, 2, 2, 2, 1, 3, 1],
[1, 1, 2, 1, 1, 2, 2, 2],
[0, 2, 1, 2, 1, 1, 2, 2]
];
var x = 3;
var y = 0;
var size = 20;
var shape = [];
var rowCount=18;
var colCount=10;
var speed = 600;
var intervalId;
var intervalId2;
var shapeDiv= [];
var container = {};
function init(){
create();
show(); //根据shape数组坐标排列组合创建的4个方块为方块图形
//绑定键盘事件
document.onkeydown = function(e){
var e= e || window.event;
switch(e.keyCode){
case 32: //space
quickDown();
break;
case 38: //rotate
rotate(0,-1);
break;
case 40: //down
move(0,1);
break;
case 37: //left
move(-1,0);
break;
case 39: //right
move(1,0);
break;
}
}
// 方块开始下降
intervalId = setInterval("move(0,1)",speed);
}
// 随机获取一个方块数组
var randomShape = function(){
shape = SHAPES[Math.floor(Math.random() * 7)];
}
// 创建4个方块
var create = function(){
x = 3;
y = 0;
shapeDiv = [];
randomShape();
for (var i=0;i<4;i++){
var div = document.createElement("div");
div.className = "activityModel";
shapeDiv[i] = div;
document.body.appendChild(div);
}
}
//根据shape数组坐标排列组合创建的4个方块为方块图形
var show = function(){
var divs = document.getElementsByClassName("activityModel");
for(var j=0,index=0,len=divs.length;j<len,index<8;j++){
divs[j].style.left = (shape[index++]+x)*size +"px";
divs[j].style.top = (shape[index++]+y)*size +"px";
}
}
// 移动方块
var move = function(a,b){
//如果没有越界就更新相对位置,并按照新的相对位置渲染方块图形
if(check(x+a,y+b,shape)){
x += a;
y += b;
show();
} else {
if ( b == 0 ) return;
fix();
create();
show();
clearInterval(intervalId2);
}
}
// 快速下落
var quickDown = function() {
intervalId2 = setInterval("move(0, 1)", 0);
}
// 旋转方块
var rotate = function() {
newShape = [shape[1], 3 - shape[0], shape[3], 3 - shape[2], shape[5], 3 - shape[4], shape[7], 3 - shape[6]];
if(!check(x,y,newShape)) return;
shape = newShape;
show();
}
// // 固定方块,变成灰色
// var fix = function(){
// var divs = document.getElementsByClassName("activityModel");
// for(var i = divs.length -1;i >= 0;i--){
// divs[i].className = "stationaryModel";
// }
// for(var j=0,index=0,len=divs.length;j<len,index<8;j++){
// var px = shape[index++]+x;
// var py = shape[index++]+y;
// // if ( j < 4)
// // {
// // divs[j].className = "stationaryModel";
// // }
// if ( j < 4){
// container[px + "_" + py] = shapeDiv[j];
// }
// }
// findFull();
// }
// 固定方块,变成灰色,缓存到容器中
var fix = function() {
var divs = document.getElementsByClassName("activityModel");
for (var i = divs.length - 1; i >= 0; i--) {
var px = shape[2 * i + 1] + y;
var py = shape[2 * i] + x;
container[px + "_" + py] = shapeDiv[i];
divs[i].className = "stationaryModel";
}
findFull();
}
// 遍历整个容器,判断是否可以消除
var findFull = function() {
for (var m = 0; m < rowCount; m++) {
var count = 0;
for (var n = 0; n < colCount; n++) {
if (container[m + "_" + n])
count++;
}
if (count === colCount) {
removeLine(m);
}
}
}
// 消除指定一行方块
var removeLine = function(rowCount) {
// 移除一行方块
for (var n = 0; n < colCount; n++)
document.body.removeChild(container[rowCount + "_" + n]);
// 把所消除行上面所有的方块下移一行
for (var i = rowCount; i > 0; i--) {
for (var j = 0; j < colCount; j++) {
container[i + "_" + j] = container[(i - 1) + "_" + j]
if (container[i + "_" + j])
container[i + "_" + j].style.top = i * size + "px";
}
}
}
var check = function(x,y,shape){
var most_left = colCount;
var most_right = 0;
var most_top = rowCount;
var most_bottom = 0;
var overlap = false;
var divs = document.getElementsByClassName("activityModel");
for (var i = divs.length - 1; i >= 0; i--) {
// 记录方块最左边坐标
if (shape[2 * i] < most_left)
most_left = shape[2 * i];
// 记录方块最右边坐标
if (shape[2 * i] > most_right)
most_right = shape[2 * i];
// 记录方块最上边坐标
if (shape[2 * i + 1] < most_top)
most_top = shape[2 * i + 1];
// 记录方块最下边坐标
if (shape[2 * i + 1] > most_bottom)
most_bottom = shape[2 * i + 1];
// 判断方块之间是否重叠
var px = shape[2 * i + 1] + y;
var py = shape[2 * i] + x;
if (container[px + "_" + py])
overlap = true;
}
// for(var i=0;i<8;i+=2){
// // 记录最左边水平坐标
// if(shape[i]<most_left){
// most_left=shape[i];
// }
// // 记录最右边水平坐标
// if(shape[i]>most_right){
// most_right=shape[i];
// }
// // 记录最上边垂直坐标
// if(shape[i+1]<most_top){
// most_top=shape[i+1];
// }
// // 记录最下边垂直坐标
// if(shape[i+1]>most_bottom){
// most_bottom=shape[i+1];
// }
// // 判断方块之间是否重叠,就是判断当前下落的方块每一个左上角坐标是否与已经变成灰色的方块左上角坐标存在重叠
// var px = shape[i] + x;
// var py = shape[i+1] + y;
// if (container[px + "_" + py]){
// overlap = true;
// }
// }
if( (most_right+x+1) > colCount || (most_left+x)< 0 || (most_bottom+y+1)>rowCount || (most_top+y)<0 || overlap ){
return false;
}
return true;
}
</script>
</head>
<body onload="init()">
<div class="container"></div>
</body>
</html>
代码传送门:https://github.com/xiaohuacc/snake/blob/index006/index006.html