<!doctype html>
<html>
<head>
<script type="text/javascript">
var canvas;
var ctx;
function init() {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
canvas2 = document.getElementById("canvas2");
ctx2 = canvas2.getContext("2d");
var TetrisGame = new tetrisgame(canvas,canvas2);
document.body.onkeydown = function (oEvent) {
TetrisGame.keyboardEventsListeners.call(TetrisGame, oEvent);
}
document.getElementById("BtnStart").onclick = function () {
TetrisGame.start.call(TetrisGame);
}
document.getElementById("BtnStop").onclick = function () {
TetrisGame.stop.call(TetrisGame);
}
}
//用于画地图的方块类
function Rect(x, y, size, context) {
this.x = x;
this.y = y;
this.size = size;
//是否着色的标志,由游戏控制类来改写
this.issetcolor = false;
this.color = "";
this.context = context;
}
//画边框,用于画地图
Rect.prototype.strokerect = function (sColor) {
this.context.strokeStyle = sColor;
this.context.strokeRect(this.x * this.size, this.y * this.size, this.size, this.size);
}
//填充颜色
Rect.prototype.fillrect = function (sColor) {
this.context.fillStyle = sColor;
this.context.fillRect(this.x * this.size + 1, this.y * this.size + 1, this.size - 2, this.size - 2);
this.color = sColor;
}
//清除颜色
Rect.prototype.clearrect = function () {
this.context.clearRect(this.x * this.size + 1, this.y * this.size + 1, this.size - 2, this.size - 2);
}
//地图类
function map(RectSize, color, canvas) {
this.width = canvas.width;
this.height = canvas.height;
this.RectSize = RectSize; //方块边长
this.backgroundcolor = color;
this.aMapArray = null;
this.ctx = canvas.getContext("2d");
this.born();
}
//地图由若干只有边框的方块组成
map.prototype.born = function () {
var i; var j;
var xCount;
var yCount;
xCount = this.width / this.RectSize;
yCount = this.height / this.RectSize;
this.aMapArray = new Array();
for (i = 0; i < xCount; i++) {
this.aMapArray[i] = new Array();
for (j = 0; j < yCount; j++) {
this.aMapArray[i][j] = new Rect(i, j, this.RectSize, this.ctx);
this.aMapArray[i][j].strokerect(this.backgroundcolor);
this.aMapArray[i][j].fillrect(this.backgroundcolor);
}
}
}
//地图刷新
map.prototype.refresh = function () {
var xCount;
var yCount;
xCount = this.width / this.RectSize;
yCount = this.height / this.RectSize;
for (var i = 0; i < xCount; i++) {
for (var j = 0; j < yCount; j++) {
this.aMapArray[i][j].fillrect("White");
this.aMapArray[i][j].fillrect(this.backgroundcolor);
this.aMapArray[i][j].issetcolor = false;
}
}
}
//获取地图上的方块
map.prototype.getrect = function (x, y) {
try {
return this.aMapArray[x][y];
}
catch (err) {
return null;
}
}
//俄罗斯方块父类
function tetris(x, y, size, scolor) {
//出生坐标
this.born_x = x;
this.born_y = y;
//方块边长
this.rectsize = size;
//存放方块的数组
this.rectarray = new Array();
//方块的颜色
this.color = scolor;
//方块名称
this.name = "";
//属于哪个地图
this.map = map;
}
tetris.prototype.setbornxy = function (x, y) {
this.born_x = x;
this.born_y = y;
}
//显示方块
tetris.prototype.show = function () {
var iCount = this.rectarray.length;
for (var i = 0; i < iCount; i++) {
this.rectarray[i].fillrect(this.color);
}
}
//清除方块
tetris.prototype.clear = function () {
var iCount = this.rectarray.length;
for (var i = 0; i < iCount; i++) {
//this.rectarray[i].clearrect();
this.rectarray[i].fillrect("Black");
}
}
//方块的旋转
tetris.prototype.trun = function () {
var iCount = this.rectarray.length;
var iTemp_x, iTemp_y, offset_x, offset_y;
/*
以rectarray的第一个方块为中心,
在定义方块时,需要将这个方块设置为中心
由于是按照中心方块来移动,所以中心方块坐标不用变。
*/
if (this.name != "D") {//我实在是找不出哪个字母比D更像“田”了
this.clear();
//取得方块所在矩阵偏移原点(0,0)的偏移量
offset_x = this.rectarray[0].x - 1;
offset_y = this.rectarray[0].y - 1;
for (var i = 1; i < iCount; i++) {
//方块坐标减去偏移量
iTemp_x = this.rectarray[i].x - offset_x;
iTemp_y = this.rectarray[i].y - offset_y;
/*
以原点坐标做矩阵旋转运算 参考公式 array[j][2 - i] = array[i][j]
逆时针旋转
计算完后,再加上偏移量
*/
this.rectarray[i].x = iTemp_y + offset_x;
this.rectarray[i].y = 2 - iTemp_x + offset_y;
}
this.show();
}
}
//方块左移
tetris.prototype.move_left = function () {
/*
左移就是x坐标减1
*/
var iCount = this.rectarray.length;
this.clear();
for (var i = 0; i < iCount; i++) {
this.rectarray[i].x = this.rectarray[i].x - 1;
}
this.show();
}
//方块右移
tetris.prototype.move_right = function (context) {
/*
右移就是x坐标加1
*/
var iCount = this.rectarray.length;
this.clear();
for (var i = 0; i < iCount; i++) {
this.rectarray[i].x = this.rectarray[i].x + 1;
}
this.show();
}
//方块下移
tetris.prototype.move_down = function (context) {
/*
下移就是y坐标加1
*/
var iCount = this.rectarray.length;
this.clear();
for (var i = 0; i < iCount; i++) {
this.rectarray[i].y = this.rectarray[i].y + 1;
}
this.show();
}
/*
所有方块继承自俄罗斯方块父类
ps:学习Javascript的继承 :)
*/
function tetris_T(x, y, size, scolor) {
tetris.call(this, x, y, size, scolor); //构造方法的继承
}
tetris_T.prototype = new tetris(); //属性和函数的继承
tetris_T.prototype.born = function (context) {
this.rectarray[0] = new Rect(this.born_x, this.born_y, this.rectsize, context); //中心方块
this.rectarray[1] = new Rect(this.born_x + 1, this.born_y, this.rectsize, context);
this.rectarray[2] = new Rect(this.born_x, this.born_y - 1, this.rectsize, context);
this.rectarray[3] = new Rect(this.born_x - 1, this.born_y, this.rectsize, context);
this.name = "T";
}
function tetris_1(x, y, size, scolor, map) {
tetris.call(this, x, y, size, scolor, map); //构造方法的继承
}
tetris_1.prototype = new tetris(); //属性和函数的继承
tetris_1.prototype.born = function (context) {
this.rectarray[0] = new Rect(this.born_x, this.born_y, this.rectsize, context);
this.rectarray[1] = new Rect(this.born_x - 1, this.born_y, this.rectsize, context);
this.rectarray[2] = new Rect(this.born_x + 1, this.born_y, this.rectsize, context);
this.rectarray[3] = new Rect(this.born_x + 2, this.born_y, this.rectsize, context);
this.name = "1";
}
function tetris_D(x, y, size, scolor, map) {
tetris.call(this, x, y, size, scolor, map); //构造方法的继承
}
tetris_D.prototype = new tetris(); //属性和函数的继承
tetris_D.prototype.born = function (context) {
this.rectarray[0] = new Rect(this.born_x, this.born_y, this.rectsize, context);
this.rectarray[1] = new Rect(this.born_x + 1, this.born_y, this.rectsize, context);
this.rectarray[2] = new Rect(this.born_x + 1, this.born_y - 1, this.rectsize, context);
this.rectarray[3] = new Rect(this.born_x, this.born_y - 1, this.rectsize, context);
this.name = "D";
}
function tetris_LL(x, y, size, scolor, map) {
tetris.call(this, x, y, size, scolor, map); //构造方法的继承
}
tetris_LL.prototype = new tetris(); //属性和函数的继承
tetris_LL.prototype.born = function (context) {
this.rectarray[0] = new Rect(this.born_x, this.born_y, this.rectsize, context);
this.rectarray[1] = new Rect(this.born_x + 1, this.born_y, this.rectsize, context);
this.rectarray[2] = new Rect(this.born_x - 1, this.born_y - 1, this.rectsize, context);
this.rectarray[3] = new Rect(this.born_x - 1, this.born_y, this.rectsize, context);
this.name = "LL";
}
function tetris_RL(x, y, size, scolor, map) {
tetris.call(this, x, y, size, scolor, map); //构造方法的继承
}
tetris_RL.prototype = new tetris(); //属性和函数的继承
tetris_RL.prototype.born = function (context) {
this.rectarray[0] = new Rect(this.born_x, this.born_y, this.rectsize, context);
this.rectarray[1] = new Rect(this.born_x + 1, this.born_y, this.rectsize, context);
this.rectarray[2] = new Rect(this.born_x + 1, this.born_y - 1, this.rectsize, context);
this.rectarray[3] = new Rect(this.born_x - 1, this.born_y, this.rectsize, context);
this.name = "RL";
}
function tetris_RZ(x, y, size, scolor, map) {
tetris.call(this, x, y, size, scolor, map); //构造方法的继承
}
tetris_RZ.prototype = new tetris(); //属性和函数的继承
tetris_RZ.prototype.born = function (context) {
this.rectarray[0] = new Rect(this.born_x, this.born_y, this.rectsize, context);
this.rectarray[1] = new Rect(this.born_x + 1, this.born_y - 1, this.rectsize, context);
this.rectarray[2] = new Rect(this.born_x, this.born_y - 1, this.rectsize, context);
this.rectarray[3] = new Rect(this.born_x - 1, this.born_y, this.rectsize, context);
this.name = "RZ";
}
function tetris_Z(x, y, size, scolor, map) {
tetris.call(this, x, y, size, scolor, map); //构造方法的继承
}
tetris_Z.prototype = new tetris(); //属性和函数的继承
tetris_Z.prototype.born = function (context) {
this.rectarray[0] = new Rect(this.born_x, this.born_y, this.rectsize, context);
this.rectarray[1] = new Rect(this.born_x + 1, this.born_y, this.rectsize, context);
this.rectarray[2] = new Rect(this.born_x, this.born_y - 1, this.rectsize, context);
this.rectarray[3] = new Rect(this.born_x - 1, this.born_y - 1, this.rectsize, context);
this.name = "Z";
}
//方块工厂类 用于产生不同形状的方块
function rectfactory(rectsize) {
this.rectsize = rectsize;
this.colorarray = new Array("red", "green", "blue", "orange", "purple", "pink", "yellow");
}
//产生方块
rectfactory.prototype.createfactory = function (x, y, witch, icolor) {
var tetris;
switch (witch) {
case 0:
tetris = new tetris_T(x, y, this.rectsize, this.colorarray[icolor]);
break;
case 1:
tetris = new tetris_1(x, y, this.rectsize, this.colorarray[icolor]);
break;
case 2:
tetris = new tetris_D(x, y, this.rectsize, this.colorarray[icolor]);
break;
case 3:
tetris = new tetris_LL(x, y, this.rectsize, this.colorarray[icolor]);
break;
case 4:
tetris = new tetris_RL(x, y, this.rectsize, this.colorarray[icolor]);
break;
case 5:
tetris = new tetris_RZ(x, y, this.rectsize, this.colorarray[icolor]);
break;
case 6:
tetris = new tetris_Z(x, y, this.rectsize, this.colorarray[icolor]);
break;
default:
tetris = null;
}
return tetris;
}
//计分类
function scoreBord() {
}
//游戏控制类
function tetrisgame(canvas,canvas2) {
this.rectsize = 20;
this.map = new map(this.rectsize, "Black", canvas);
//预览下一个俄罗斯方块的地图
this.map2 = new map(this.rectsize, "Black", canvas2);
this.tetrisfactory = new rectfactory(this.rectsize);
this.tetris = null; //当前俄罗斯方块
this.tetris2 = null; //下一个俄罗斯方块
this.rectcreate_x = parseInt(this.map.aMapArray.length / 2);
this.score = 0;
this.scorebord = document.getElementById("score");
this.level = 0;//级别(随分数变化而增加)
this.isstart = false;
};
//判断是否Game Over 方块不能再向下移动时调用
tetrisgame.prototype.isgameover = function (tetris) {
var iCount = tetris.rectarray.length;
var bHas = false;
//有方块的y坐标小于0时,这个俄罗斯方块就算挂了
for (var i = 0; i < iCount; i++) {
if (tetris.rectarray[i].y < 0) {
bHas = true;
break;
}
}
return bHas;
}
tetrisgame.prototype.gameover = function () {
this.stop();
this.tetris = null;
this.tetris2 = null;
this.score = 0;
alert("Game Over!");
this.map.refresh();
this.map2.refresh();
};
//产生俄罗斯方块
tetrisgame.prototype.createrect = function () {
var icolor, ikind;
icolor = parseInt(Math.random() * (this.tetrisfactory.colorarray.length - 1));
ikind = parseInt(Math.random() * 6);
//return this.tetrisfactory.createfactory(this.rectcreate_x,-1,ikind,icolor);
return this.tetrisfactory.createfactory(2, 2, ikind, icolor);
}
//判断方块能否再继续向下移动
tetrisgame.prototype.iscanmove_down = function (tetris, map) {
var iCount = tetris.rectarray.length;
var bord_y = map.aMapArray[0].length;
var result = true;
var x, y, maprect;
for (var i = 0; i < iCount; i++) {
x = tetris.rectarray[i].x;
y = tetris.rectarray[i].y + 1;
maprect = map.getrect(x, y);
if (maprect == null && y < 0) {//方块刚产生时有些坐标超出了边界,这时不应该报错
continue;
}
else if (maprect == null && y >= bord_y) {//这个判断是用于刚产生方块时,就移出边界了
result = false;
break;
}
else if (maprect.issetcolor || y >= bord_y) {//超出边界和地图上的方块有着色了都不能移
result = false;
break;
}
}
return result;
}
//判断方块能否再继续向左移动
tetrisgame.prototype.iscanmove_left = function (tetris, map) {
var iCount = tetris.rectarray.length;
var result = true;
var x, y, maprect;
for (var i = 0; i < iCount; i++) {
x = tetris.rectarray[i].x - 1;
y = tetris.rectarray[i].y;
maprect = map.getrect(x, y);
if (maprect == null && x >= 0) {//方块刚产生时有些坐标超出了边界,这时不应该报错
continue;
}
else if (x < 0 || maprect.issetcolor) {//超出边界,地图方块着色都不能移
result = false;
break;
}
}
return result;
}
//判断方块能否再继续向右移动
tetrisgame.prototype.iscanmove_right = function (tetris, map) {
var iCount = tetris.rectarray.length;
var bord_x = map.aMapArray.length;
var bord_y = map.aMapArray[0].length;
var result = true;
var x, y, maprect;
for (var i = 0; i < iCount; i++) {
x = tetris.rectarray[i].x + 1;
y = tetris.rectarray[i].y;
maprect = map.getrect(x, y);
if (maprect == null && x < bord_x) {//判断逻辑同左移
continue;
}
else if (x >= bord_x || maprect.issetcolor) {
result = false;
break;
}
}
return result;
}
//判断方块能否旋转
tetrisgame.prototype.iscanturn = function (tetris, map) {
var iCount = tetris.rectarray.length;
var bord_x = map.aMapArray.length;
var bord_y = map.aMapArray[0].length;
var x, y, iTemp_x, iTemp_y, offset_x, offset_y;
var result = true;
var rect = null;
if (tetris.name != "D") {
offset_x = tetris.rectarray[0].x - 1;
offset_y = tetris.rectarray[0].y - 1;
for (var i = 1; i < iCount; i++) {
iTemp_x = tetris.rectarray[i].x - offset_x;
iTemp_y = tetris.rectarray[i].y - offset_y;
x = iTemp_y + offset_x;
y = 2 - iTemp_x + offset_y;
if (x < 0 || x >= bord_x || y >= bord_y) {//超出边界
result = false;
break;
}
else if (y < 0) {//方块刚产生时存在坐标小于0的情况
continue;
}
else {//着色的地方不能再旋转了
rect = map.getrect(x, y);
if (rect == null || rect.issetcolor) {
result = false;
break;
}
}
}
}
return result;
}
//方块落下后对方块所在坐标的地图方块标记已着色
tetrisgame.prototype.setmapcolor = function (tetris, map) {
var iCount = tetris.rectarray.length;
var x, y;
for (var i = 0; i < iCount; i++) {
x = tetris.rectarray[i].x;
y = tetris.rectarray[i].y;
map.aMapArray[x][y].issetcolor = true;
map.aMapArray[x][y].color = tetris.color;
}
}
//方块落下记录需要消除的层,然后消层,降落
tetrisgame.prototype.clearup = function (tetris, map) {
var iCount = tetris.rectarray.length;
var xCount = map.aMapArray.length;
var yCount = 0;
var bAll = false; //整行是否都着色了
var bHas = false; //是否有着色的
var cleararray = new Array();
//获取俄罗斯方块中方块的最大y坐标
for (var i = 0; i < iCount; i++) {
if (yCount < tetris.rectarray[i].y) {
yCount = tetris.rectarray[i].y;
}
}
//计算哪些层需要消除
for (var j = yCount; j >= 0; j--) {
bAll = true;
bHas = false;
for (var i = 0; i < xCount; i++) {
if (map.aMapArray[i][j].issetcolor) {
bHas = true;
}
else {
bAll = false;
}
}
//整行都是,则记录起来
if (bAll) {
cleararray[cleararray.length] = j;
}
//整行都没有则break
if (!bHas) {
break;
}
}
//消层;
this.doclearup(cleararray);
//降落
this.falldown(cleararray);
}
//消层
tetrisgame.prototype.doclearup = function (cleararray) {
var iCount = cleararray.length;
var xCount = this.map.aMapArray.length;
var score = 0;
var yTemp;
if (iCount > 0) {//根据记录中要消除的层数,将那一层填充为地图背景色
for (var j = 0; j < iCount; j++) {
yTemp = cleararray[j];
for (var i = 0; i < xCount; i++) {
this.map.aMapArray[i][yTemp].fillrect(this.map.backgroundcolor);
this.map.aMapArray[i][yTemp].issetcolor = false;
}
}
//消层后计算分数
score = this.calcscore(cleararray);
this.setscore(score);
}
}
//降落
tetrisgame.prototype.falldown = function (cleararray) {
var iCount = cleararray.length;
var xCount = this.map.aMapArray.length;
var yPre, yNow, color, bHasColor;
/*
1、cleararray记录的是消除的层数,下标最大的为最上面消除的层数
2、从最上面消除的层数循环降落,直至最下面消除的层数
3、降落其实就是将消除的那一层(y)着色为上一层(y - 1)的颜色
然后再向上降落,直至上层都没有了颜色则停止
*/
if (iCount > 0) {
for (var j = iCount - 1; j >= 0; j--) {//倒循环cleararray
bHasColor = true;
yNow = cleararray[j];
while (yNow > 0 && bHasColor) {//消除的那一层一次向上做降落动作,直到上一层没有着色的方块或者到顶了
bHasColor = false;
yPre = yNow - 1;
for (var i = 0; i < xCount; i++) {
color = this.map.aMapArray[i][yPre].color; //获取上一层的方块颜色
this.map.aMapArray[i][yNow].fillrect(color); //本层着色(颜色为上一层的颜色)
if (color != "Black") {
bHasColor = true;
this.map.aMapArray[i][yNow].issetcolor = true;
}
else {
this.map.aMapArray[i][yNow].issetcolor = false;
}
}
yNow = yNow - 1;
}
}
}
}
//设置分数
tetrisgame.prototype.setscore = function(score){
var temp;
this.score = this.score + score;
if (this.level < 9){
temp = this.score / 20;//20分一个等级
this.level = temp.toFixed(0);
}
this.scorebord.innerHTML = "得分: " + this.score;
}
//计算得分 得分公式 2 * (n - 1) + 1 n 为连续消除了n层
tetrisgame.prototype.calcscore = function(cleararray){
var iCount = cleararray.length;
var result,result1;
//获取所有层数的和
function sum(cleararray){
var sum = 0;
for (var i = 0; i < iCount ;i++ ){
sum = sum + cleararray[i];
}
return sum;
}
switch (iCount){
case 1 :
return 1;//消除1层得1分
break;
case 2 :
result = cleararray[0] - cleararray[1];
if (result == 1){//连续2层得3分
return 3;
}
else {
return 2;//不连续就得2分
}
break;
case 3 :
result = 3 * (cleararray[0] + cleararray[2]) / 2;
result1 = sum(cleararray);
if (result == result1){
return 7;//连续3层是7分
}
else{
/*
如果消除了3层,不是连续的话,
只可能是连续的2层 和不连续的1层,
得分都是 3 + 1 = 4
*/
return 4;
}
break;
case 4 ://如果消除了4层只可能是连续的4层
return 15;
break;
default :
return 0;
}
}
//直接调用 setTimeOut(this.start())不行,貌似是因为循环的时候this对象丢失
//以下是网上查到的办法
tetrisgame.prototype.delay = function (obj, fn, time) {
fnGameDelay = function () {
fn.call(obj); // call方法会把fn方法中的this关键字替换成obj对象
};
return setTimeout('fnGameDelay()', time);
};
//暂停
tetrisgame.prototype.stop = function () {
clearTimeout(this.timeOut);
};
//游戏开始
tetrisgame.prototype.start = function () {
var icolor, ikind, x, cleararray,level;
if (this.tetris == null && this.tetris2 == null) {
this.tetris = this.createrect();
this.tetris.setbornxy(this.rectcreate_x, -1);
this.tetris.born(this.map.ctx); //方块出生在哪个地图
this.tetris.show();
this.tetris2 = this.createrect();
this.tetris2.born(this.map2.ctx);
this.tetris2.show();
}
if (this.tetris == null) {
this.tetris = this.tetris2;
this.tetris.setbornxy(this.rectcreate_x, -1);
this.tetris.born(this.map.ctx);
this.tetris2 = this.createrect();
this.tetris2.born(this.map2.ctx);
this.map2.refresh();
this.tetris2.show();
}
if (this.iscanmove_down(this.tetris, this.map)) {
this.tetris.move_down();
}
else {
//是否gameover
if (this.isgameover(this.tetris)) {
this.gameover();
}
this.setmapcolor(this.tetris, this.map);
//消层
this.clearup(this.tetris, this.map);
this.tetris = null;
}
//设置动画
level = 1000 - this.level * 100;
this.timeOut = this.delay(this, this.start, level);
};
// 键盘监听事件
tetrisgame.prototype.keyboardEventsListeners = function (oEvent) {
if (window.event) {// ie
var direc = window.event.keyCode;
} else if (oEvent.which) {// ff
var direc = oEvent.which;
}
if (direc == 37 || direc == 65) {// 37-->left 65 --> A
if (this.iscanmove_left(this.tetris, this.map)) {
this.tetris.move_left();
}
}
else if (direc == 39 || direc == 68) {// 39-->right 68 --> D
if (this.iscanmove_right(this.tetris, this.map)) {
this.tetris.move_right();
}
}
else if (direc == 38 || direc == 87) {// 38-->up 87--> W
if (this.iscanturn(this.tetris, this.map)) {
this.tetris.trun();
}
}
else if (direc == 40 || direc == 83) {// 40-->down 83 --> S
/*
if (this.iscanmove_down(this.tetris,this.map)){
this.tetris.move_down();
}
*/
this.stop();
this.start();
}
else {
return;
}
};
</script>
<meta charset="gbk" />
<style type="text/css">
body
{
border: 0;
text-align: center;
}
#container
{
margin: 0 auto;
width: 800px;
}
#leftside, #top, #ctrl
{
float: left;
margin-left: 20px;
}
#score
{
font-size:20px;
font-style:normal;
color:"red";
text-align: center;
}
</style>
</head>
<body οnlοad="init()">
<div id="container">
<div id="leftside">
<canvas id="canvas" width="260" height="500" style="border: solid 15px #000; background-color: #FFF;">
<p>
该吃药了,亲!浏览器不支持canvas元素,建议使用最新版chrome,firefox等浏览器,ie就别用了</p>
</canvas>
</div>
<div id="top">
<canvas id="canvas2" width="100" height="100" style="border: solid 5px #000; background-color: #FFF;">
</canvas>
<br/>
<span id="score">
</span>
</div>
</div>
<div id="ctrl">
<input id="BtnStart" class="" type="button" value="点我开始" />
<input id="BtnStop" class="" type="button" value="点我暂停" />
</div>
</body>
</html>
自己写的俄罗斯方块(JS + canvas)
最新推荐文章于 2023-12-28 10:00:00 发布