JS как в тетрисе реализовать падение фигуры при нажатии клавиши
Написал тетрис на JS. Реализовал что только можно. Но я захотел добавить функцию, которая по нажатию клавиши пробел, роняла фигуру на дно стакана. И тут все оказалось не так классно как хотелось бы. Функция роняет фигуру на дно стакана,
HTML
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tetris</title>
<link rel="stylesheet" href="style/style.css" />
</head>
<body>
<div id="game">
<button id="start">Start</button>
<button id="pause">Pause</button>
<div>Level: <span id="level">0</span></div>
<div>Score: <span id="score">0</span></div>
<div class="main">
<!-- <ul id="body_slides">
<li></li>
<li></li>
<li></li>
</ul> -->
</div>
<div id="game-over">Game <br>over</br></div>
<div id="next-tetro"></div>
<div id="nexted-tetro"></div>
<audio class="wave" src="audio/korobeyniki.mp3" controls autoplay loop></audio>
</div>
<script src="script/script.js"></script>
</body>
</html>
CSS
* {
box-sizing: border-box;
}
body {
margin: 0;
background: #000;
color: #fff;
font-family: Arial, Helvetica, sans-serif;
}
#game {
padding-top: 20px;
font-size: 40px;
width: 320px;
margin: auto;
}
/* .wave{
display: none;
} */
.main {
width: 320px;
height: 620px;
margin: 20px auto 0;
border: 10px solid white;
font-size: 0;
background: url(../image/costel.png);
}
.cell {
width: 30px;
height: 30px;
border: 1px solid white;
display: inline-block;
}
.movingCell {
background-color: blue;
}
.fixedCell {
background-color: red;
}
#next-tetro {
position: absolute;
top: 200px;
left: 200px;
font-size: 0;
}
#nexted-tetro {
position: absolute;
top: 500px;
left: 200px;
font-size: 0;
}
#game-over {
font-size: 100px;
color: red;
text-align: center;
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
JS
let main = document.querySelector(".main");
const scroeElem = document.getElementById("score");
const levelElem = document.getElementById("level");
const nextTetroElem = document.getElementById("next-tetro");
const nextedTetroElem = document.getElementById("nexted-tetro");
const startBtn = document.getElementById("start");
const pauseBtn = document.getElementById("pause");
const gameOver = document.getElementById("game-over");
let playfield = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];
let score = 0;
let gameTimerID;
let currentLevel = 1;
let isPaused = true;
let possibleLevels = {
1: {
scorePerLine: 10,
speed: 400,
nextLevelScore: 50,
},
2: {
scorePerLine: 15,
speed: 300,
nextLevelScore: 100,
},
3: {
scorePerLine: 20,
speed: 200,
nextLevelScore: 1000,
},
4: {
scorePerLine: 30,
speed: 100,
nextLevelScore: 2000,
},
5: {
scorePerLine: 50,
speed: 50,
nextLevelScore: Infinity,
},
};
let figures = {
O: [
[1, 1],
[1, 1],
],
I: [
[0, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 0, 0],
[0, 0, 0, 0],
],
S: [
[0, 1, 1],
[1, 1, 0],
[0, 0, 0],
],
Z: [
[1, 1, 0],
[0, 1, 1],
[0, 0, 0],
],
L: [
[1, 0, 0],
[1, 1, 1],
[0, 0, 0],
],
J: [
[0, 0, 1],
[1, 1, 1],
[0, 0, 0],
],
T: [
[1, 1, 1],
[0, 1, 0],
[0, 0, 0],
],
D: [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
],
R: [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1],
],
P: [
[1, 0],
[1, 1],
],
K: [
[0, 0, 0],
[1, 1, 1],
[0, 0, 0],
],
};
let activeTetro = getNewTetro();
let nextTetro = getNewTetro();
let nextedTetro = getNewTetro();
function draw() {
let mainInnerHTML = "";
for (let y = 0; y < playfield.length; y++) {
for (let x = 0; x < playfield[y].length; x++) {
if (playfield[y][x] === 1) {
mainInnerHTML += '<div class="cell movingCell"></div>';
} else if (playfield[y][x] === 2) {
mainInnerHTML += '<div class="cell fixedCell"></div>';
} else {
mainInnerHTML += '<div class="cell"></div>';
}
}
}
main.innerHTML = mainInnerHTML;
}
function drawNextTetro() {
let nextTetroInnerHTML = "";
for (let y = 0; y < nextTetro.shape.length; y++) {
for (let x = 0; x < nextTetro.shape[y].length; x++) {
if (nextTetro.shape[y][x]) {
nextTetroInnerHTML += '<div class="cell movingCell"></div>';
} else {
nextTetroInnerHTML += '<div class="cell"></div>';
}
}
nextTetroInnerHTML += "<br/>";
}
nextTetroElem.innerHTML = nextTetroInnerHTML;
}
function drawNextedTetro(){
let nextedTetroInnerHTML = "";
for (let y = 0; y < nextedTetro.shape.length; y++) {
for (let x = 0; x < nextedTetro.shape[y].length; x++) {
if (nextedTetro.shape[y][x]) {
nextedTetroInnerHTML += '<div class="cell movingCell"></div>';
} else {
nextedTetroInnerHTML += '<div class="cell"></div>';
}
}
nextedTetroInnerHTML += "<br/>";
}
nextedTetroElem.innerHTML = nextedTetroInnerHTML;
}
function removePrevActiveTetro() {
for (let y = 0; y < playfield.length; y++) {
for (let x = 0; x < playfield[y].length; x++) {
if (playfield[y][x] === 1) {
playfield[y][x] = 0;
}
}
}
}
function addActiveTetro() {
removePrevActiveTetro();
for (let y = 0; y < activeTetro.shape.length; y++) {
for (let x = 0; x < activeTetro.shape[y].length; x++) {
if (activeTetro.shape[y][x] === 1) {
playfield[activeTetro.y + y][activeTetro.x + x] =
activeTetro.shape[y][x];
}
}
}
}
function rotateTetro() {
const prevTetroState = activeTetro.shape;
activeTetro.shape = activeTetro.shape[0].map((val, index) =>
activeTetro.shape.map((row) => row[index]).reverse()
);
if (hasCollisions()) {
activeTetro.shape = prevTetroState;
}
}
function hasCollisions() {
for (let y = 0; y < activeTetro.shape.length; y++) {
for (let x = 0; x < activeTetro.shape[y].length; x++) {
if (
activeTetro.shape[y][x] &&
(playfield[activeTetro.y + y] === undefined ||
playfield[activeTetro.y + y][activeTetro.x + x] === undefined ||
playfield[activeTetro.y + y][activeTetro.x + x] === 2)
) {
return true;
}
}
}
return false;
}
function removeFullLines() {
let canRemoveLine = true,
filledLines = 0;
for (let y = 0; y < playfield.length; y++) {
for (let x = 0; x < playfield[y].length; x++) {
if (playfield[y][x] !== 2) {
canRemoveLine = false;
break;
}
}
if (canRemoveLine) {
playfield.splice(y, 1);
playfield.splice(0, 0, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
filledLines += 1;
}
canRemoveLine = true;
}
switch (filledLines) {
case 1:
score += possibleLevels[currentLevel].scorePerLine;
break;
case 2:
score += possibleLevels[currentLevel].scorePerLine * 3;
break;
case 3:
score += possibleLevels[currentLevel].scorePerLine * 6;
break;
case 4:
score += possibleLevels[currentLevel].scorePerLine * 12;
break;
}
scroeElem.innerHTML = score;
if (score >= possibleLevels[currentLevel].nextLevelScore) {
currentLevel++;
levelElem.innerHTML = currentLevel;
}
}
function getNewTetro() {
const possibleFigures = "IOLJTSZDRPK";
const rand = Math.floor(Math.random() * 11);
const newTetro = figures[possibleFigures[rand]];
return {
x: Math.floor((10 - newTetro[0].length) / 2),
y: 0,
shape: newTetro,
};
}
function fixTetro() {
for (let y = 0; y < playfield.length; y++) {
for (let x = 0; x < playfield[y].length; x++) {
if (playfield[y][x] === 1) {
playfield[y][x] = 2;
}
}
}
}
function moveTetroDown() {
activeTetro.y += 1;
if (hasCollisions()) {
activeTetro.y -= 1;
fixTetro();
removeFullLines();
activeTetro = nextTetro;
if (hasCollisions()) {
reset();
}
nextTetro = nextedTetro;
nextedTetro = getNewTetro()
}
}
function dropTetro() {
for (let y = activeTetro.y; y < playfield.length; y++) {
activeTetro.y += 1;
if (hasCollisions()) {
activeTetro.y -= 1;
break;
}
}
}
function reset() {
isPaused = true;
clearTimeout(gameTimerID);
playfield = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];
draw();
gameOver.style.display = "block";
}
document.onkeydown = function (e) {
if (!isPaused) {
if (e.keyCode === 37) {
activeTetro.x -= 1;
if (hasCollisions()) {
activeTetro.x += 1;
}
}
else if (e.keyCode === 39) {
activeTetro.x += 1;
if (hasCollisions()) {
activeTetro.x -= 1;
}
}
else if (e.keyCode === 40) {
moveTetroDown();
}
else if (e.keyCode === 38) {
rotateTetro();
}
else if (e.keyCode === 32) {
dropTetro();
}
updateGameState();
}
};
function updateGameState() {
if (!isPaused) {
addActiveTetro();
draw();
drawNextTetro();
drawNextedTetro();
}
}
pauseBtn.addEventListener("click", (e) => {
if (e.target.innerHTML === "Pause") {
e.target.innerHTML = "Keep Playing...";
clearTimeout(gameTimerID);
} else {
e.target.innerHTML = "Pause";
gameTimerID = setTimeout(startGame, possibleLevels[currentLevel].speed);
}
isPaused = !isPaused;
});
startBtn.addEventListener("click", (e) => {
e.target.innerHTML = "Start again";
isPaused = false;
gameTimerID = setTimeout(startGame, possibleLevels[currentLevel].speed);
gameOver.style.display = "none";
});
scroeElem.innerHTML = score;
levelElem.innerHTML = currentLevel;
draw();
function startGame() {
moveTetroDown();
if (!isPaused) {
updateGameState();
gameTimerID = setTimeout(startGame, possibleLevels[currentLevel].speed);
}
}
Ответы (1 шт):
Автор решения: Igor
→ Ссылка
Заведите функцию:
function nextTimeout() {
clearTimeout(gameTimerID);
gameTimerID = setTimeout(startGame, possibleLevels[currentLevel].speed);
}
и всегда вызывайте ее вместо строчки
gameTimerID = setTimeout(startGame, possibleLevels[currentLevel].speed);