Inscription #65935640#65,935,640pngInscription #1177284#1,177,284htmlInscription #1172540#1,172,540htmlInscription #1161912#1,161,912htmlInscription #1106174#1,106,174htmlInscription #1104013#1,104,013htmlInscription #1103727#1,103,727htmlInscription #1103699#1,103,699htmlInscription #1103608#1,103,608htmlInscription #1095276#1,095,276htmlInscription #1092144#1,092,144htmlInscription #1082249#1,082,249htmlInscription #1082127#1,082,127htmlInscription #1038641#1,038,641htmlInscription #1038635#1,038,635htmlInscription #1038626#1,038,626htmlInscription #1038623#1,038,623htmlInscription #1038620#1,038,620htmlInscription #1038609#1,038,609htmlInscription #1038601#1,038,601htmlInscription #1038599#1,038,599htmlInscription #1015841#1,015,841htmlInscription #1012919#1,012,919htmlInscription #1012918#1,012,918htmlInscription #1012484#1,012,484htmlInscription #1010894#1,010,894htmlInscription #986129#986,129htmlInscription #984782#984,782htmlInscription #813600#813,600htmlInscription #659331#659,331htmlInscription #658340#658,340html<!DOCTYPE html> <html> <head> <title>Tetris</title> <meta charset="UTF-8"> <style> html, body { height: 100%; margin: 0; } body { background: black; display: flex; align-items: center; justify-content: center; } canvas { border: 1px solid white; } </style> </head> <body> <canvas width="320" height="640" id="game"></canvas> <script> // https://tetris.fandom.com/wiki/Tetris_Guideline // get a random integer between the range of [min,max] // @see https://stackoverflow.com/a/1527820/2124254 function getRandomInt(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; } // generate a new tetromino sequence // @see https://tetris.fandom.com/wiki/Random_Generator function generateSequence() { const sequence = ['I', 'J', 'L', 'O', 'S', 'T', 'Z']; while (sequence.length) { const rand = getRandomInt(0, sequence.length - 1); const name = sequence.splice(rand, 1)[0]; tetrominoSequence.push(name); } } // get the next tetromino in the sequence function getNextTetromino() { if (tetrominoSequence.length === 0) { generateSequence(); } const name = tetrominoSequence.pop(); const matrix = tetrominos[name]; // I and O start centered, all others start in left-middle const col = playfield[0].length / 2 - Math.ceil(matrix[0].length / 2); // I starts on row 21 (-1), all others start on row 22 (-2) const row = name === 'I' ? -1 : -2; return { name: name, // name of the piece (L, O, etc.) matrix: matrix, // the current rotation matrix row: row, // current row (starts offscreen) col: col // current col }; } // rotate an NxN matrix 90deg // @see https://codereview.stackexchange.com/a/186834 function rotate(matrix) { const N = matrix.length - 1; const result = matrix.map((row, i) => row.map((val, j) => matrix[N - j][i]) ); return result; } // check to see if the new matrix/row/col is valid function isValidMove(matrix, cellRow, cellCol) { for (let row = 0; row < matrix.length; row++) { for (let col = 0; col < matrix[row].length; col++) { if (matrix[row][col] && ( // outside the game bounds cellCol + col < 0 || cellCol + col >= playfield[0].length || cellRow + row >= playfield.length || // collides with another piece playfield[cellRow + row][cellCol + col]) ) { return false; } } } return true; } // place the tetromino on the playfield function placeTetromino() { for (let row = 0; row < tetromino.matrix.length; row++) { for (let col = 0; col < tetromino.matrix[row].length; col++) { if (tetromino.matrix[row][col]) { // game over if piece has any part offscreen if (tetromino.row + row < 0) { return showGameOver(); } playfield[tetromino.row + row][tetromino.col + col] = tetromino.name; } } } // check for line clears starting from the bottom and working our way up for (let row = playfield.length - 1; row >= 0; ) { if (playfield[row].every(cell => !!cell)) { // drop every row above this one for (let r = row; r >= 0; r--) { for (let c = 0; c < playfield[r].length; c++) { playfield[r][c] = playfield[r-1][c]; } } } else { row--; } } tetromino = getNextTetromino(); } // show the game over screen function showGameOver() { cancelAnimationFrame(rAF); gameOver = true; context.fillStyle = 'black'; context.globalAlpha = 0.75; context.fillRect(0, canvas.height / 2 - 30, canvas.width, 60); context.globalAlpha = 1; context.fillStyle = 'white'; context.font = '36px monospace'; context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillText('GAME OVER!', canvas.width / 2, canvas.height / 2); } const canvas = document.getElementById('game'); const context = canvas.getContext('2d'); const grid = 32; const tetrominoSequence = []; // keep track of what is in every cell of the game using a 2d array // tetris playfield is 10x20, with a few rows offscreen const playfield = []; // populate the empty state for (let row = -2; row < 20; row++) { playfield[row] = []; for (let col = 0; col < 10; col++) { playfield[row][col] = 0; } } // how to draw each tetromino // @see https://tetris.fandom.com/wiki/SRS const tetrominos = { 'I': [ [0,0,0,0], [1,1,1,1], [0,0,0,0], [0,0,0,0] ], 'J': [ [1,0,0], [1,1,1], [0,0,0], ], 'L': [ [0,0,1], [1,1,1], [0,0,0], ], 'O': [ [1,1], [1,1], ], 'S': [ [0,1,1], [1,1,0], [0,0,0], ], 'Z': [ [1,1,0], [0,1,1], [0,0,0], ], 'T': [ [0,1,0], [1,1,1], [0,0,0], ] }; // color of each tetromino const colors = { 'I': 'cyan', 'O': 'yellow', 'T': 'purple', 'S': 'green', 'Z': 'red', 'J': 'blue', 'L': 'orange' }; let count = 0; let tetromino = getNextTetromino(); let rAF = null; // keep track of the animation frame so we can cancel it let gameOver = false; // game loop function loop() { rAF = requestAnimationFrame(loop); context.clearRect(0,0,canvas.width,canvas.height); // draw the playfield for (let row = 0; row < 20; row++) { for (let col = 0; col < 10; col++) { if (playfield[row][col]) { const name = playfield[row][col]; context.fillStyle = colors[name]; // drawing 1 px smaller than the grid creates a grid effect context.fillRect(col * grid, row * grid, grid-1, grid-1); } } } // draw the active tetromino if (tetromino) { // tetromino falls every 35 frames if (++count > 35) { tetromino.row++; count = 0; // place piece if it runs into anything if (!isValidMove(tetromino.matrix, tetromino.row, tetromino.col)) { tetromino.row--; placeTetromino(); } } context.fillStyle = colors[tetromino.name]; for (let row = 0; row < tetromino.matrix.length; row++) { for (let col = 0; col < tetromino.matrix[row].length; col++) { if (tetromino.matrix[row][col]) { // drawing 1 px smaller than the grid creates a grid effect context.fillRect((tetromino.col + col) * grid, (tetromino.row + row) * grid, grid-1, grid-1); } } } } } // listen to keyboard events to move the active tetromino document.addEventListener('keydown', function(e) { if (gameOver) return; // left and right arrow keys (move) if (e.which === 37 || e.which === 39) { const col = e.which === 37 ? tetromino.col - 1 : tetromino.col + 1; if (isValidMove(tetromino.matrix, tetromino.row, col)) { tetromino.col = col; } } // up arrow key (rotate) if (e.which === 38) { const matrix = rotate(tetromino.matrix); if (isValidMove(matrix, tetromino.row, tetromino.col)) { tetromino.matrix = matrix; } } // down arrow key (drop) if(e.which === 40) { const row = tetromino.row + 1; if (!isValidMove(tetromino.matrix, row, tetromino.col)) { tetromino.row = row - 1; placeTetromino(); return; } tetromino.row = row; } }); // start the game rAF = requestAnimationFrame(loop); </script> </body> </html>#657,136text<!DOCTYPE html> <html> <head> <title>Basic Pong HTML Game</title> <meta charset="UTF-8"> <style> html, body { height: 100%; margin: 0; } body { background: black; display: flex; align-items: center; justify-content: center; } </style> </head> <body> <canvas width="750" height="585" id="game"></canvas> <script> const canvas = document.getElementById('game'); const context = canvas.getContext('2d'); const grid = 15; const paddleHeight = grid * 5; // 80 const maxPaddleY = canvas.height - grid - paddleHeight; var paddleSpeed = 6; var ballSpeed = 5; const leftPaddle = { // start in the middle of the game on the left side x: grid * 2, y: canvas.height / 2 - paddleHeight / 2, width: grid, height: paddleHeight, // paddle velocity dy: 0 }; const rightPaddle = { // start in the middle of the game on the right side x: canvas.width - grid * 3, y: canvas.height / 2 - paddleHeight / 2, width: grid, height: paddleHeight, // paddle velocity dy: 0 }; const ball = { // start in the middle of the game x: canvas.width / 2, y: canvas.height / 2, width: grid, height: grid, // keep track of when need to reset the ball position resetting: false, // ball velocity (start going to the top-right corner) dx: ballSpeed, dy: -ballSpeed }; // check for collision between two objects using axis-aligned bounding box (AABB) // @see https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection function collides(obj1, obj2) { return obj1.x < obj2.x + obj2.width && obj1.x + obj1.width > obj2.x && obj1.y < obj2.y + obj2.height && obj1.y + obj1.height > obj2.y; } // game loop function loop() { requestAnimationFrame(loop); context.clearRect(0,0,canvas.width,canvas.height); // move paddles by their velocity leftPaddle.y += leftPaddle.dy; rightPaddle.y += rightPaddle.dy; // prevent paddles from going through walls if (leftPaddle.y < grid) { leftPaddle.y = grid; } else if (leftPaddle.y > maxPaddleY) { leftPaddle.y = maxPaddleY; } if (rightPaddle.y < grid) { rightPaddle.y = grid; } else if (rightPaddle.y > maxPaddleY) { rightPaddle.y = maxPaddleY; } // draw paddles context.fillStyle = 'white'; context.fillRect(leftPaddle.x, leftPaddle.y, leftPaddle.width, leftPaddle.height); context.fillRect(rightPaddle.x, rightPaddle.y, rightPaddle.width, rightPaddle.height); // move ball by its velocity ball.x += ball.dx; ball.y += ball.dy; // prevent ball from going through walls by changing its velocity if (ball.y < grid) { ball.y = grid; ball.dy *= -1; } else if (ball.y + grid > canvas.height - grid) { ball.y = canvas.height - grid * 2; ball.dy *= -1; } // reset ball if it goes past paddle (but only if we haven't already done so) if ( (ball.x < 0 || ball.x > canvas.width) && !ball.resetting) { ball.resetting = true; // give some time for the player to recover before launching the ball again setTimeout(() => { ball.resetting = false; ball.x = canvas.width / 2; ball.y = canvas.height / 2; }, 400); } // check to see if ball collides with paddle. if they do change x velocity if (collides(ball, leftPaddle)) { ball.dx *= -1; // move ball next to the paddle otherwise the collision will happen again // in the next frame ball.x = leftPaddle.x + leftPaddle.width; } else if (collides(ball, rightPaddle)) { ball.dx *= -1; // move ball next to the paddle otherwise the collision will happen again // in the next frame ball.x = rightPaddle.x - ball.width; } // draw ball context.fillRect(ball.x, ball.y, ball.width, ball.height); // draw walls context.fillStyle = 'lightgrey'; context.fillRect(0, 0, canvas.width, grid); context.fillRect(0, canvas.height - grid, canvas.width, canvas.height); // draw dotted line down the middle for (let i = grid; i < canvas.height - grid; i += grid * 2) { context.fillRect(canvas.width / 2 - grid / 2, i, grid, grid); } } // listen to keyboard events to move the paddles document.addEventListener('keydown', function(e) { // up arrow key if (e.which === 38) { rightPaddle.dy = -paddleSpeed; } // down arrow key else if (e.which === 40) { rightPaddle.dy = paddleSpeed; } // w key if (e.which === 87) { leftPaddle.dy = -paddleSpeed; } // a key else if (e.which === 83) { leftPaddle.dy = paddleSpeed; } }); // listen to keyboard events to stop the paddle if key is released document.addEventListener('keyup', function(e) { if (e.which === 38 || e.which === 40) { rightPaddle.dy = 0; } if (e.which === 83 || e.which === 87) { leftPaddle.dy = 0; } }); // start the game requestAnimationFrame(loop); </script> </body> </html>#649,850textInscription #431543#431,543html