mirror of
https://github.com/cmclark00/tetris-3d.git
synced 2025-05-17 23:25:21 +01:00
Fix mobile view: next piece display, game board scaling, and visibility issues
This commit is contained in:
parent
6285492580
commit
40700d1636
2 changed files with 112 additions and 21 deletions
117
script.js
117
script.js
|
@ -381,11 +381,11 @@ class Firework {
|
|||
|
||||
// The Piece class
|
||||
class Piece {
|
||||
constructor(tetromino, color) {
|
||||
constructor(tetromino, tetrominoN, color) {
|
||||
this.tetromino = tetromino;
|
||||
this.color = color;
|
||||
|
||||
this.tetrominoN = 0; // Default rotation state
|
||||
this.tetrominoN = tetrominoN || 0; // Rotation state
|
||||
this.activeTetromino = this.tetromino[this.tetrominoN];
|
||||
this.shadowTetromino = this.activeTetromino; // For shadow calculation
|
||||
|
||||
|
@ -1510,8 +1510,11 @@ function drawBoard() {
|
|||
|
||||
// Generate random piece
|
||||
function randomPiece() {
|
||||
let r = Math.floor(Math.random() * PIECES.length);
|
||||
return new Piece(PIECES[r], COLORS[r]);
|
||||
let randomN = Math.floor(Math.random() * PIECES.length);
|
||||
let randomTetromino = PIECES[randomN];
|
||||
let randomIndex = Math.floor(Math.random() * randomTetromino.length);
|
||||
|
||||
return new Piece(randomTetromino, randomIndex, COLORS[randomN]);
|
||||
}
|
||||
|
||||
// Play piece movement sounds
|
||||
|
@ -1620,7 +1623,26 @@ function draw() {
|
|||
// Drop the piece - called by interval
|
||||
function dropPiece() {
|
||||
if (gameOver || paused) return;
|
||||
p.moveDown();
|
||||
|
||||
let now = Date.now();
|
||||
let delta = now - dropStart;
|
||||
|
||||
// Drop speed depends on level
|
||||
let speed = 1000 * (1 - (level - 1) * 0.1);
|
||||
speed = Math.max(speed, 100); // Don't allow too fast drops (minimum 100ms)
|
||||
|
||||
if (delta > speed) {
|
||||
p.moveDown();
|
||||
dropStart = now;
|
||||
|
||||
// If we can't move down and we're still at the top, game over
|
||||
if (p.collision(0, 1) && p.y < 1) {
|
||||
gameOver = true;
|
||||
showGameOver();
|
||||
}
|
||||
}
|
||||
|
||||
if (!gameOver) requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
// Show game over modal
|
||||
|
@ -1752,9 +1774,11 @@ function init() {
|
|||
// Draw the board
|
||||
drawBoard();
|
||||
|
||||
// Generate pieces
|
||||
// Generate initial pieces
|
||||
p = randomPiece();
|
||||
nextPiece = randomPiece();
|
||||
|
||||
// Draw initial next piece
|
||||
nextPiece.drawNextPiece();
|
||||
|
||||
// Set up game interval
|
||||
|
@ -1836,13 +1860,13 @@ function handleResize() {
|
|||
|
||||
// For portrait: maximize width, maintain aspect ratio
|
||||
if (isPortrait) {
|
||||
// Use 95% of viewport width
|
||||
const targetWidth = viewportWidth * 0.95;
|
||||
// Use 90% of viewport width
|
||||
const targetWidth = viewportWidth * 0.9;
|
||||
const targetHeight = (targetWidth / COLS) * ROWS;
|
||||
|
||||
// If height is too tall, scale down
|
||||
if (targetHeight > availableHeight * 0.9) {
|
||||
const scaleFactor = (availableHeight * 0.9) / targetHeight;
|
||||
if (targetHeight > availableHeight * 0.8) {
|
||||
const scaleFactor = (availableHeight * 0.8) / targetHeight;
|
||||
canvas.style.width = `${targetWidth * scaleFactor}px`;
|
||||
canvas.style.height = `${targetHeight * scaleFactor}px`;
|
||||
} else {
|
||||
|
@ -1852,8 +1876,8 @@ function handleResize() {
|
|||
}
|
||||
// For landscape: maximize height, maintain aspect ratio
|
||||
else {
|
||||
// Use 80% of available height
|
||||
const targetHeight = availableHeight * 0.8;
|
||||
// Use 75% of available height
|
||||
const targetHeight = availableHeight * 0.75;
|
||||
const targetWidth = (targetHeight / ROWS) * COLS;
|
||||
|
||||
// If width is too wide, scale down
|
||||
|
@ -1867,11 +1891,12 @@ function handleResize() {
|
|||
}
|
||||
}
|
||||
|
||||
// Scale next piece preview to match
|
||||
if (nextPieceCanvas) {
|
||||
const scale = parseInt(canvas.style.width) / (COLS * BLOCK_SIZE);
|
||||
nextPieceCanvas.style.transform = `scale(${scale})`;
|
||||
nextPieceCanvas.style.transformOrigin = 'top left';
|
||||
// Force a redraw of the nextPiece preview to fix rendering issues
|
||||
if (nextPiece) {
|
||||
setTimeout(() => {
|
||||
nextPieceCtx.clearRect(0, 0, nextPieceCanvas.width, nextPieceCanvas.height);
|
||||
nextPiece.drawNextPiece();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Show touch controls mode
|
||||
|
@ -2303,6 +2328,64 @@ function createTouchControlButtons() {
|
|||
}, 5000);
|
||||
}
|
||||
|
||||
// Set active piece to next piece and create new next piece
|
||||
function getNextPiece() {
|
||||
// Current piece becomes the next piece
|
||||
p = nextPiece;
|
||||
|
||||
// Generate a new next piece
|
||||
nextPiece = randomPiece();
|
||||
|
||||
// Draw the next piece in preview
|
||||
nextPiece.drawNextPiece();
|
||||
|
||||
// Force redraw for mobile to ensure it renders
|
||||
if (isMobile || forceMobileControls) {
|
||||
setTimeout(() => {
|
||||
nextPieceCtx.clearRect(0, 0, nextPieceCanvas.width, nextPieceCanvas.height);
|
||||
nextPiece.drawNextPiece();
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
// Lock the piece in the board and get the next piece
|
||||
function lockPiece() {
|
||||
// Add piece to board
|
||||
for (let r = 0; r < p.activeTetromino.length; r++) {
|
||||
for (let c = 0; c < p.activeTetromino[r].length; c++) {
|
||||
// Skip empty squares
|
||||
if (!p.activeTetromino[r][c]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Game over when piece is locked at the top
|
||||
if (p.y + r < 0) {
|
||||
gameOver = true;
|
||||
showGameOver();
|
||||
return;
|
||||
}
|
||||
|
||||
// Lock piece
|
||||
board[p.y + r][p.x + c] = p.color;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for completed rows
|
||||
checkRows();
|
||||
|
||||
// Update the score
|
||||
updateScore();
|
||||
|
||||
// Get next piece
|
||||
getNextPiece();
|
||||
|
||||
// Reset drop timer
|
||||
dropStart = Date.now();
|
||||
|
||||
// Play drop sound
|
||||
playSound(dropSound);
|
||||
}
|
||||
|
||||
// Start the game
|
||||
window.onload = function() {
|
||||
init();
|
||||
|
|
14
style.css
14
style.css
|
@ -516,15 +516,20 @@ input[type=range]::-moz-range-thumb {
|
|||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
margin-bottom: 20px; /* Add bottom margin to make room for next piece */
|
||||
}
|
||||
|
||||
.mobile-mode #next-piece-preview {
|
||||
position: absolute;
|
||||
top: -50px; /* Position above the game board */
|
||||
top: auto;
|
||||
bottom: -70px; /* Position below the game board */
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
margin: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
z-index: 10;
|
||||
border-color: rgba(0, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.mobile-mode .score-container {
|
||||
|
@ -589,10 +594,12 @@ input[type=range]::-moz-range-thumb {
|
|||
@media (orientation: portrait) {
|
||||
.mobile-mode .game-container {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 80px; /* Add bottom padding for next piece */
|
||||
}
|
||||
|
||||
.mobile-mode canvas#tetris {
|
||||
width: 90vw;
|
||||
max-height: 75vh; /* Ensure game board doesn't take too much height */
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
@ -623,13 +630,14 @@ input[type=range]::-moz-range-thumb {
|
|||
.mobile-mode #next-piece-preview {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: auto;
|
||||
left: 100%;
|
||||
margin-left: 10px;
|
||||
transform: none;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.mobile-mode canvas#tetris {
|
||||
height: 80vh;
|
||||
height: 75vh; /* Slightly reduced to ensure full visibility */
|
||||
width: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue