Из-за этого после вращения часть фигуры может оказаться за пределами игрового поля или «залезть» на другую фигуру, уже стоящую на поле.
Кроме того, сам алгоритм вращения неординарный, и я не смог найти какой-то закономерности в изменениях координат при вращении. Можно было бы еще посидеть и подумать, но фигур не так много и вариантов позиций у каждой максимум 4.
Поэтому я решил сделать не очень красиво. Просто вручную описать все возможные положения фигур и порядок их изменения.
Внимание! Этот код может повлиять на вашу психику.
// ... setRotatedCoords: function() { var newCoords = []; switch(this.type) { // Проверяем тип фигуры case 'I': switch(this.rotatePosition) { // и, в зависимости от текущего положения фигуры, изменяем координаты case 0: newCoords.push([ [this.coords[2][0][0], this.coords[2][0][1]-1], [this.coords[2][0][0], this.coords[2][0][1]], [this.coords[2][0][0], this.coords[2][0][1]+1], [this.coords[2][0][0], this.coords[2][0][1]+2] ]); this.coords = newCoords; this.rotatePosition = 1; break; case 1: newCoords.push([ [this.coords[0][1][0]-2, this.coords[0][1][1]] ],[ [this.coords[0][1][0]-1, this.coords[0][1][1]] ],[ [this.coords[0][1][0], this.coords[0][1][1]] ],[ [this.coords[0][1][0]+1, this.coords[0][1][1]] ]); this.coords = newCoords; this.rotatePosition = 0; break; } break; case 'S': switch(this.rotatePosition) { case 0: newCoords.push([ [this.coords[0][0][0], this.coords[0][0][1]-1] ],[ [this.coords[1][0][0], this.coords[1][0][1]], [this.coords[1][1][0], this.coords[1][1][1]] ],[ [this.coords[1][1][0]+1, this.coords[1][1][1]] ]); this.coords = newCoords; this.rotatePosition = 1; break; case 1: newCoords.push([ [this.coords[0][0][0], this.coords[0][0][1]+1], [this.coords[0][0][0], this.coords[0][0][1]+2] ],[ [this.coords[1][0][0], this.coords[1][0][1]], [this.coords[1][1][0], this.coords[1][1][1]] ]); this.coords = newCoords; this.rotatePosition = 0; break; } break; case 'Z': switch(this.rotatePosition) { case 0: newCoords.push([ [this.coords[0][1][0], this.coords[0][1][1]+1] ],[ [this.coords[1][0][0], this.coords[1][0][1]], [this.coords[1][1][0], this.coords[1][1][1]] ],[ [this.coords[1][0][0]+1, this.coords[1][0][1]] ]); this.coords = newCoords; this.rotatePosition = 1; break; case 1: newCoords.push([ [this.coords[0][0][0], this.coords[0][0][1]-2], [this.coords[0][0][0], this.coords[0][0][1]-1] ],[ [this.coords[1][0][0], this.coords[1][0][1]], [this.coords[1][1][0], this.coords[1][1][1]] ]); this.coords = newCoords; this.rotatePosition = 0; break; } break; case 'T': switch(this.rotatePosition) { case 0: newCoords.push([ [this.coords[0][1][0]-1, this.coords[0][1][1]] ],[ [this.coords[0][0][0], this.coords[0][0][1]], [this.coords[0][1][0], this.coords[0][1][1]] ],[ [this.coords[1][0][0], this.coords[1][0][1]] ]); this.coords = newCoords; this.rotatePosition = 1; break; case 1: newCoords.push([ [this.coords[0][0][0], this.coords[0][0][1]] ],[ [this.coords[1][0][0], this.coords[1][0][1]], [this.coords[1][1][0], this.coords[1][1][1]], [this.coords[1][1][0], this.coords[1][1][1]+1] ]); this.coords = newCoords; this.rotatePosition = 2; break; case 2: newCoords.push([ [this.coords[0][0][0], this.coords[0][0][1]] ],[ [this.coords[1][1][0], this.coords[1][1][1]], [this.coords[1][2][0], this.coords[1][2][1]] ],[ [this.coords[1][1][0]+1, this.coords[1][1][1]] ]); this.coords = newCoords; this.rotatePosition = 3; break; case 3: newCoords.push([ [this.coords[1][0][0], this.coords[0][0][1]-1], [this.coords[1][0][0], this.coords[1][0][1]], [this.coords[1][1][0], this.coords[1][1][1]] ],[ [this.coords[2][0][0], this.coords[2][0][1]] ]); this.coords = newCoords; this.rotatePosition = 0; break; } break; case 'J': switch(this.rotatePosition) { case 0: newCoords.push([ [this.coords[0][0][0], this.coords[0][0][1]-1] ],[ [this.coords[1][0][0], this.coords[1][0][1]-1], [this.coords[1][0][0], this.coords[1][0][1]], [this.coords[1][0][0], this.coords[1][0][1]+1] ]); this.coords = newCoords; this.rotatePosition = 1; break; case 1: newCoords.push([ [this.coords[0][0][0], this.coords[0][0][1]], [this.coords[0][0][0], this.coords[0][0][1]+1] ],[ [this.coords[1][0][0], this.coords[1][0][1]] ],[ [this.coords[1][0][0]+1, this.coords[1][0][1]] ]); this.coords = newCoords; this.rotatePosition = 2; break; case 2: newCoords.push([ [this.coords[1][0][0], this.coords[1][0][1]-1], [this.coords[1][0][0], this.coords[1][0][1]], [this.coords[1][0][0], this.coords[1][0][1]+1] ],[ [this.coords[1][0][0]+1, this.coords[1][0][1]+1] ]); this.coords = newCoords; this.rotatePosition = 3; break; case 3: newCoords.push([ [this.coords[0][1][0]-1, this.coords[0][1][1]+1] ],[ [this.coords[0][1][0], this.coords[0][1][1]+1] ],[ [this.coords[1][0][0], this.coords[1][0][1]-1], [this.coords[1][0][0], this.coords[1][0][1]] ]); this.coords = newCoords; this.rotatePosition = 0; break; } break; case 'L': switch(this.rotatePosition) { case 0: newCoords.push([ [this.coords[0][0][0], this.coords[0][0][1]+1] ],[ [this.coords[1][0][0], this.coords[1][0][1]-1], [this.coords[1][0][0], this.coords[1][0][1]], [this.coords[1][0][0], this.coords[1][0][1]+1] ]); this.coords = newCoords; this.rotatePosition = 1; break; case 1: newCoords.push([ [this.coords[0][0][0], this.coords[0][0][1]-1], [this.coords[0][0][0], this.coords[0][0][1]] ],[ [this.coords[1][0][0], this.coords[1][0][1]+2] ],[ [this.coords[1][0][0]+1, this.coords[1][0][1]+2] ]); this.coords = newCoords; this.rotatePosition = 2; break; case 2: newCoords.push([ [this.coords[1][0][0], this.coords[1][0][1]+1], [this.coords[1][0][0], this.coords[1][0][1]], [this.coords[1][0][0], this.coords[1][0][1]-1] ],[ [this.coords[1][0][0]+1, this.coords[1][0][1]-1] ]); this.coords = newCoords; this.rotatePosition = 3; break; case 3: newCoords.push([ [this.coords[0][1][0]-1, this.coords[0][1][1]-1] ],[ [this.coords[0][1][0], this.coords[0][1][1]-1] ],[ [this.coords[1][0][0], this.coords[1][0][1]], [this.coords[1][0][0], this.coords[1][0][1]+1] ]); this.coords = newCoords; this.rotatePosition = 0; break; } break; } }, //...При создании фигуры будем не только выбирать тип фигуры, но и задавать случайным образом поворот:
// ... getRandomFigure: function() { var keys = Object.keys(Tetris.config.figureTypes); var randKey = Math.floor(Math.random() * keys.length); // Запомним тип фигуры, чтобы потом знать, по какому алгоритму поворачивать фигуру Tetris.figure.type = keys[randKey]; this.coords = Tetris.config.figureTypes[keys[randKey]](); // Выберем случайное количество поворотов var rotatePosition = Math.floor(Math.random() * 4); // и вращаем, вращаем, вращаем if (rotatePosition) { for (var i = 0; i < rotatePosition; i++) { this.setRotatedCoords(); } } console.log(rotatePosition); }, // ...Не забудем обнулять позицию поворота при уничтожении фигуры
// ... destroy: function() { this.coords = []; this.rotatePosition = 0; }, // ...Добавим вызов метода при нажатии кнопки «Вверх»
// ... window.onkeydown = function (event) { var direction = ''; if (event.keyCode == 39) { direction = 'right'; } else if(event.keyCode == 37) { direction = 'left'; } if (direction) { Tetris.figure.sideStepStart(direction); } else if(event.keyCode == 40) { Tetris.setSpeed(30); } if (event.keyCode == 38) { Tetris.figure.rotate(); } } // ...Теперь в методе rotate пропишем вызов вращения и различные проверки
// ... figure: { // ... create: function() { // В методе getRandomFigure уже прописана установка координат, поэтому просто вызываем this.getRandomFigure(); }, rotatePosition: 0, rotate: function() { // Если кнопку нажали до создания фигуры, ничего не делаем if (this.coords.length == 0) { return false; } // Заставляем фигуру совершить поворот this.setRotatedCoords(); // Проверяем - не вылезла ли фигура за пределы поля Tetris.each(this.coords, function(i,j){ var figureRow = Tetris.figure.coords[i][j][0]; var figureCol = Tetris.figure.coords[i][j][1]; // Если вылезла слева, будем двигать фигуру вправо if (figureCol < 0) { Tetris.figure.needStepSide = 'right'; } // Если справа - влево if (figureCol >= Tetris.pitch.width) { Tetris.figure.needStepSide = 'left'; } }); // Если мы определили, что надо бы подвинуть фигуру, делаем это if (this.needStepSide) { this.sideStep(this.needStepSide); // обнуляем флаг this.needStepSide = false; } // Проверяем, не налезла ли фигура на другие фигуры и не провалилась ли она под пол if (this.touched()) { // Если да, то откатываем поворот this.rotateRollback(); return false; } // Если всё ОК, рисуем новое положение фигуры Tetris.draw(); }, needStepSide: false, // Возможных позиций у фигуры 4, поэтому, чтобы вернуться к предыдущему // состоянию, нужно повернуться еще 3 раза rotateRollback: function() { this.setRotatedCoords(); this.setRotatedCoords(); this.setRotatedCoords(); }, // ... }, // ...
Теперь в тетрис можно уже и поиграть: http://jsfiddle.net/ilyautkin/aavju3jd/42/
Я теперь понимаю, почему гигабайты требуются там, где раньше хватало килобайт.
Ну, как нет закономерности во вращении?
Возьмем для простоты квадрат три на три (четыре нужно только для палок и добавляется элементарно)
_____Y
X___44__45__46 ________ O
____54__55__56 _____ O O О
____64__65__66
Назначаем приказным порядком один квадратик в фигурке центральным (вокруг него и будем вращать)
пусть это будет точка с координатами 55
теперь вычтем ее коорд. из коорд. остальных точек. Это не обычное вычитание, а вычитание поразрядно, что в общем тоже элементарно реализуется (причем это все только для наглядности)
Получим такую матрицу
-1 -1 ___ -1 0 ___ {-1 1}
0 -1 ___ 0 0 ___ (0 1)
1 -1 ___ 1 0 ___ 1 1
Для перемещения точки ( 0: 1) (Y: X ) при повороте на 90 градусов в точку ( -1: 0) (Ynext: Xnext)против часовой стрелки выводится простейшая формула
if (Y == 0) {Xnext = 0; Ynext = -X} else {Xnext = Y; Ynext = 0}
для точки {-1 1} будет такая же формула, но чуть с другими параметрами.
ВСЁ! Мы умеем врашать фигуры 3 *3.
Для палок используется третья формула, аналогичная предыдущим.
Итого три строчки кода. И одна строчка ДО, что бы отделить нужные точки для нужных формул (ну там все просто — переход по условию)
Зачем ты столько нагородил???
PS ecли мы для интереса построим графики изменения X и Y от поворота, то получим пилу для точки ( 0 1) и меандр для точки{-1 1}. По сути это разложение в ряд синусоиды вращения. А графики — гармоники:)
Ну как неординарный алгоритм?!
Очень даже ординарный.
Как раз вчера дописал функционирующую программку тетриса, рабочий блок кода на джаве для вращения. Сильно за
стиль не ругайте, я не погромист.
int Y_temp = Y_current-Y_central;
int X_temp = X_current-X_central;
int sWitch = Math.abs(Y_temp)+Math.abs(X_temp);
if ((Math.abs(Y_temp)|Math.abs(X_temp))==2){sWitch=3;}
switch (sWitch)
{
case 1:
if (Y_temp == 0) {Xnext = 0; Ynext = -X_temp;} else {Xnext = Y_temp; Ynext = 0;}
break;
case 2:
if (Y_temp == -1 & X_temp == 1) {Ynext = -1; Xnext = -1;}
if (Y_temp == -1 & X_temp == -1) {Ynext = 1; Xnext = -1;}
if (Y_temp == 1 & X_temp == -1) {Ynext = 1; Xnext = 1;}
if (Y_temp == 1 & X_temp == 1) {Ynext = -1; Xnext = 1;}
break;
case 3:
if (Y_temp == 0) {Xnext = 0; Ynext = -X_temp;} else {Xnext = Y_temp; Ynext = 0;}
break;
}
Вращает все фигуры в том числе и палки. Ну и это все завернуто в цикл FOR для трех обсчитываемых точек (четвертая центральная — неподвижна). Для точки {-1;1} (case 2:) не нашел более короткого алгоритма, зато для крайней точки палки ОООО (case 3:) он тоже в одну строчку.Если у кого получится короче, выкладывайте.
int Y_temp = Y_current-Y_central;
int X_temp = X_current-X_central;
int sWitch = Math.abs(Y_temp)+Math.abs(X_temp);
if ((Math.abs(Y_temp)|Math.abs(X_temp))==2){sWitch=1;}
switch (sWitch)
{
case 1:
if (Y_temp == 0) {Xnext = 0; Ynext = -X_temp;} else {Xnext = Y_temp; Ynext = 0;}
break;
case 2:
if (Y_temp == -1 & X_temp == 1) {Ynext = -1; Xnext = -1;}
if (Y_temp == -1 & X_temp == -1) {Ynext = 1; Xnext = -1;}
if (Y_temp == 1 & X_temp == -1) {Ynext = 1; Xnext = 1;}
if (Y_temp == 1 & X_temp == 1) {Ynext = -1; Xnext = 1;}
}
Ynext= Ynext + Y_central;
Xnext= Xnext + X_central;
Илья, подскажите откуда newCoords.push, появляется 2, и как образуются,
эти данные в массиве.Может есть литература на эту тему?