<!DOCTYPE html>
<!-- saved from url=(0066)http://xzh-loop.github.io/Manji/lab/html5game/20141114-tetris.html -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>C# 文档</title>
</head>
<body><canvas id="canvas" width="502" height="500" style="border:1px solid black;"></canvas>
<div style="display:inline-block;">
A: 左移<br>
D:右移<br>
S:下移<br>
Space:变换方向<br>
</div>
<script>
var i, j, tmp,
score = 500,
eachScore = 0, //每块砖头值多少分
winScore = 20*30, //胜利所需分数
speed = 400, //初始速度ms, the more little the more fast
qia = 1, //关卡
qiaSpeed = 50, //每个关卡提升的速度
qiaScore = winScore, //每个关卡增加多少胜利分数(注意score每关会累积)
nBrick = Math.floor(Math.random()*5), //下一个砖头的形状
nIsOverturn = Math.floor(Math.random()*2),
nDirection = Math.floor(Math.random()*4),
BOARD = [],
SHAPE = ['Tian','Chu','Tu','Thunder','Line'],
canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
brickWidth = 20, //砖块大小
width = 400, height = 500; //画布宽高,20X25
ctx.strokeStyle = 'black'; //砖块边框颜色
// 主界面网格显示,@dev
// for(i=0;i<20;i++) {
// for(j=0;j<25;j++) {
// ctx.strokeRect(brickWidth*i, brickWidth*j, brickWidth, brickWidth); //注意+1和减2
// }
// }
function Brick() { }
Brick.prototype.embattle = null; //砖块的布局(需重载)
Brick.prototype.isOverturn = 0; //是否翻转
Brick.prototype.originX = 9; //砖头的绘制起点X
Brick.prototype.originY = -3; //砖头的绘制起点Y
Brick.prototype.direction = 0; //砖头朝向
Brick.prototype.autoMoveTimer = null; //自动移动计时器
Brick.prototype.draw = function() {
ctx.fillStyle = 'rgb('+Math.floor(Math.random()*256)+','+Math.floor(Math.random()*256)+','+Math.floor(Math.random()*256)+')';
for(i=0;i<4;i++) {
tmp = this.embattle[this.isOverturn][this.direction][i];
ctx.fillRect((this.originX+tmp%4)*brickWidth, (this.originY+Math.floor(tmp/4))*brickWidth, brickWidth, brickWidth);
ctx.strokeRect((this.originX+tmp%4)*brickWidth+1, (this.originY+Math.floor(tmp/4))*brickWidth+1, brickWidth-2, brickWidth-2); //注意+1和减2
}
}
Brick.prototype.move = function(moveX, moveY) {
//---终止判定---
tmp = this.embattle[this.isOverturn][this.direction];
var as = BOARD[this.originX+tmp[0]%4][this.originY+Math.floor(tmp[0]/4)+1],
bs = BOARD[this.originX+tmp[1]%4][this.originY+Math.floor(tmp[1]/4)+1],
cs = BOARD[this.originX+tmp[2]%4][this.originY+Math.floor(tmp[2]/4)+1],
ds = BOARD[this.originX+tmp[3]%4][this.originY+Math.floor(tmp[3]/4)+1];
// 横向移动违规
if((this.originX==0 && moveX==-1)
|| (this.originX+tmp[0]%4==19 && moveX==1)
|| (this.originX+tmp[1]%4==19 && moveX==1)
|| (this.originX+tmp[2]%4==19 && moveX==1)
|| (this.originX+tmp[3]%4==19 && moveX==1)
|| (BOARD[this.originX+tmp[0]%4+moveX][this.originY+Math.floor(tmp[0]/4)]==1)
|| (BOARD[this.originX+tmp[1]%4+moveX][this.originY+Math.floor(tmp[1]/4)]==1)
|| (BOARD[this.originX+tmp[2]%4+moveX][this.originY+Math.floor(tmp[2]/4)]==1)
|| (BOARD[this.originX+tmp[3]%4+moveX][this.originY+Math.floor(tmp[3]/4)]==1)) {
return;
}
// 纵向终止
if((as==1 || bs==1 || cs==1 || ds==1) && moveX==0) {
clearInterval(this.autoMoveTimer);
this.autoMoveTimer = null;
BOARD[this.originX+tmp[0]%4][this.originY+Math.floor(tmp[0]/4)] = 1;
BOARD[this.originX+tmp[1]%4][this.originY+Math.floor(tmp[1]/4)] = 1;
BOARD[this.originX+tmp[2]%4][this.originY+Math.floor(tmp[2]/4)] = 1;
BOARD[this.originX+tmp[3]%4][this.originY+Math.floor(tmp[3]/4)] = 1;
// 全民爱消除~
ALabel:for(i=0;i<4;i++) {
var k = this.originY+Math.floor(tmp[i]/4);
for(j=0;j<20;j++) {
if(BOARD[j][k] == 0) {
continue ALabel;
}
}
ctx.clearRect(410,0,100,100);
ctx.save();
ctx.fillStyle = 'black';
score += 20*eachScore; //加分
ctx.fillText('分数:'+score,410,30);
ctx.fillText('关卡:'+qia,410,50);
ctx.fillText('胜利需:'+winScore,410,70);
ctx.restore();
for(j=k;j>0;j--) {
for(var p=0;p<20;p++) {
BOARD[p][j] = BOARD[p][j-1];
}
}
for(j=0;j<20;BOARD[j][0]=0,j++); //初始化第一行
for(var p=0;p<20;p++) {
for(j=0;j<=k;j++) {
if(BOARD[p][j]==0) {
ctx.clearRect(p*brickWidth, j*brickWidth, brickWidth, brickWidth);
} else {
ctx.fillRect(p*brickWidth, j*brickWidth, brickWidth, brickWidth);
ctx.strokeRect(p*brickWidth+1, j*brickWidth+1, brickWidth-2, brickWidth-2);
}
}
}
}
if(this.originY<0) {
alert("You fail!!!");
return;
}
while(score>=winScore) {
alert('You win!!!');
init();
return;
}
deleteObj(b); //为什么无法删除该对象?
NextBrick(); //出现下一个
return;
}
for(i=0;i<4;i++) {
tmp = this.embattle[this.isOverturn][this.direction][i];
ctx.clearRect((this.originX+tmp%4)*brickWidth, (this.originY+Math.floor(tmp/4))*brickWidth, brickWidth, brickWidth);
}
this.originX += moveX;
this.originY += moveY;
this.draw();
}
Brick.prototype.autoMove = function() {
var status, self = this;
this.autoMoveTimer = setInterval(function() {
status = self.move(0,1);
},speed);
}
Brick.prototype.change = function() {
var ox = this.originX, oy = this.originY,
originDirection = this.direction;
this.direction = (this.direction+1)%4,
tmp = this.embattle[this.isOverturn][this.direction];
// 变换方向之后如果有部分溢出了右边界,将整体左移
while(ox+tmp[0]%4 > 19 || ox+tmp[1]%4 > 19 || ox+tmp[2]%4 > 19 || ox+tmp[3]%4 > 19) {
ox -= 1;
}
if(BOARD[ox+tmp[0]%4][oy+Math.floor(tmp[0]/4)] == 0
&& BOARD[ox+tmp[1]%4][oy+Math.floor(tmp[1]/4)] == 0
&& BOARD[ox+tmp[2]%4][oy+Math.floor(tmp[2]/4)] ==0
&& BOARD[ox+tmp[3]%4][oy+Math.floor(tmp[3]/4)] ==0) {
// 清除原位置
for(i=0;i<4;i++) {
tmp = this.embattle[this.isOverturn][originDirection][i];
ctx.clearRect((this.originX+tmp%4)*brickWidth, (this.originY+Math.floor(tmp/4))*brickWidth, brickWidth, brickWidth);
}
this.originX = ox;
this.originY = oy;
this.draw();
} else {
this.direction = originDirection; //还原方向
}
}
function Tian(isModel) {
this.embattle = [
[ [0,1,4,5], [0,1,4,5], [0,1,4,5], [0,1,4,5] ], //布局表为4X4表格,数字为砖头位置
[ [0,1,4,5], [0,1,4,5], [0,1,4,5], [0,1,4,5] ] //布局表为4X4表格,数字为砖头位置
];
isModel || this.autoMove();
}
function Chu(isModel) {
this.embattle = [
[ [0,1,4,8], [0,4,5,6], [1,5,8,9], [0,1,2,6] ] , //布局表为4X4表格,数字为砖头位置
[ [0,1,5,9], [0,1,2,4], [0,4,8,9], [2,4,5,6] ] //次行为翻转的情况
];
isModel || this.autoMove();
}
function Tu(isModel) {
this.embattle = [
[ [0,4,5,8], [1,4,5,6], [1,4,5,9], [0,1,2,5] ], //布局表为4X4表格,数字为砖头位置
[ [0,4,5,8], [1,4,5,6], [1,4,5,9], [0,1,2,5] ] //次行为翻转的情况
];
isModel || this.autoMove();
}
function Thunder(isModel) {
this.embattle = [
[ [0,4,5,9], [1,2,4,5], [0,4,5,9], [1,2,4,5] ], //布局表为4X4表格,数字为砖头位置
[ [1,4,5,8], [0,1,5,6], [1,4,5,8], [0,1,5,6] ] //次行为翻转的情况
];
isModel || this.autoMove();
}
function Line(isModel) {
this.embattle = [
[ [0,4,8,12], [0,1,2,3], [0,4,8,12], [0,1,2,3] ], //布局表为4X4表格,数字为砖头位置
[ [0,4,8,12], [0,1,2,3], [0,4,8,12], [0,1,2,3] ] //布局表为4X4表格,数字为砖头位置
];
isModel || this.autoMove();
}
// 继承父类
Tian.prototype = new Brick();
Tian.prototype.constructor = Tian;
Chu.prototype = new Brick();
Chu.prototype.constructor = Chu;
Tu.prototype = new Brick();
Tu.prototype.constructor = Tu;
Thunder.prototype = new Brick();
Thunder.prototype.constructor = Thunder;
Line.prototype = new Brick();
Line.prototype.constructor = Line;
//--------------------------------------
init(1);
function init(isFirstInit) {
if(!isFirstInit) {
speed -= qia*qiaSpeed;
qia += 1;
winScore += qiaScore;
}
ctx.clearRect(0,0,500,500);
// 初始化BOARD,注意纵向有26个,最后一排用来判断是否触底
for(i=0;i<20;i++){
BOARD[i] = [];
for(j=0;j<26;j++) {
if(j==25) {
BOARD[i][j] = 1
} else {
BOARD[i][j] = 0;
}
}
}
// 初始界面的一些内容,如主界面与信息界面的分割线、分数、关卡
ctx.save();
ctx.moveTo(401,0);
ctx.strokeStyle = 'red';
ctx.lineWidth = '1';
ctx.lineTo(401,500);
ctx.stroke();
ctx.fillStyle = 'black';
ctx.fillText('分数:'+score,410,30);
ctx.fillText('关卡:'+qia,410,50);
ctx.fillText('胜利需:'+winScore,410,70);
ctx.restore();
NextBrick();
}
window.document.onkeydown = function(evt) { //事件处理是否也应该放进对象里?
evt = (evt) ? evt : window.event;
// console.log(evt.keyCode)
switch(evt.keyCode){
case 65: b.move(-1,0); break; //还区分大小写?
case 97: b.move(-1,0); break;
case 68: b.move(1,0); break;
case 100: b.move(1,0); break;
case 83: b.move(0,1); break;
case 115: b.move(0,1); break;
case 32: b.change(); break;
}
}
function NextBrick() {
b = new window[SHAPE[nBrick]]();
b.direction = nDirection;
b.isOverturn = nIsOverturn;
nBrick = Math.floor(Math.random()*5); //下一个的形状
nDirection = Math.floor(Math.random()*4); //下一个的方向
nIsOverturn = Math.floor(Math.random()*2); //下一个的翻转
ctx.clearRect(410,100,100,100);
ctx.strokeRect(410,100,80,80);
var show = new window[SHAPE[nBrick]](1);
var em = show.embattle[nIsOverturn][nDirection];
for(var u=0;u<4;u++) {
ctx.fillRect(410+em[u]%4*10,100+Math.floor(em[u]/4)*10,10,10);
ctx.strokeRect(410+em[u]%4*10+1,100+Math.floor(em[u]/4)*10+1,10-2,10-2)
}
// deleteObj(this, show); //这里无须手动回收
}
// 这里提供彻底清除对象的方法
function deleteObj(obj) {
// console.log(obj);
if(typeof(obj)==='object') {
for(var e in obj) {
// console.log(e);
if(typeof(e)==='object' && obj.hasOwnProperty(e)) {
deleteObj(obj[e]);
} else {
// console.log(Object.getOwnPropertyDescriptor(obj, e));
delete obj[e];
}
}
}
delete obj;
// console.log(obj);
}
</script></body></html>