纯js实现俄罗斯方块详解与源码
2017-08-23 17:54
573 查看
对于小白来说用js实现俄罗斯方块还是有难度的,网上找了很多代码看,有的很长难懂,有的短小精悍,但不只用到了js还用到了框架,对于还未接触框架的小白宝宝,也只能无奈自己是小白了,自己写不出来那就找一篇纯js代码,弄懂也是一种收获吧。so 接下来就是我的理解咯,有不对的地方请多多包涵
个人觉得思路还是很重要的,那我就先以我理解之后,来说说其思路
首先整个编程过程用到了六个数组吧,第一个是全局数组shapes,存放了7种俄罗斯方块的形状,之后的数组都是动态创建并赋值的,divs[]长度为4 存放要下落的方块,div2[]长度也是4用来存放预告方块的,对应的就有shape[]用来说明下落方块的形状是怎么样,shape2[]用来存放预告方块形状的,这两个形状都是随机产生的,还有一个隐形的数组用来存放显示界面中所有方块的,个人认为最难的就是理清显示界面和抽象数组之间的关系吧,先上一个思维导图让大家直观的理清思路,之后慢慢详解代码
<style> //设置好元素样式,到时候动态创建元素,直接设置其属性 根据类名确定样式
.c {
margin: 1px;
width: 19px;
height: 19px;
background: red;
position: absolute; //下落方块样式
}
.d {
margin: 1px;
width: 19px;
height: 19px;
background: gray;
position: absolute; //下落到不能下落方块样式 其实就改变了颜色
}
.f {
top: 0px;
left: 0px;
background: black;
position: absolute; //整个游戏显示界面
}
.e {
top: 0px;
background: #151515;
position: absolute; //预告方块界面
}
.g {
width: 100px;
height: 20px;
color: white;
position: absolute; //分数显示界面
}
</style>
开始JS代码
<script type="text/javascript">
var row = 18;
var col = 10;
var announcement = 6;
var size = 20;
var isOver = false;
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").split(";");
var tetris;
var container;
最难理解的可能就是shapes数组 .split(";");是将数组以;分割成一个个数组值,每个数组值单号代表top值,双号代表left值代表一种方块形状 此处上图
function createElm(tag, css) {
var elm = document.createElement(tag);
elm.className = css;
document.body.appendChild(elm);
return elm;
}
function Tetris(css, x, y, shape) {
// 创建4个div用来组合出各种方块
var myCss = css ? css : "c";
this.divs = [createElm("div", myCss), createElm("div", myCss), createElm("div", myCss), createElm("div", myCss)];
if(!shape) {
this.divs2 = [createElm("div", myCss), createElm("div", myCss), createElm("div", myCss), createElm("div", myCss)];
this.score = createElm("div", "g");
this.score.style.top = 10 * size + "px";
this.score.style.left = (col - -1) * size + "px";
this.score.innerHTML = "score:0";
}
this.container = null;
this.refresh = function() {
this.x = (typeof(x) != 'undefined') ? x : 3;
this.y = (typeof(y) != 'undefined') ? y : 0;
// 如果有传参,优先使用参数的,如果有预告,优先使用预告,都没有就自己生成
if(shape)
this.shape = shape;
else if(this.shape2)
this.shape = this.shape2;
else
this.shape = shape ? shape : shapes[Math.floor((Math.random() * shapes.length - 0.000000001))].split(",");
this.shape2 = shapes[Math.floor((Math.random() * shapes.length - 0.000000001))].split(",");
if(this.container && !this.container.check(this.x, this.y, this.shape)) {
isOver = true;
alert("游戏结束");
} else {
this.show();
this.showScore();
this.showAnnouncement();
}
}
// 显示方块
this.show = function() {
for(var i in this.divs) {
this.divs[i].style.top = (this.shape[i * 2 + 1] - -this.y) * size + "px";//顶部中间开始
this.divs[i].style.left = (this.shape[i * 2] - -this.x) * size + "px";
}
}
// 显示预告
this.showAnnouncement = function() {
for(var i in this.divs2) {
this.divs2[i].style.top = (this.shape2[i * 2 + 1] - -1) * size + "px";//定位在显示区
this.divs2[i].style.left = (this.shape2[i * 2] - -1 - -col) * size + "px";
}
}
// 显示分数
this.showScore = function() {
if(this.container && this.score) {
this.score.innerHTML = "score:" + this.container.score;
}
}
// 水平移动方块的位置
this.hMove = function(step) {
if(this.container.check(this.x - -step, this.y, this.shape)) {
this.x += step;
this.show();
}
}
// 垂直移动方块位置
this.vMove = function(step) {
if(this.container.check(this.x, this.y - -step, this.shape)) {
this.y += step;
this.show();
} else {
this.container.fixShape(this.x, this.y, this.shape);
this.container.findFull();
this.refresh();
}
}
// 旋转方块
this.rotate = function() {
var newShape = [this.shape[1], 3 - this.shape[0], this.shape[3], 3 - this.shape[2], this.shape[5], 3 - this.shape[4], this.shape[7], 3 - this.shape[6]];
if(this.container.check(this.x, this.y, newShape)) {
this.shape = newShape;
this.show();
}
}
this.refresh();
}
function Container() {
this.init = function() {
// 绘制方块所在区域
var bgDiv = createElm("div", "f");
bgDiv.style.width = size * col + "px";
bgDiv.style.height = size * row + "px";
// 绘制预告所在区域
var bgDiv = createElm("div", "e");
bgDiv.style.left = size * col + "px";
bgDiv.style.width = size * announcement + "px";
bgDiv.style.height = size * row + "px";
// 清空积分
this.score = 0;
}
this.check = function(x, y, shape) {
// 检查边界越界
var flag = false;
var leftmost = col;
var rightmost = 0;
var undermost = 0;
for(var i = 0; i < 8; i += 2) {
// 记录最左边水平坐标
if(shape[i] < leftmost)
leftmost = shape[i];
// 记录最右边水平坐标
if(shape[i] > rightmost)
rightmost = shape[i];
// 记录最下边垂直坐标
if(shape[i + 1] > undermost)
undermost = shape[i + 1];
// 判断是否碰撞
if(this[(shape[i + 1] - -y) * 100 - -(shape[i] - -x)])
flag = true;
}
// 判断是否到达极限高度
for(var m = 0; m < 3; m++)
for(var n = 0; n < col; n++)
if(this[m * 100 + n])
flag = true;
if((rightmost - -x + 1) > col || (leftmost - -x) < 0 || (undermost - -y + 1) > row || flag)
return false;
return true;
}
// 用灰色方块替换红色方块,并在容器中记录灰色方块的位置
this.fixShape = function(x, y, shape) {
var t = new Tetris("d", x, y, shape);
for(var i = 0; i < 8; i += 2)
this[(shape[i + 1] - -y) * 100 - -(shape[i] - -x)] = t.divs[i / 2];
}
// 遍历整个容器,判断是否可以消除
this.findFull = function() {
var s = 0;
for(var m = 0; m < row; m++) {
var count = 0;
for(var n = 0; n < col; n++)
if(this[m * 100 + n])
count++;
if(count == col) {
s++;
this.removeLine(m);
}
}
this.score -= -this.calScore(s);
}
this.calScore = function(s) {
if(s != 0)
return s - -this.calScore(s - 1)
else
return 0;
}
// 消除指定一行方块
this.removeLine = function(row) {
// 移除一行方块
for(var n = 0; n < col; n++)
document.body.removeChild(this[row * 100 + n]);
// 把所消除行上面所有的方块下移一行
for(var i = row; i > 0; i--) {
for(var j = 0; j < col; j++) {
this[i * 100 - -j] = this[(i - 1) * 100 - -j]//数组中 消除行上的所有元素逐行下移 (等同于在数组改变位置)
if(this[i * 100 - -j])//如果此下标有元素 让其定位到要显示的位置
this[i * 100 - -j].style.top = i * size + "px";
}
}
}
}
function init() {
container = new Container();
container.init();
tetris = new Tetris();
tetris.container = container;
document.onkeydown = function(e) {
if(isOver) return;
var e = window.event ? window.event : e;
switch(e.keyCode) {
case 38: //up
tetris.rotate();
break;
case 40: //down
tetris.vMove(1);
break;
case 37: //left
tetris.hMove(-1);
break;
case 39: //right
tetris.hMove(1);
break;
}
}
setInterval("if(!isOver) tetris.vMove(1)", 500);
}
</script>
</head>
<body onload="init()">
</body>
</html>
个人觉得思路还是很重要的,那我就先以我理解之后,来说说其思路
首先整个编程过程用到了六个数组吧,第一个是全局数组shapes,存放了7种俄罗斯方块的形状,之后的数组都是动态创建并赋值的,divs[]长度为4 存放要下落的方块,div2[]长度也是4用来存放预告方块的,对应的就有shape[]用来说明下落方块的形状是怎么样,shape2[]用来存放预告方块形状的,这两个形状都是随机产生的,还有一个隐形的数组用来存放显示界面中所有方块的,个人认为最难的就是理清显示界面和抽象数组之间的关系吧,先上一个思维导图让大家直观的理清思路,之后慢慢详解代码
<style> //设置好元素样式,到时候动态创建元素,直接设置其属性 根据类名确定样式
.c {
margin: 1px;
width: 19px;
height: 19px;
background: red;
position: absolute; //下落方块样式
}
.d {
margin: 1px;
width: 19px;
height: 19px;
background: gray;
position: absolute; //下落到不能下落方块样式 其实就改变了颜色
}
.f {
top: 0px;
left: 0px;
background: black;
position: absolute; //整个游戏显示界面
}
.e {
top: 0px;
background: #151515;
position: absolute; //预告方块界面
}
.g {
width: 100px;
height: 20px;
color: white;
position: absolute; //分数显示界面
}
</style>
开始JS代码
<script type="text/javascript">
var row = 18;
var col = 10;
var announcement = 6;
var size = 20;
var isOver = false;
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").split(";");
var tetris;
var container;
最难理解的可能就是shapes数组 .split(";");是将数组以;分割成一个个数组值,每个数组值单号代表top值,双号代表left值代表一种方块形状 此处上图
function createElm(tag, css) {
var elm = document.createElement(tag);
elm.className = css;
document.body.appendChild(elm);
return elm;
}
function Tetris(css, x, y, shape) {
// 创建4个div用来组合出各种方块
var myCss = css ? css : "c";
this.divs = [createElm("div", myCss), createElm("div", myCss), createElm("div", myCss), createElm("div", myCss)];
if(!shape) {
this.divs2 = [createElm("div", myCss), createElm("div", myCss), createElm("div", myCss), createElm("div", myCss)];
this.score = createElm("div", "g");
this.score.style.top = 10 * size + "px";
this.score.style.left = (col - -1) * size + "px";
this.score.innerHTML = "score:0";
}
this.container = null;
this.refresh = function() {
this.x = (typeof(x) != 'undefined') ? x : 3;
this.y = (typeof(y) != 'undefined') ? y : 0;
// 如果有传参,优先使用参数的,如果有预告,优先使用预告,都没有就自己生成
if(shape)
this.shape = shape;
else if(this.shape2)
this.shape = this.shape2;
else
this.shape = shape ? shape : shapes[Math.floor((Math.random() * shapes.length - 0.000000001))].split(",");
this.shape2 = shapes[Math.floor((Math.random() * shapes.length - 0.000000001))].split(",");
if(this.container && !this.container.check(this.x, this.y, this.shape)) {
isOver = true;
alert("游戏结束");
} else {
this.show();
this.showScore();
this.showAnnouncement();
}
}
// 显示方块
this.show = function() {
for(var i in this.divs) {
this.divs[i].style.top = (this.shape[i * 2 + 1] - -this.y) * size + "px";//顶部中间开始
this.divs[i].style.left = (this.shape[i * 2] - -this.x) * size + "px";
}
}
// 显示预告
this.showAnnouncement = function() {
for(var i in this.divs2) {
this.divs2[i].style.top = (this.shape2[i * 2 + 1] - -1) * size + "px";//定位在显示区
this.divs2[i].style.left = (this.shape2[i * 2] - -1 - -col) * size + "px";
}
}
// 显示分数
this.showScore = function() {
if(this.container && this.score) {
this.score.innerHTML = "score:" + this.container.score;
}
}
// 水平移动方块的位置
this.hMove = function(step) {
if(this.container.check(this.x - -step, this.y, this.shape)) {
this.x += step;
this.show();
}
}
// 垂直移动方块位置
this.vMove = function(step) {
if(this.container.check(this.x, this.y - -step, this.shape)) {
this.y += step;
this.show();
} else {
this.container.fixShape(this.x, this.y, this.shape);
this.container.findFull();
this.refresh();
}
}
// 旋转方块
this.rotate = function() {
var newShape = [this.shape[1], 3 - this.shape[0], this.shape[3], 3 - this.shape[2], this.shape[5], 3 - this.shape[4], this.shape[7], 3 - this.shape[6]];
if(this.container.check(this.x, this.y, newShape)) {
this.shape = newShape;
this.show();
}
}
this.refresh();
}
function Container() {
this.init = function() {
// 绘制方块所在区域
var bgDiv = createElm("div", "f");
bgDiv.style.width = size * col + "px";
bgDiv.style.height = size * row + "px";
// 绘制预告所在区域
var bgDiv = createElm("div", "e");
bgDiv.style.left = size * col + "px";
bgDiv.style.width = size * announcement + "px";
bgDiv.style.height = size * row + "px";
// 清空积分
this.score = 0;
}
this.check = function(x, y, shape) {
// 检查边界越界
var flag = false;
var leftmost = col;
var rightmost = 0;
var undermost = 0;
for(var i = 0; i < 8; i += 2) {
// 记录最左边水平坐标
if(shape[i] < leftmost)
leftmost = shape[i];
// 记录最右边水平坐标
if(shape[i] > rightmost)
rightmost = shape[i];
// 记录最下边垂直坐标
if(shape[i + 1] > undermost)
undermost = shape[i + 1];
// 判断是否碰撞
if(this[(shape[i + 1] - -y) * 100 - -(shape[i] - -x)])
flag = true;
}
// 判断是否到达极限高度
for(var m = 0; m < 3; m++)
for(var n = 0; n < col; n++)
if(this[m * 100 + n])
flag = true;
if((rightmost - -x + 1) > col || (leftmost - -x) < 0 || (undermost - -y + 1) > row || flag)
return false;
return true;
}
// 用灰色方块替换红色方块,并在容器中记录灰色方块的位置
this.fixShape = function(x, y, shape) {
var t = new Tetris("d", x, y, shape);
for(var i = 0; i < 8; i += 2)
this[(shape[i + 1] - -y) * 100 - -(shape[i] - -x)] = t.divs[i / 2];
}
// 遍历整个容器,判断是否可以消除
this.findFull = function() {
var s = 0;
for(var m = 0; m < row; m++) {
var count = 0;
for(var n = 0; n < col; n++)
if(this[m * 100 + n])
count++;
if(count == col) {
s++;
this.removeLine(m);
}
}
this.score -= -this.calScore(s);
}
this.calScore = function(s) {
if(s != 0)
return s - -this.calScore(s - 1)
else
return 0;
}
// 消除指定一行方块
this.removeLine = function(row) {
// 移除一行方块
for(var n = 0; n < col; n++)
document.body.removeChild(this[row * 100 + n]);
// 把所消除行上面所有的方块下移一行
for(var i = row; i > 0; i--) {
for(var j = 0; j < col; j++) {
this[i * 100 - -j] = this[(i - 1) * 100 - -j]//数组中 消除行上的所有元素逐行下移 (等同于在数组改变位置)
if(this[i * 100 - -j])//如果此下标有元素 让其定位到要显示的位置
this[i * 100 - -j].style.top = i * size + "px";
}
}
}
}
function init() {
container = new Container();
container.init();
tetris = new Tetris();
tetris.container = container;
document.onkeydown = function(e) {
if(isOver) return;
var e = window.event ? window.event : e;
switch(e.keyCode) {
case 38: //up
tetris.rotate();
break;
case 40: //down
tetris.vMove(1);
break;
case 37: //left
tetris.hMove(-1);
break;
case 39: //right
tetris.hMove(1);
break;
}
}
setInterval("if(!isOver) tetris.vMove(1)", 500);
}
</script>
</head>
<body onload="init()">
</body>
</html>
相关文章推荐
- 详解通过源码解析Node.js中cluster模块的主要功能实现
- D3.js 实现Sequences sunburst的源码详解
- JS实现导出Excel的五种方法详解【附源码下载】
- Extjs源码中core目录下的Ext.js详解
- 网站JS源码1--产品分类下拉菜单实现原理
- 纯js实现俄罗斯方块[效率待优化]
- [转]网站JS源码1--产品分类下拉菜单实现原理
- JS中简单的实现像C#中using功能(有源码下载)
- android widget 开发实例 : 桌面便签程序的实现详解和源码 (上)
- JS实现数字转换为货币汉字大写数字的方法详解
- 修改ETM,用Ogre实现《天龙八部》地形与部分场景详解(附源码)
- 修改ETM,用Ogre实现《天龙八部》地形与部分场景详解(附源码)
- 【iOS-Cocos2d游戏开发之十五】详解CCProgressTimer 进度条并修改cocos2d源码实现“理想”游戏进度条!
- 修改ETM,用Ogre实现《天龙八部》地形与部分场景详解(附源码)
- 修改ETM,用Ogre实现《天龙八部》地形与部分场景详解(附源码)
- 读GI源码、学JS编程——JavaScript中的事件实现(完整版)。
- [原](源码)JS OOP 实现页面浮动图片,支持多图同时浮动,简单调用
- 提示信息窗口的JS实现(附源码)_AX
- android widget 开发实例 : 桌面便签程序的实现详解和源码 (下)
- Js之软键盘实现(源码)