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
|
// The Piece class
|
||||||
class Piece {
|
class Piece {
|
||||||
constructor(tetromino, color) {
|
constructor(tetromino, tetrominoN, color) {
|
||||||
this.tetromino = tetromino;
|
this.tetromino = tetromino;
|
||||||
this.color = color;
|
this.color = color;
|
||||||
|
|
||||||
this.tetrominoN = 0; // Default rotation state
|
this.tetrominoN = tetrominoN || 0; // Rotation state
|
||||||
this.activeTetromino = this.tetromino[this.tetrominoN];
|
this.activeTetromino = this.tetromino[this.tetrominoN];
|
||||||
this.shadowTetromino = this.activeTetromino; // For shadow calculation
|
this.shadowTetromino = this.activeTetromino; // For shadow calculation
|
||||||
|
|
||||||
|
@ -1510,8 +1510,11 @@ function drawBoard() {
|
||||||
|
|
||||||
// Generate random piece
|
// Generate random piece
|
||||||
function randomPiece() {
|
function randomPiece() {
|
||||||
let r = Math.floor(Math.random() * PIECES.length);
|
let randomN = Math.floor(Math.random() * PIECES.length);
|
||||||
return new Piece(PIECES[r], COLORS[r]);
|
let randomTetromino = PIECES[randomN];
|
||||||
|
let randomIndex = Math.floor(Math.random() * randomTetromino.length);
|
||||||
|
|
||||||
|
return new Piece(randomTetromino, randomIndex, COLORS[randomN]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Play piece movement sounds
|
// Play piece movement sounds
|
||||||
|
@ -1620,7 +1623,26 @@ function draw() {
|
||||||
// Drop the piece - called by interval
|
// Drop the piece - called by interval
|
||||||
function dropPiece() {
|
function dropPiece() {
|
||||||
if (gameOver || paused) return;
|
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
|
// Show game over modal
|
||||||
|
@ -1752,9 +1774,11 @@ function init() {
|
||||||
// Draw the board
|
// Draw the board
|
||||||
drawBoard();
|
drawBoard();
|
||||||
|
|
||||||
// Generate pieces
|
// Generate initial pieces
|
||||||
p = randomPiece();
|
p = randomPiece();
|
||||||
nextPiece = randomPiece();
|
nextPiece = randomPiece();
|
||||||
|
|
||||||
|
// Draw initial next piece
|
||||||
nextPiece.drawNextPiece();
|
nextPiece.drawNextPiece();
|
||||||
|
|
||||||
// Set up game interval
|
// Set up game interval
|
||||||
|
@ -1836,13 +1860,13 @@ function handleResize() {
|
||||||
|
|
||||||
// For portrait: maximize width, maintain aspect ratio
|
// For portrait: maximize width, maintain aspect ratio
|
||||||
if (isPortrait) {
|
if (isPortrait) {
|
||||||
// Use 95% of viewport width
|
// Use 90% of viewport width
|
||||||
const targetWidth = viewportWidth * 0.95;
|
const targetWidth = viewportWidth * 0.9;
|
||||||
const targetHeight = (targetWidth / COLS) * ROWS;
|
const targetHeight = (targetWidth / COLS) * ROWS;
|
||||||
|
|
||||||
// If height is too tall, scale down
|
// If height is too tall, scale down
|
||||||
if (targetHeight > availableHeight * 0.9) {
|
if (targetHeight > availableHeight * 0.8) {
|
||||||
const scaleFactor = (availableHeight * 0.9) / targetHeight;
|
const scaleFactor = (availableHeight * 0.8) / targetHeight;
|
||||||
canvas.style.width = `${targetWidth * scaleFactor}px`;
|
canvas.style.width = `${targetWidth * scaleFactor}px`;
|
||||||
canvas.style.height = `${targetHeight * scaleFactor}px`;
|
canvas.style.height = `${targetHeight * scaleFactor}px`;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1852,8 +1876,8 @@ function handleResize() {
|
||||||
}
|
}
|
||||||
// For landscape: maximize height, maintain aspect ratio
|
// For landscape: maximize height, maintain aspect ratio
|
||||||
else {
|
else {
|
||||||
// Use 80% of available height
|
// Use 75% of available height
|
||||||
const targetHeight = availableHeight * 0.8;
|
const targetHeight = availableHeight * 0.75;
|
||||||
const targetWidth = (targetHeight / ROWS) * COLS;
|
const targetWidth = (targetHeight / ROWS) * COLS;
|
||||||
|
|
||||||
// If width is too wide, scale down
|
// If width is too wide, scale down
|
||||||
|
@ -1867,11 +1891,12 @@ function handleResize() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale next piece preview to match
|
// Force a redraw of the nextPiece preview to fix rendering issues
|
||||||
if (nextPieceCanvas) {
|
if (nextPiece) {
|
||||||
const scale = parseInt(canvas.style.width) / (COLS * BLOCK_SIZE);
|
setTimeout(() => {
|
||||||
nextPieceCanvas.style.transform = `scale(${scale})`;
|
nextPieceCtx.clearRect(0, 0, nextPieceCanvas.width, nextPieceCanvas.height);
|
||||||
nextPieceCanvas.style.transformOrigin = 'top left';
|
nextPiece.drawNextPiece();
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show touch controls mode
|
// Show touch controls mode
|
||||||
|
@ -2303,6 +2328,64 @@ function createTouchControlButtons() {
|
||||||
}, 5000);
|
}, 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
|
// Start the game
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
init();
|
init();
|
||||||
|
|
16
style.css
16
style.css
|
@ -516,15 +516,20 @@ input[type=range]::-moz-range-thumb {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 20px; /* Add bottom margin to make room for next piece */
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-mode #next-piece-preview {
|
.mobile-mode #next-piece-preview {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -50px; /* Position above the game board */
|
top: auto;
|
||||||
|
bottom: -70px; /* Position below the game board */
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: rgba(0, 0, 0, 0.7);
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
z-index: 10;
|
||||||
|
border-color: rgba(0, 255, 255, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-mode .score-container {
|
.mobile-mode .score-container {
|
||||||
|
@ -589,10 +594,12 @@ input[type=range]::-moz-range-thumb {
|
||||||
@media (orientation: portrait) {
|
@media (orientation: portrait) {
|
||||||
.mobile-mode .game-container {
|
.mobile-mode .game-container {
|
||||||
padding-top: 40px;
|
padding-top: 40px;
|
||||||
|
padding-bottom: 80px; /* Add bottom padding for next piece */
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-mode canvas#tetris {
|
.mobile-mode canvas#tetris {
|
||||||
width: 90vw;
|
width: 90vw;
|
||||||
|
max-height: 75vh; /* Ensure game board doesn't take too much height */
|
||||||
height: auto;
|
height: auto;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
@ -623,13 +630,14 @@ input[type=range]::-moz-range-thumb {
|
||||||
.mobile-mode #next-piece-preview {
|
.mobile-mode #next-piece-preview {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
bottom: auto;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
margin-left: 10px;
|
|
||||||
transform: none;
|
transform: none;
|
||||||
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-mode canvas#tetris {
|
.mobile-mode canvas#tetris {
|
||||||
height: 80vh;
|
height: 75vh; /* Slightly reduced to ensure full visibility */
|
||||||
width: auto;
|
width: auto;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue