前言
最近在整理一些旧的文档,发现了以前用js写的小游戏.发出来分享一下.
功能说明
仿照以前game boy的俄罗斯方块游戏
[左][右]控制移动,[上]控制旋转,[下]加速下降.[Esc]是暂停
消除行可得分,分数越高方块默认下降速度越快 可以看得到得分及下一方块信息
页面主要包含三个对象
1.主体(方块要堆积的地方)
2.下一方块提示
3.信息区
设计上比较关键的几个方法,在代码中有简单注释
1.设置了一个数组用来初始化所有方块类型
2.用两个变量记录当前方块及下一方块
3.方块的初始化方法,旋转方法
4.方块的碰撞检测方法
5.行消除方法,得分计算
6.方块自动下降及速度控制
详细代码
<html>
<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;
}
.info {
top: 0px;
background: black;
position: absolute;
color: white;
}
</style>
<body></body>
<html>
<script>
var over = false;
var stop = false;
var level = 1;
//定义方块形状
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 nextShape = null;
/**
* 创建方块元素
* @param tag 元素标签
* @param css 样式
*/
function create(tag, css) {
var elm = document.createElement(tag);
elm.className = css;
document.body.appendChild(elm);
return elm;
}
/**
* 获得下落的方块
* @param c css样式名称
* @param t 方块坐标点集合,方块样式集合
* @param x 初始位置x位置
* @param y 初始位置y位置
*/
function Tetris(c, t, x, y) {
var c = c ? c : "c";
//当前方块
this.divs = [create("div", c), create("div", c), create("div", c), create("div", c)];
//下一方块
if ("d" != c) {
this.nextDivs = [create("div", c), create("div", c), create("div", c), create("div", c)];
}
/**
* 重置
*/
this.reset = function () {
this.x = typeof x != 'undefined' ? x : 3;
this.y = typeof y != 'undefined' ? y : 0;
this.shape = t ? t : (nextShape ? nextShape : shapes[Math.floor(Math.random() * (shapes.length - 0.00001))].split(","));
if ("d" != c) {
nextShape = t ? t : shapes[Math.floor(Math.random() * (shapes.length - 0.00001))].split(",");
}
this.show();
if (this.field && this.field.check(this.shape, this.x, this.y, 'v') == 'D') {
over = true;
this.field.fixShape(this.shape, this.x, this.y);
alert('game over');
}
};
/**
* 显示
*/
this.show = function () {
//当前方块组装
for (var i in this.divs) {
this.divs[i].style.left = (this.shape[i * 2] * 1 + this.x) * 20 + 'px';
this.divs[i].style.top = (this.shape[i * 2 + 1] * 1 + this.y) * 20 + 'px';
}
if ("d" != c) {
//下一方块组装
for (var i in this.nextDivs) {
this.nextDivs[i].style.left = (nextShape[i * 2] * 1 + 16) * 20 + 'px';
this.nextDivs[i].style.top = (nextShape[i * 2 + 1] * 1 + 0) * 20 + 'px';
}
}
};
this.field = null;
/**
* 左右移动
* @param step
*/
this.hMove = function (step) {
if(stop) return;
var r = this.field.check(this.shape, this.x - -step, this.y, 'h');
if (r != 'N' && r == 0) {
this.x -= -step;
this.show();
}
};
/**
* 向下移动
*/
this.vMove = function () {
if(stop) return;
if (this.field.check(this.shape, this.x, this.y - -1, 'v') == 'N') {
this.y++;
this.show();
}
else {
//冻结方块
this.field.fixShape(this.shape, this.x, this.y);
//查找填满的行,并消除
this.field.findFull();
//重置方块
this.reset();
}
};
/**
* 旋转
*/
this.rotate = function () {
if(stop) return;
var s = this.shape;
var newShape = [3 - s[1], s[0], 3 - s[3], s[2], 3 - s[5], s[4], 3 - s[7], s[6]];
var r = this.field.check(newShape, this.x, this.y, 'h');
if (r == 'D') return;
if (r == 0) {
this.shape = newShape;
this.show();
}
else if (this.field.check(newShape, this.x - r, this.y, 'h') == 0) {
this.x -= r;
this.shape = newShape;
this.show();
}
};
/**
* 暂停开始
* @param step
*/
this.stop = function () {
stop=!stop;
if(stop){
document.getElementById("stop").innerText=("stop");
}else{
document.getElementById("stop").innerText=("begin");
}
};
//重置
this.reset();
}
/**
* 指定背景和初始化操作
* @param w 宽(格字数)
* @param h 高(格字数)
* @constructor
*/
function Field(w, h) {
//默认宽15格
this.width = w ? w : 15;
//默认高25格
this.height = h ? h : 25;
//显示
this.show = function () {
//创建背景画布
var f = create("div", "f");
f.style.width = this.width * 20 + 'px';
f.style.height = this.height * 20 + 'px';
//创建下一个方块信息框
var info = create("div", "info");
info.style.width = 4 * 20 + 'px';
info.style.height = 4 * 20 + 'px';
info.style.top = 0 * 20 + 'px';
info.style.left = (this.width + 1) * 20 + 'px';
//创建分数框
var scoreDiv = create("div", "info");
scoreDiv.style.width = 4 * 20 + 'px';
scoreDiv.style.height = 8 * 20 + 'px';
scoreDiv.style.top = 5 * 20 + 'px';
scoreDiv.style.left = (this.width + 1) * 20 + 'px';
//得分
var sourceInfo = document.createElement("p");
sourceInfo.id = "sourceInfo";
sourceInfo.innerText = "SOURCE:";
scoreDiv.appendChild(sourceInfo);
var source = document.createElement("p");
source.id = "source";
source.innerText = "0";
scoreDiv.appendChild(source);
//级别
var level = document.createElement("p");
level.id = "level";
level.innerText = "level: 1";
scoreDiv.appendChild(level);
//状态
var stop = document.createElement("p");
stop.id = "stop";
stop.innerText = "begin";
scoreDiv.appendChild(stop);
};
//查找拼接完整的一行
this.findFull = function () {
//计算一次消除多少行
var removeLineCount = 0;
//计算消除行
for (var l = 0; l < this.height; l++) {
var s = 0;
for (var i = 0; i < this.width; i++) {
s += this[l * this.width + i] ? 1 : 0;
}
if (s == this.width) {
removeLineCount++;
//消除行
this.removeLine(l);
}
}
//计算得分
var source = parseInt(document.getElementById("source").innerText);
switch (removeLineCount) {
case 0:
document.getElementById("source").innerText=(source+0);
break;
case 1:
document.getElementById("source").innerText=(source+10);
break;
case 2:
document.getElementById("source").innerText=(source+30);
break;
case 3:
document.getElementById("source").innerText=(source+60);
break;
case 4:
document.getElementById("source").innerText=(source+100);
break;
}
//计算级别
if(source/50>=level){
level++;
document.getElementById("level").innerText=("level: "+level);
clearInterval(timer);
timer=window.setInterval("if(!over&&!stop)s.vMove();", 400/level);
}
};
/**
* 消除行
* @param line 行号
*/
this.removeLine = function (line) {
for (var i = 0; i < this.width; i++) {
document.body.removeChild(this[line * this.width + i]);
}
for (var l = line; l > 0; l--) {
for (var i = 0; i < this.width; i++) {
this[l * this.width - -i] = this[(l - 1) * this.width - -i];
if (this[l * this.width - -i]) this[l * this.width - -i].style.top = l * 20 + 'px';
}
}
};
/**
* 旋转检查
* @param shape 方块集合
* @param x
* @param y
* @param d
* @returns {*}
*/
this.check = function (shape, x, y, d) {
var r1 = 0, r2 = 'N';
for (var i = 0; i < 8; i += 2) {
if (shape[i] - -x < 0 && shape[i] - -x < r1) {
r1 = shape[i] - -x;
}
else if (shape[i] - -x >= this.width && shape[i] - -x > r1) {
r1 = shape[i] - -x;
}
if (shape[i + 1] - -y >= this.height || this[shape[i] - -x - -(shape[i + 1] - -y) * this.width]) {
r2 = 'D'
}
}
if (d == 'h' && r2 == 'N') return r1 > 0 ? r1 - this.width - -1 : r1;
else return r2;
};
/**
* 冻结的下落方块
* @param shape
* @param x
* @param y
*/
this.fixShape = function (shape, x, y) {
//冻结方块
var d = new Tetris("d", shape, x, y);
d.show();
for (var i = 0; i < 8; i += 2) {
this[shape[i] - -x - -(shape[i + 1] - -y) * this.width] = d.divs[i / 2];
}
};
}
var f = new Field();
f.show();
var s = new Tetris();
s.field = f;
s.show();
//定时器,游戏未结束的情况下,每0.5s向下移动方块
var timer = window.setInterval("if(!over&&!stop)s.vMove();", 500);
//键盘监听
document.onkeydown = function (e) {
if (over) return;
var e = window.event ? window.event : e;
switch (e.keyCode) {
case 38: //up
s.rotate();
break;
case 40: //down
s.vMove();
break;
case 37: //left
s.hMove(-1);
break;
case 39: //right
s.hMove(1);
break;
case 27: //ESC暂停,开始
s.stop();
break;
}
}
</script>