一.简介
俄罗斯方块(Tetris, 俄文:Тетрис)是一款风靡全球的电视游戏机和掌上游戏机游戏,它由俄罗斯人阿列克谢·帕基特诺夫发明,故得此名。俄罗斯方块的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。由于上手简单、老少皆宜,从而家喻户晓,风靡世界。
二.需求分析
(完全按照QQ游戏的制作,如下图:)
三.技术分析与实现
1.方块位置定位
解决方案:建立盒子模型
由于长条的存在,所以建立一个4*4的盒子模型,任何一个方块都会存在该盒子当中,方块的定位就===盒子的定位。
2.颜色状态的生成与保存
随机生成颜色:
function randomColor() {
//16进制方式表示颜色0-F
var arrHex = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
var strHex = "#";
var index;
for (var i = 0; i < 6; i++) {
//取得0-15之间的随机整数
index = Math.round(Math.random() * 15);
strHex += arrHex[index];
}
return strHex;
//16进制方式表示颜色0-F
var arrHex = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
var strHex = "#";
var index;
for (var i = 0; i < 6; i++) {
//取得0-15之间的随机整数
index = Math.round(Math.random() * 15);
strHex += arrHex[index];
}
return strHex;
}
颜色保存:(那一个方块的一种状态做示例)
var diamonds = new Array();
diamonds[0] = { x: appearPosition.position.x + 1, y: appearPosition.position.y, diamondColor: color };
diamonds[1] = { x: appearPosition.position.x + 0, y: appearPosition.position.y + 1, diamondColor: color };
diamonds[2] = { x: appearPosition.position.x + 1, y: appearPosition.position.y + 1, diamondColor: color };
diamonds[3] = { x: appearPosition.position.x + 2, y: appearPosition.position.y + 1, diamondColor: color };
diamonds[0] = { x: appearPosition.position.x + 1, y: appearPosition.position.y, diamondColor: color };
diamonds[1] = { x: appearPosition.position.x + 0, y: appearPosition.position.y + 1, diamondColor: color };
diamonds[2] = { x: appearPosition.position.x + 1, y: appearPosition.position.y + 1, diamondColor: color };
diamonds[3] = { x: appearPosition.position.x + 2, y: appearPosition.position.y + 1, diamondColor: color };
所有生成的方块有个diamondColor属性,用于存颜色。appearPosition.position是盒子模型的位置。
3.碰撞检测
碰撞分两种,一种是元素与左右墙壁和底部的碰撞,另外一种是方块与底部方块的接触碰撞
a.元素与左右墙壁和底部的碰撞
a.1元素与底部的碰撞检测
if (diamonds[i].y * height + height >= canvasHeight) {
appearPosition.position.x = Math.round(appearPosition.position.x);
appearPosition.position.y = Math.round(appearPosition.position.y);
createElement();
breakTag = 1;
appearPosition.position.x = Math.round(appearPosition.position.x);
appearPosition.position.y = Math.round(appearPosition.position.y);
createElement();
breakTag = 1;
}
a.2元素与左右墙壁的碰撞检测
function returnRightOrLeft() {
var max_X = 11;
for (i = 0; i < diamonds.length; i++) {
if (diamonds[i].x > max_X) {
max_X = diamonds[i].x;
}
}
if (max_X != 11) appearPosition.position.x = appearPosition.position.x - (max_X - 11);
var min_X = 0;
for (i = 0; i < diamonds.length; i++) {
if (diamonds[i].x < min_X) {
min_X = diamonds[i].x;
}
}
if (min_X != 0) appearPosition.position.x = appearPosition.position.x - min_X;
}
var max_X = 11;
for (i = 0; i < diamonds.length; i++) {
if (diamonds[i].x > max_X) {
max_X = diamonds[i].x;
}
}
if (max_X != 11) appearPosition.position.x = appearPosition.position.x - (max_X - 11);
var min_X = 0;
for (i = 0; i < diamonds.length; i++) {
if (diamonds[i].x < min_X) {
min_X = diamonds[i].x;
}
}
if (min_X != 0) appearPosition.position.x = appearPosition.position.x - min_X;
}
b.元素与元素碰撞检测
//判断下面是否有元素
for (j = 0; j < bottomElement.length; j++) {
if (bottomElement[j].x == diamonds[i].x) {
if (Math.round(bottomElement[j].y) == Math.round(diamonds[i].y + 1)) {
appearPosition.position.x = Math.round(appearPosition.position.x);
appearPosition.position.y = Math.round(appearPosition.position.y);
createElement();
breakTag = 1;
}
}
}
//判断arrayOne是否在arrayTwo的右边
function IsAtRight(arrayOne, arrayTwo) {
for (i = 0; i < arrayOne.length; i++) {
for (j = 0; j < arrayTwo.length; j++) {
if (Math.round(arrayOne[i].y) == Math.round(arrayTwo[j].y)) {
if (arrayTwo[j].x == arrayOne[i].x + 1) return true;
}
}
}
return false;
}
//判D断arrayOne是否在arrayTwo的左边
function IsAtLeft(arrayOne, arrayTwo) {
for (i = 0; i < arrayOne.length; i++) {
for (j = 0; j < arrayTwo.length; j++) {
if (Math.round(arrayOne[i].y) == Math.round(arrayTwo[j].y)) {
if (arrayTwo[j].x == arrayOne[i].x - 1) return true;
}
}
}
return false;
}
for (j = 0; j < bottomElement.length; j++) {
if (bottomElement[j].x == diamonds[i].x) {
if (Math.round(bottomElement[j].y) == Math.round(diamonds[i].y + 1)) {
appearPosition.position.x = Math.round(appearPosition.position.x);
appearPosition.position.y = Math.round(appearPosition.position.y);
createElement();
breakTag = 1;
}
}
}
//判断arrayOne是否在arrayTwo的右边
function IsAtRight(arrayOne, arrayTwo) {
for (i = 0; i < arrayOne.length; i++) {
for (j = 0; j < arrayTwo.length; j++) {
if (Math.round(arrayOne[i].y) == Math.round(arrayTwo[j].y)) {
if (arrayTwo[j].x == arrayOne[i].x + 1) return true;
}
}
}
return false;
}
//判D断arrayOne是否在arrayTwo的左边
function IsAtLeft(arrayOne, arrayTwo) {
for (i = 0; i < arrayOne.length; i++) {
for (j = 0; j < arrayTwo.length; j++) {
if (Math.round(arrayOne[i].y) == Math.round(arrayTwo[j].y)) {
if (arrayTwo[j].x == arrayOne[i].x - 1) return true;
}
}
}
return false;
}
4.方块变形
var direction = 0;
if (e.keyCode == 87) {
direction++;
direction %= 4;
}
if (e.keyCode == 87) {
direction++;
direction %= 4;
}
W键是变形,0123分别代表四种。
如果是长条或者只有两种状态的直接 if (direction % 2 == 0) {},如果是正方块直接忽略direction,因为它就一种形状。
5.键盘捕获(目前WSAD+空格,W是变形,S和空格都是加速,IE9和FF异常,建议在谷歌浏览器下运行)
document.onkeydown = function (e) {
if (e.keyCode == 65) {
for (i = 0; i < diamonds.length; i++) {
if (diamonds[i].x == 0) {
return;
}
}
if (IsAtLeft(diamonds, bottomElement)) {
return;
}
appearPosition.position.x -= 1;
}
if (e.keyCode == 87) {
direction++;
direction %= 4;
}
if (e.keyCode == 68) {
for (i = 0; i < diamonds.length; i++) {
if (diamonds[i].x == 11) {
return;
}
}
if (IsAtRight(diamonds, bottomElement)) {
return;
}
appearPosition.position.x += 1;
}
if (e.keyCode == 32) {
delay = 1;
}
if (e.keyCode == 83) {
delay = 1;
}
}
document.onkeyup = function (e) {
if (e.keyCode == 32) {
delay = 20;
}
if (e.keyCode == 83) {
delay = 20;
}
}
if (e.keyCode == 65) {
for (i = 0; i < diamonds.length; i++) {
if (diamonds[i].x == 0) {
return;
}
}
if (IsAtLeft(diamonds, bottomElement)) {
return;
}
appearPosition.position.x -= 1;
}
if (e.keyCode == 87) {
direction++;
direction %= 4;
}
if (e.keyCode == 68) {
for (i = 0; i < diamonds.length; i++) {
if (diamonds[i].x == 11) {
return;
}
}
if (IsAtRight(diamonds, bottomElement)) {
return;
}
appearPosition.position.x += 1;
}
if (e.keyCode == 32) {
delay = 1;
}
if (e.keyCode == 83) {
delay = 1;
}
}
document.onkeyup = function (e) {
if (e.keyCode == 32) {
delay = 20;
}
if (e.keyCode == 83) {
delay = 20;
}
}
6.消除加分
//一行满了的话,消除并加分
function clearUp() {
for (var line = 0; line < 21; line++) {
var count = 0;
for (var i = 0; i < bottomElement.length; i++) {
if (bottomElement[i].y == line) {
count++;
}
}
if (count == 12) clearByLineNum(line);
}
// if(count==12)
}
function clearByLineNum(num) {
//以上的元素下降一行
score++;
var count = 0;
for (i = 0; i < bottomElement.length; i++) {
if (bottomElement[i].y == num) {
count++;
}
}
for (var j = 0; j < count; j++) {
for (var i = 0; i < bottomElement.length; i++) {
if (bottomElement[i].y == num) {
bottomElement.splice(i, 1);
break;
}
}
}
for (i = 0; i < bottomElement.length; i++) {
if (bottomElement[i].y < num) {
bottomElement[i].y += 1;
}
}
}
function clearUp() {
for (var line = 0; line < 21; line++) {
var count = 0;
for (var i = 0; i < bottomElement.length; i++) {
if (bottomElement[i].y == line) {
count++;
}
}
if (count == 12) clearByLineNum(line);
}
// if(count==12)
}
function clearByLineNum(num) {
//以上的元素下降一行
score++;
var count = 0;
for (i = 0; i < bottomElement.length; i++) {
if (bottomElement[i].y == num) {
count++;
}
}
for (var j = 0; j < count; j++) {
for (var i = 0; i < bottomElement.length; i++) {
if (bottomElement[i].y == num) {
bottomElement.splice(i, 1);
break;
}
}
}
for (i = 0; i < bottomElement.length; i++) {
if (bottomElement[i].y < num) {
bottomElement[i].y += 1;
}
}
}
消除加分有一个潜在的逻辑就是,在该行以上的元素的位置下降一个格子。
7.控制核心Jscex Show Time
var JropAsync = eval(Jscex.compile("async", function () {
var breakTag = 0;
while (true) {
color = randomColor();
rectBlockIndex = MR() * 7 | 0;
direction = MR() * 3 | 0;
$await(Jscex.Async.sleep(1));
while (true) {
for (i = 0; i < diamonds.length; i++) {
if (diamonds[i].y * height + height >= 525) {
appearPosition.position.x = Math.round(appearPosition.position.x);
appearPosition.position.y = Math.round(appearPosition.position.y);
createElement();
breakTag = 1;
}
//判D断?下?面?是?否?有D元a素?
for (j = 0; j < bottomElement.length; j++) {
if (bottomElement[j].x == diamonds[i].x) {
if (Math.round(bottomElement[j].y) == Math.round(diamonds[i].y + 1)) {
appearPosition.position.x = Math.round(appearPosition.position.x);
appearPosition.position.y = Math.round(appearPosition.position.y);
createElement();
breakTag = 1;
}
}
}
}
if (breakTag == 1) {
for (i = 0; i < diamonds.length; i++) {
//alert(diamonds[i].x + "____" + diamonds[i].y)
bottomElement.push(diamonds[i]);
}
clearUp();
//清?空?下?降μ的?元a素?
diamonds.splice(0, diamonds.length);
appearPosition = { position: { x: 4, y: -2 }, direction: 0 };
breakTag = 0;
break;
}
appearPosition.position.y += step;
draw();
$await(Jscex.Async.sleep(delay));
}
}
}));
var breakTag = 0;
while (true) {
color = randomColor();
rectBlockIndex = MR() * 7 | 0;
direction = MR() * 3 | 0;
$await(Jscex.Async.sleep(1));
while (true) {
for (i = 0; i < diamonds.length; i++) {
if (diamonds[i].y * height + height >= 525) {
appearPosition.position.x = Math.round(appearPosition.position.x);
appearPosition.position.y = Math.round(appearPosition.position.y);
createElement();
breakTag = 1;
}
//判D断?下?面?是?否?有D元a素?
for (j = 0; j < bottomElement.length; j++) {
if (bottomElement[j].x == diamonds[i].x) {
if (Math.round(bottomElement[j].y) == Math.round(diamonds[i].y + 1)) {
appearPosition.position.x = Math.round(appearPosition.position.x);
appearPosition.position.y = Math.round(appearPosition.position.y);
createElement();
breakTag = 1;
}
}
}
}
if (breakTag == 1) {
for (i = 0; i < diamonds.length; i++) {
//alert(diamonds[i].x + "____" + diamonds[i].y)
bottomElement.push(diamonds[i]);
}
clearUp();
//清?空?下?降μ的?元a素?
diamonds.splice(0, diamonds.length);
appearPosition = { position: { x: 4, y: -2 }, direction: 0 };
breakTag = 0;
break;
}
appearPosition.position.y += step;
draw();
$await(Jscex.Async.sleep(delay));
}
}
}));
这是也整个俄罗斯方块的控制核心,由两个while循环构成,简单大方。