<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tetris Game</title>
<style>
canvas {
border: 1px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="200" height="400"></canvas>
<script>
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
const blockSize = 20;
const numRows = 20;
const numCols = 10;
const screen = new Array(numRows).fill(0).map(() => new Array(numCols).fill(0));
let gameStarted = false; // New variable to track game start
let paused = false; // Variable to track whether the game is paused
const tetrominoes = [
{ shape: [[1, 1, 1, 1]], color: "red" },
{ shape: [[1, 1, 1], [1]], color: "orange" },
{ shape: [[1, 1, 1], [0, 0, 1]], color: "yellow" },
{ shape: [[1, 1, 1], [0, 1]], color: "green" },
{ shape: [[1, 1], [1, 1]], color: "blue" },
{ shape: [[1, 1, 0], [0, 1, 1]], color: "indigo" },
{ shape: [[0, 1, 1], [1, 1]], color: "purple" },
];
let tetromino = getRandomTetromino();
function drawBlock(x, y, color) {
context.fillStyle = color;
context.fillRect(x * blockSize, y * blockSize, blockSize, blockSize);
context.strokeStyle = "black";
context.strokeRect(x * blockSize, y * blockSize, blockSize, blockSize);
}
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
// Draw filled blocks on the screen
for (let y = 0; y < numRows; y++) {
for (let x = 0; x < numCols; x++) {
if (screen[y][x] !== 0) {
drawBlock(x, y, "gray");
}
}
}
if (!gameStarted) {
// Draw initial screen message
context.fillStyle = "black";
context.font = "14px Arial"; // Adjusted font size
const message = paused ? "Game Paused - Press Space to Resume" : "Press Enter to start the game";
const textWidth = context.measureText(message).width;
context.fillText(message, (canvas.width - textWidth) / 2, canvas.height / 2);
} else {
// Draw Tetromino
tetromino.shape.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0) {
drawBlock(tetromino.x + x, tetromino.y + y, tetromino.color);
}
});
});
}
// Draw pause/resume message
if (paused && gameStarted) {
context.fillStyle = "black";
context.font = "10px Arial"; // Adjusted font size
const pauseMessage = "Game Paused - Press Space to Resume";
const pauseTextWidth = context.measureText(pauseMessage).width;
context.fillText(pauseMessage, (canvas.width - pauseTextWidth) / 2, canvas.height - 10);
}
}
function moveDown() {
tetromino.y++;
if (checkCollision()) {
tetromino.y--;
placeTetromino();
clearLines();
if (isGameOver()) {
endGame();
} else {
spawnTetromino();
}
}
}
function moveLeft() {
tetromino.x--;
if (checkCollision()) {
tetromino.x++;
}
}
function moveRight() {
tetromino.x++;
if (checkCollision()) {
tetromino.x--;
}
}
function rotate() {
const originalShape = tetromino.shape;
tetromino.shape = rotateMatrix(tetromino.shape);
if (checkCollision()) {
tetromino.shape = originalShape;
}
}
function getRandomTetromino() {
const randomIndex = Math.floor(Math.random() * tetrominoes.length);
return {
shape: tetrominoes[randomIndex].shape,
color: tetrominoes[randomIndex].color,
x: Math.floor((numCols - tetrominoes[randomIndex].shape[0].length) / 2),
y: 0,
};
}
function rotateMatrix(matrix) {
const rows = matrix.length;
const cols = matrix[0].length;
const result = new Array(cols).fill(0).map(() => new Array(rows).fill(0));
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
result[j][rows - 1 - i] = matrix[i][j];
}
}
return result;
}
function checkCollision() {
for (let y = 0; y < tetromino.shape.length; y++) {
for (let x = 0; x < tetromino.shape[y].length; x++) {
if (
tetromino.shape[y][x] !== 0 &&
(screen[tetromino.y + y] && screen[tetromino.y + y][tetromino.x + x]) !== 0
) {
return true;
}
}
}
return false;
}
function placeTetromino() {
tetromino.shape.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0) {
screen[tetromino.y + y][tetromino.x + x] = 1;
}
});
});
}
function clearLines() {
for (let y = numRows - 1; y >= 0; y--) {
if (screen[y].every((value) => value !== 0)) {
// Clear the line
screen.splice(y, 1);
screen.unshift(new Array(numCols).fill(0));
}
}
}
function spawnTetromino() {
tetromino = getRandomTetromino();
}
function isGameOver() {
// Check if the top row has any filled blocks
return screen[0].some((value) => value !== 0);
}
function endGame() {
// Perform actions when the game is over
gameStarted = false;
paused = false;
alert("Game Over! Press Enter to restart.");
resetGame();
}
function resetGame() {
// Reset the game state
screen.forEach((row) => row.fill(0));
}
function togglePause() {
paused = !paused;
}
function update() {
if (gameStarted && !paused) {
moveDown();
}
draw();
}
// Handle keyboard input
document.addEventListener("keydown", (event) => {
if (!gameStarted && event.code === "Enter") {
// Start or resume the game on pressing Enter
gameStarted = true;
paused = false; // Ensure the game is not paused when starting
} else if (gameStarted) {
// Continue handling other keys during the game
switch (event.code) {
case "ArrowLeft":
moveLeft();
break;
case "ArrowRight":
moveRight();
break;
case "ArrowDown":
moveDown();
break;
case "ArrowUp":
rotate();
break;
case "Space":
togglePause(); // Pause or resume the game on pressing Space
break;
}
}
});
// Game loop
setInterval(update, 500);
</script>
</body>
</html>
html로 테트리스 개발해보면서 공부하려는데, 테트리스 블록 중에서 T자 블록이랑 L자 블록이 회전하면서 모양이 뭉게지는 현상이 발생함.
아마 rotate()함수나 rotateMatrix()함수에 문제가 있는 것 같은데 계속 수정해봤는데도 문제가 있음. 해결책 알려주면 감사하겠습니다...
모양 배열 빈칸을 0으로 채워서 직사각형으로 만들어라
const tetrominoes = [ { shape: [[1, 1, 1, 1]], color: "red" }, { shape: [[1, 1, 1], [1, 0, 0]], color: "orange" }, { shape: [[1, 1, 1], [0, 0, 1]], color: "yellow" }, { shape: [[1, 1, 1], [0, 1, 0]], color: "green" }, { shape: [[1, 1], [1, 1]], color: "blue" },
{ shape: [[1, 1, 0], [0, 1, 1]], color: "indigo" }, { shape: [[0, 1, 1], [1, 1, 0]], color: "purple" }, ];
이렇게 하니까 문제없이 회전 잘되는 듯.ㄳㄳ
drawBlock이 문제임 0이 아니면 그리고 있는데 원래 데이터 대로면 undefined가 결과에 포함됨. 그러면 그건 0과는 다른 무엇이기 때문에 그려짐
정확히는 drawBlock을 호출하는 조건문이 문제