Play Tetris Online: Classic Brick-Stacking Game
<html>
<head>
<title>Tetris</title>
<style>
body {
background-color: #555;
}
<pre><code>#tetris {
position: relative;
margin: 0 auto;
border: 1px solid #999;
background: #000;
width: 200px;
height: 400px;
}
.tetromino {
background-color: #0f0;
width: 20px;
height: 20px;
position: absolute;
}
</code></pre>
</style>
</head>
<body>
<h1>Tetris</h1>
<div id='tetris'></div>
<script>
// Generate pieces
const PIECES = [
[Z, 'red'],
[S, 'green'],
[T, 'yellow'],
[O, 'blue'],
[L, 'purple'],
[I, 'cyan'],
[J, 'orange']
];
<pre><code>// Create pieces
let board = [];
let score = 0;
let gameOver = false;
// Create divs
let gameDiv = document.getElementById('tetris');
let nextDiv = document.createElement('div');
let scoreDiv = document.createElement('div');
scoreDiv.setAttribute('id', 'score');
gameDiv.appendChild(nextDiv);
gameDiv.appendChild(scoreDiv);
// Create pieces
let pieces = [];
let currentPiece = randomPiece();
let nextPiece = randomPiece();
// The main game loop
let dropCounter = 0;
let dropInterval = 1000;
let lastTime = 0;
function update(time = 0) {
const deltaTime = time - lastTime;
lastTime = time;
dropCounter += deltaTime;
if (dropCounter > dropInterval) {
movePieceDown();
}
draw();
drawNext();
scoreDiv.innerHTML = 'Score: ' + score;
if (!gameOver) {
requestAnimationFrame(update);
}
}
update();
// Move pieces
document.addEventListener('keydown', movePiece);
function movePiece(event) {
switch (event.keyCode) {
case 37:
movePieceLeft();
break;
case 38:
rotatePiece();
break;
case 39:
movePieceRight();
break;
case 40:
movePieceDown();
break;
}
}
// Create functions
function randomPiece() {
let r = randomN = Math.floor(Math.random() * PIECES.length) // 0 -> 6
return new Piece(PIECES[r][0], PIECES[r][1]);
}
function draw() {
gameDiv.innerHTML = '';
for (let r = 0; r < board.length; r++) {
for (let c = 0; c < board[r].length; c++) {
if (board[r][c] !== 0) {
let div = document.createElement('div');
div.setAttribute('class', 'tetromino');
div.style.top = r * 20 + 'px';
div.style.left = c * 20 + 'px';
div.style.backgroundColor = board[r][c];
gameDiv.appendChild(div);
}
}
}
}
function drawNext() {
nextDiv.innerHTML = '';
for (let r = 0; r < nextPiece.matrix.length; r++) {
for (let c = 0; c < nextPiece.matrix[r].length; c++) {
if (nextPiece.matrix[r][c] !== 0) {
let div = document.createElement('div');
div.setAttribute('class', 'tetromino');
div.style.top = r * 20 + 'px';
div.style.left = c * 20 + 'px';
div.style.backgroundColor = nextPiece.color;
nextDiv.appendChild(div);
}
}
}
}
// Create pieces
function Piece(tetromino, color) {
this.tetromino = tetromino;
this.color = color;
this.tetrominoN = 0;
this.activeTetromino = this.tetromino[this.tetrominoN];
this.x = 3;
this.y = -2;
}
// Move pieces
Piece.prototype.fill = function (color) {
for (let r = 0; r < this.activeTetromino.length; r++) {
for (let c = 0; c < this.activeTetromino.length; c++) {
// We draw only occupied squares
if (this.activeTetromino[r][c]) {
board[this.y + r][this.x + c] = color;
}
}
}
}
Piece.prototype.moveDown = function() {
if (!this.collision(0, 1, this.activeTetromino)) {
this.y++;
} else {
// Lock the current piece and generate a new one
this.lock();
currentPiece = nextPiece;
nextPiece = randomPiece();
}
}
Piece.prototype.moveLeft = function() {
if (!this.collision(-1, 0, this.activeTetromino)) {
this.x--;
}
}
Piece.prototype.moveRight = function() {
if (!this.collision(1, 0, this.activeTetromino)) {
this.x++;
}
}
Piece.prototype.rotate = function() {
let nextPattern = this.tetromino[(this.tetrominoN + 1)%this.tetromino.length];
let kick = 0;
if (this.collision(0, 0, nextPattern)) {
if (this.x > COL/2) {
// It's the right wall
kick = -1; // We need to move the piece to the left
} else {
// It's the left wall
kick = 1; // We need to move the piece to the right
}
}
if (!this.collision(kick, 0, nextPattern)) {
this.x += kick;
this.tetrominoN = (this.tetrominoN + 1)%this.tetromino.length; // (0+1)%4 => 1
this.activeTetromino = this.tetromino[this.tetrominoN];
}
}
let ROW = 20;
let COL = 10;
let pieces = 'ILJOTSZ';
let board = [];
for (let r = 0; r < ROW; r++) {
board[r] = [];
for (let c = 0; c < COL; c++) {
board[r][c] = 0;
}
}
// Lock pieces
Piece.prototype.lock = function() {
for (let r = 0; r < this.activeTetromino.length; r++) {
for (let c = 0; c < this.activeTetromino.length; c++) {
// We skip the vacant squares
if (!this.activeTetromino[r][c]) {
continue;
}
// Pieces to lock on top = game over
if (this.y + r < 0) {
alert('Game Over!');
// Stop request animation frame
gameOver = true;
break;
}
// We lock the piece
board[this.y+r][this.x+c] = this.color;
}
}
// Remove full rows
for (let r = 0; r < ROW; r++) {
let isRowFull = true;
for (let c = 0; c < COL; c++) {
isRowFull = isRowFull && (board[r][c] != 0);
}
if (isRowFull) {
board.splice(r, 1);
board.unshift(new Array(COL).fill(0));
score += 100;
}
}
}
// Check collisions
Piece.prototype.collision = function(xOffset, yOffset, matrix) {
for (let r = 0; r < matrix.length; r++) {
for (let c = 0; c < matrix.length; c++) {
// We skip vacant squares
if (!matrix[r][c]) {
continue;
}
// Checks if the piece is inside the game board
if (this.x + c + xOffset < 0 || this.x + c + xOffset >= COL || this.y + r + yOffset >= ROW) {
return true;
}
// Checks if there is a collision with the game board
if (this.y + r + yOffset < 0) {
continue;
}
if (board[this.y + r + yOffset][this.x + c + xOffset] != 0) {
return true;
}
}
}
return false;
}
// Pieces matrix
const Z = [
[0, 0, 0],
[1, 1, 0],
[0, 1, 1],
[0, 0, 0]
];
const S = [
[0, 0, 0],
[0, 1, 1],
[1, 1, 0],
[0, 0, 0]
];
const T = [
[0, 0, 0],
[1, 1, 1],
[0, 1, 0],
[0, 0, 0]
];
const O = [
[0, 1, 1],
[0, 1, 1],
[0, 0, 0],
[0, 0, 0]
];
const L = [
[0, 1, 0],
[0, 1, 0],
[0, 1, 1],
[0, 0, 0]
];
const I = [
[0, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 0, 0],
[0, 0, 0, 0]
];
const J = [
[0, 1, 0],
[0, 1, 0],
[1, 1, 0],
[0, 0, 0]
];
</code></pre>
</script>
</body>
</htm
原文地址: https://www.cveoy.top/t/topic/lmeK 著作权归作者所有。请勿转载和采集!