界面出现对应方块后,可自行控制其左右或快速下降的移动,上键可以实现方块形状的改变
调整代码结构
首先调整代码结构,建立JS文件,如图
实现键盘控制方块移动
game.js中代码
var Game = function () {
//dom元素
var gameDiv;
var nextDiv;
// 游戏矩阵
var gameData = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];
// 当前方块
var cur;
// 下一个方块
var next;
// divs
var nextDivs = [];
var gameDivs = [];
// 初始化div
var initDiv = function (container, data, divs) {
for (var i = 0; i < data.length; i++) {
var div = [];
for (var j = 0; j < data[0].length; j++) {
var newNode = document.createElement('div');
newNode.className = 'none';
newNode.style.top = (i * 20) + 'px';
newNode.style.left = (j * 20) + 'px';
// 建立一维数组
container.appendChild(newNode);
div.push(newNode);
}
// 把一维数组放到多维数组中
divs.push(div);
}
}
// 刷新div
var refreshDiv = function (data, divs) {
for (var i = 0; i < data.length; i++) {
for (var j = 0; j < data[0].length; j++) {
if (data[i][j] == 0) {
divs[i][j].className = 'none';
} else if (data[i][j] == 1) {
divs[i][j].className = 'done';
} else if (data[i][j] == 2) {
divs[i][j].className = 'current';
}
}
}
}
// 检测点是否合法,即是否降落到最底部——pos为方块原点位置,x=cur.origin.x,y=cur.origin.y,
var check = function (pos, x, y) {
if (pos.x + x < 0) {
// 超出上面
return false;
} else if (pos.x + x >= gameData.length) {
// 超出下面
return false;
} else if (pos.y + y < 0) {
// 到最左边
return false;
} else if (gameData[pos.x + x][pos.y + y] == 1) {
// 已经有落下来的方块了
return false;
} else {
return true;
}
}
// 检测数据是否合法,pos为原点,data为每次下降的方块的数据,为有方块存在的数据下降操作时做准备
var isValid = function (pos, data) {
for (var i = 0; i < data.length; i++) {
for (j = 0; j < data[0].length; j++) {
if (data[i][j] != 0) {
if (!check(pos, i, j)) {
// 不等于0且非法
return false;
}
}
}
}
return true;
}
// 清除数据
var clearData = function () {
for (var i = 0; i < cur.data.length; i++) {
if (check(cur.origin, i, j)) {
for (var j = 0; j < cur.data[0].length; j++) {
gameData[cur.origin.x + i][cur.origin.y + j] = 0;
}
}
}
}
// 设置数据
var setData = function () {
for (var i = 0; i < cur.data.length; i++) {
for (var j = 0; j < cur.data[0].length; j++) {
// 先判断是否合法
if (check(cur.origin, i, j)) {
// 将cur.data[i][j]中的数据拷贝到gameData数组中
gameData[cur.origin.x + i][cur.origin.y + j] = cur.data[i][j];
}
}
}
}
// 下移设置
var down = function () {
// 判断是否可以下降,方法写在square中
if (cur.canDown(isValid)) {
clearData();
cur.down();
setData();
refreshDiv(gameData, gameDivs);
return true;
} else {
// 不能再向下
return false;
}
}
// 旋转设置
var rotate = function () {
// 判断是否可以下降,方法写在square中
if (cur.canRotate(isValid)) {
clearData();
cur.rotate();
setData();
refreshDiv(gameData, gameDivs);
}
}
// 左移设置
var left = function () {
// 判断是否可以下降,方法写在square中
if (cur.canLeft(isValid)) {
clearData();
cur.left();
setData();
refreshDiv(gameData, gameDivs);
}
}
// 右移设置
var right = function () {
// 判断是否可以下降,方法写在square中
if (cur.canRight(isValid)) {
clearData();
cur.right();
setData();
refreshDiv(gameData, gameDivs);
}
}
// 初始化 doms对象包含两个内容gameDiv、nextDiv
var init = function (doms) {
gameDiv = doms.gameDiv;
nextDiv = doms.nextDiv;
let square = new SquareFactory();
cur = square.make(2, 2);
next = square.make(3, 3);
setData();
initDiv(gameDiv, gameData, gameDivs);
initDiv(nextDiv, next.data, nextDivs);
// 调用封装函数,将cur.data[i][j]中的数据拷贝到gameData数组中
setData();
refreshDiv(gameData, gameDivs);
refreshDiv(next.data, nextDivs);
}
// 导出API,在外部local里就可以调用这个init函数了
this.init = init;
this.down = down;
this.left = left;
this.right = right;
this.rotate = rotate;
this.fall = function () {
// 返回false就不能再下降
while (down());
}
}
注意这里在SquareFactory中又写了个make函数,所以在game.js调用的时候,需要先new一个实例,否则会出现未定义的情况
square.js中代码
var Square = function () {
// 方块数据
this.data = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
];
// 原点对象
this.origin = {
x: 0,
y: 0
}
// 方向,即旋转中的索引
this.dir = 0;
}
// 判断是否可以旋转
Square.prototype.canRotate = function (isValid) {
var d = (this.dir + 1) % 4;
var test = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
];
for (var i = 0; i < this.data.length; i++) {
for (var j = 0; j < this.data[0].length; j++) {
test[i][j] = this.rotates[d][i][j];
}
}
return isValid(this.origin, test);
}
Square.prototype.rotate = function (num) {
if (!num) num = 1;
this.dir = (this.dir + num) % 4;
for (var i = 0; i < this.data.length; i++) {
for (var j = 0; j < this.data[0].length; j++) {
this.data[i][j] = this.rotates[this.dir][i][j];
}
}
}
// 判断是否可以下降
Square.prototype.canDown = function (isValid) {
var test = {};
test.x = this.origin.x + 1;
test.y = this.origin.y;
return isValid(test, this.data);
}
Square.prototype.down = function () {
this.origin.x = this.origin.x + 1;
}
// 判断是否可以左移
Square.prototype.canLeft = function (isValid) {
var test = {};
test.x = this.origin.x;
test.y = this.origin.y - 1;
return isValid(test, this.data);
}
Square.prototype.left = function () {
this.origin.y = this.origin.y - 1;
}
// 判断是否可以右移
Square.prototype.canRight = function (isValid) {
var test = {};
test.x = this.origin.x;
test.y = this.origin.y + 1;
return isValid(test, this.data);
}
Square.prototype.right = function () {
this.origin.y = this.origin.y + 1;
}
squareFactory.js中代码
var Square1 = function () {
// 调用Square方法
Square.call(this);
// 旋转数组,枚举可能的情况 其中每个元素都是二维数组
this.rotates = [
[
[0, 2, 0, 0],
[2, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 0, 0, 0],
[2, 2, 0, 0],
[2, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 2, 2, 0],
[0, 2, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 0, 0],
[2, 2, 0, 0],
[0, 2, 0, 0],
[0, 0, 0, 0]
],
]
}
// 省去代码
Square1.prototype = Square.prototype;
var Square2 = function () {
// 调用Square方法
Square.call(this);
// 旋转数组,枚举可能的情况 其中每个元素都是二维数组
this.rotates = [
[
[0, 2, 0, 0],
[0, 2, 0, 0],
[0, 2, 0, 0],
[0, 2, 0, 0]
],
[
[0, 0, 0, 0],
[2, 2, 2, 2],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 0, 0],
[0, 2, 0, 0],
[0, 2, 0, 0],
[0, 2, 0, 0]
],
[
[0, 0, 0, 0],
[2, 2, 2, 2],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
]
}
// 判断是否可以旋转
Square2.prototype = Square.prototype;
var Square3 = function () {
// 调用Square方法
Square.call(this);
// 旋转数组,枚举可能的情况 其中每个元素都是二维数组
this.rotates = [
[
[2, 2, 2, 0],
[0, 0, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 0, 0],
[0, 2, 0, 0],
[2, 2, 0, 0],
[0, 0, 0, 0]
],
[
[2, 0, 0, 0],
[2, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 2, 0, 0],
[2, 0, 0, 0],
[2, 0, 0, 0],
[0, 0, 0, 0]
],
]
}
// 判断是否可以旋转
Square3.prototype = Square.prototype;
var Square4 = function () {
// 调用Square方法
Square.call(this);
// 旋转数组,枚举可能的情况 其中每个元素都是二维数组
this.rotates = [
[
[2, 2, 2, 0],
[2, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 2, 0, 0],
[0, 2, 0, 0],
[0, 2, 0, 0],
[0, 0, 0, 0]
],
[
[0, 0, 2, 0],
[2, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 0, 0, 0],
[2, 0, 0, 0],
[2, 2, 0, 0],
[0, 0, 0, 0]
],
]
}
// 判断是否可以旋转
Square4.prototype = Square.prototype;
var Square5 = function () {
// 调用Square方法
Square.call(this);
// 旋转数组,枚举可能的情况 其中每个元素都是二维数组
this.rotates = [
[
[2, 2, 0, 0],
[2, 2, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 2, 0, 0],
[2, 2, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 2, 0, 0],
[2, 2, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 2, 0, 0],
[2, 2, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
]
}
// 判断是否可以旋转
Square5.prototype = Square.prototype;
var Square6 = function () {
// 调用Square方法
Square.call(this);
// 旋转数组,枚举可能的情况 其中每个元素都是二维数组
this.rotates = [
[
[0, 2, 2, 0],
[2, 2, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 0, 0, 0],
[2, 2, 0, 0],
[0, 2, 0, 0],
[0, 0, 0, 0]
],
[
[0, 2, 2, 0],
[2, 2, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 0, 0, 0],
[2, 2, 0, 0],
[0, 2, 0, 0],
[0, 0, 0, 0]
],
]
}
// 判断是否可以旋转
Square6.prototype = Square.prototype;
var Square7 = function () {
// 调用Square方法
Square.call(this);
// 旋转数组,枚举可能的情况 其中每个元素都是二维数组
this.rotates = [
[
[2, 2, 2, 0],
[2, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 2, 0, 0],
[2, 2, 0, 0],
[2, 2, 0, 0],
[0, 0, 0, 0]
],
[
[2, 2, 2, 0],
[2, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[2, 2, 0, 0],
[2, 2, 0, 0],
[2, 2, 0, 0],
[0, 0, 0, 0]
],
]
}
// 判断是否可以旋转
Square7.prototype = Square.prototype;
var SquareFactory = function () {
SquareFactory.prototype.make = function (index, dir) {
var s;
index = index + 1;
switch (index) {
case 1:
s = new Square1();
break;
case 2:
s = new Square2();
break;
case 3:
s = new Square3();
break;
case 4:
s = new Square4();
break;
case 5:
s = new Square5();
break;
case 6:
s = new Square6();
break;
case 7:
s = new Square7();
break;
default:
break;
}
// 定义原点
s.origin.x = 0;
s.origin.y = 3;
s.rotate(dir);
return s;
}
}
local.js代码
var Local = function () {
// 游戏对象
var game;
// 绑定键盘事件
var bindKeyEvent = function () {
document.onkeydown = function (e) {
if (e.keyCode == 38) {
// 向上
game.rotate();
} else if (e.keyCode == 39) {
// 向右
game.right();
} else if (e.keyCode == 40) {
// 向下
game.down();
} else if (e.keyCode == 37) {
// 向左
game.left();
} else if (e.keyCode == 32) {
// 空格键
game.fall();
}
}
}
// 开始
var start = function () {
var doms = {
gameDiv: document.getElementById('game'),
nextDiv: document.getElementById('next')
}
game = new Game();
game.init(doms);
bindKeyEvent();
}
// 导出API
this.start = start;
}
script.js代码
// 创建local对象并调用
var local = new Local();
local.start();