如何找到俄罗斯方块(俄罗斯方块)在网格上的位置并将其显示为预览



我正在学习HTML画布,并选择制作俄罗斯方块游戏作为我通过观看一些教程完成游戏的第一个项目。但是,如果想通过添加Tetromino登陆页面的实时预览来做得更好,有人能帮我吗?我该怎么做?我所说的预览是指我如何向玩家展示棋子落在gameArea上的位置。

像这样的

Html文件:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tetris Game</title>
<link rel="stylesheet" href="./Game Assets/Style/style.css">
<link rel="shortcut icon" href="./Game Assets/img/tetris.png" type="image/x-icon">
</head>
<body body>
<div id="wrapper">
<div id="GameArea">
<canvas id="gameCanvas" width="300" height="600">
</canvas>
<div id="SLN">
<div id="SCORE">
<h3>Score:</h3>
<div id="sc">0</div>
</div>
<div id="Level">
</div>
<div id="NEXT">
<h3>Next:</h3>
<canvas id="preCanvas" width="120" height="120"></canvas>
</div>
</div>
</div>
</div>
<script src="./Game Assets/Script/index.js"></script>
</body>
</html>

这是Java脚本文件

const canvas = document.getElementById("gameCanvas"); //Get The Canvas Element
const ctx = canvas.getContext("2d");
const preview = document.getElementById("preCanvas");
const pre = preview.getContext("2d");
//Tetrominoes Code
const I = [
[
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
],
[
[0, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 0, 0],
[0, 0, 0, 0],
],
[
[0, 0, 1, 0],
[0, 0, 1, 0],
[0, 0, 1, 0],
[0, 0, 1, 0],
],
[
[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 0, 0],
]
];
const J = [
[
[1, 0, 0],
[1, 1, 1],
[0, 0, 0]
],
[
[0, 1, 1],
[0, 1, 0],
[0, 1, 0]
],
[
[0, 0, 0],
[1, 1, 1],
[0, 0, 1]
],
[
[0, 1, 0],
[0, 1, 0],
[1, 1, 0]
]
];
const L = [
[
[0, 0, 1],
[1, 1, 1],
[0, 0, 0]
],
[
[0, 1, 0],
[0, 1, 0],
[0, 1, 1]
],
[
[0, 0, 0],
[1, 1, 1],
[1, 0, 0]
],
[
[1, 1, 0],
[0, 1, 0],
[0, 1, 0]
]
];
const O = [
[
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
]
];
const S = [
[
[0, 1, 1],
[1, 1, 0],
[0, 0, 0]
],
[
[0, 1, 0],
[0, 1, 1],
[0, 0, 1]
],
[
[0, 0, 0],
[0, 1, 1],
[1, 1, 0]
],
[
[1, 0, 0],
[1, 1, 0],
[0, 1, 0]
]
];
const T = [
[
[0, 1, 0],
[1, 1, 1],
[0, 0, 0]
],
[
[0, 1, 0],
[0, 1, 1],
[0, 1, 0]
],
[
[0, 0, 0],
[1, 1, 1],
[0, 1, 0]
],
[
[0, 1, 0],
[1, 1, 0],
[0, 1, 0]
]
];
const Z = [
[
[1, 1, 0],
[0, 1, 1],
[0, 0, 0]
],
[
[0, 0, 1],
[0, 1, 1],
[0, 1, 0]
],
[
[0, 0, 0],
[1, 1, 0],
[0, 1, 1]
],
[
[0, 1, 0],
[1, 1, 0],
[1, 0, 0]
]
];
// we first need too define ROW and  columns constants
const ROW = 20;
const COL = 10;
// Const for square size
const SQ = 30;
function drawSquare(x, y, color, stroke, T) {
if (stroke === "piece") {
ctx.fillStyle = color;
ctx.fillRect(x * SQ, y * SQ, SQ, SQ);
ctx.strokeStyle = "black";;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.strokeRect(x * SQ + 1, y * SQ + 1, SQ - 2, SQ - 2);
ctx.strokeRect(x * SQ + 2, y * SQ + 2, SQ - 4, SQ - 4);
// ctx.strokeRect(x * SQ + 2.5, y * SQ + 2.5, SQ - 5, SQ - 5);


ctx.lineWidth = 1;
ctx.beginPath();
ctx.strokeRect(x * SQ + 6.5, y * SQ + 6.5, SQ - 13, SQ - 13);

ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(x * SQ + 2, y * SQ + 2)
ctx.lineTo(x * SQ + 6.5, y * SQ + 6.5);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x * SQ + SQ - 2, y * SQ + 2)
ctx.lineTo(x * SQ + SQ - 6.5, y * SQ + 6.5);
ctx.stroke();
// Downn
ctx.beginPath();
ctx.moveTo(x * SQ + 2, y * SQ - 2 + SQ)
ctx.lineTo(x * SQ + 6.5, y * SQ - 6.5 + SQ);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x * SQ + SQ - 2, y * SQ - 2 + SQ)
ctx.lineTo(x * SQ - 6.5 + SQ, y * SQ - 6.5 + SQ);
ctx.stroke();

} else {
ctx.fillStyle = color;
ctx.fillRect(x * SQ, y * SQ, SQ, SQ);
}
};

// a VACANT (empty) square has this color.
// const VACANT = "rgba(19, 18, 18, 0.719)";
const VACANT = "transparent";
// now we define the board array.
let board = [];

// let's create the rows.
for (let r = 0; r < ROW; r++) {
board[r] = [];
// let's create the columns
for (let c = 0; c < COL; c++) {
board[r][c] = VACANT;
// when we first draw the board all the square are empty, so every square has the value "VACANT".
}
}

function drawBoard() {
for (r = 0; r < ROW; r++) {
for (c = 0; c < COL; c++) {
if (board[r][c] != VACANT) {
drawSquare(c, r, board[r][c], "piece")
} else {
drawSquare(c, r, board[r][c], "board");
}
}
}
}
drawBoard()
//Piece and there color
let Score = 0;
const PIECES = [
[Z, "blue"],
[S, "rgb(238, 132, 46)"],
[T, "rgb(248, 232, 232)"],
[O, "yellow"],
[L, "rgb(245, 94, 144)"],
[I, "purple"],
[J, "rgb(121, 236, 240)"]
]
// Generator random Piece
function NewPiece(canvas) {
let r = randomN = Math.floor(Math.random() * PIECES.length)
return new Piece(PIECES[r][0], PIECES[r][1])
}

function nextPiece() {
r = Math.floor(Math.random() * PIECES.length)
previewPiece = new PreviewPiece(PIECES[r][0], PIECES[r][1]);
xd = r
previewPiece.clear();
previewPiece.draw();
return xd
}
x = nextPiece()
let p = new Piece(PIECES[x][0], PIECES[x][1]);

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;
// Piece.prototype.draw
this.draw = function() {
for (r = 0; r < this.activeTetromino.length; r++) {
for (c = 0; c < this.activeTetromino.length; c++) {
if (this.activeTetromino[r][c]) {
drawSquare(this.x + c, this.y + r, this.color, "piece");
}
}
}

}
// Piece.prototype.Clear 
this.update = function() {
for (r = 0; r < this.activeTetromino.length; r++) {
for (c = 0; c < this.activeTetromino.length; c++) {
if (this.activeTetromino[r][c]) {
update();
}
}
}
}
// Piece.prototype.moveDown
this.moveDown = function() {
if (!this.collide(0, 1, this.activeTetromino)) {
this.update();
this.y++;
this.draw();
// ctx.shadowOffsetY -= SQ
} else {
//generate new Piece
this.lockPiece();
p = new Piece(PIECES[x][0], PIECES[x][1]);
}
if (this.y === -1) {
// ctx.shadowOffsetY -= 1 * SQ
x = nextPiece();
}
}
// Piece.prototype.moveRight 
this.moveRight = function() {
if (!this.collide(1, 0, this.activeTetromino)) {
this.update();
this.x++;
this.draw();
}
}
// Piece.prototype.moveLeft
this.moveLeft = function() {
if (!this.collide(-1, 0, this.activeTetromino)) {
this.update();
this.x--;
this.draw();
}
}
// Piece.prototype.rotate 
this.rotate = function() {
let nextPat = this.tetromino[(this.tetrominoN + 1) % this.tetromino.length];
let kick = 0;
if (this.collide(0, 0, nextPat)) {
if (this.x > COL / 2) {
kick = -1;
} else {
kick = 1;
}
}
if (!this.collide(kick, 0, nextPat)) {
this.update();
this.x += kick;
this.tetrominoN = (this.tetrominoN + 1) % this.tetromino.length;
this.activeTetromino = this.tetromino[this.tetrominoN];
this.draw();
}
}
// Piece.prototype.collide
this.collide = function(x, y, piece) {
for (r = 0; r < piece.length; r++) {
for (c = 0; c < piece.length; c++) {
//Empty square skip
if (!piece[r][c]) {
continue;
}
let newX = this.x + c + x;
let newY = this.y + r + y;
if (newX < 0 || newX > COL || newY >= ROW) {
return true;
}
if (newY < 0) {
continue;
}
if (board[newY][newX] != VACANT) { return true; }
}
}
}
this.lockPiece = function() {
for (r = 0; r < this.activeTetromino.length; r++) {
for (c = 0; c < this.activeTetromino.length; c++) {
//skip empty block
if (!this.activeTetromino[r][c]) {
continue;
}
//piece to lock if reaches the top
if (this.y + r < 0) {
//Game over
// alert("Game Over");
//Stop Game
gameOver = true;
if (gameOver) {
// document.getElementById("gameCanvas").style.display = "none"
}
break;
}
//we lock piece when it reaches bottom
board[this.y + r][this.x + c] = this.color
}
}
// remove full rows
for (r = 0; r < ROW; r++) {
let isRowFull = true;
for (c = 0; c < COL; c++) {
isRowFull = isRowFull && (board[r][c] != VACANT);
}
if (isRowFull) {
for (y = r; y > 1; y--) {
for (c = 0; c < COL; c++) {
// ctx.clearRect(y * SQ, c * SQ, SQ, SQ);
board[y][c] = board[y - 1][c];
}
}
//the top row board[0][...] has no row above it
for (c = 0; c < COL; c++) {
board[0][c] = VACANT;
// ctx.clearRect(r * SQ, c * SQ, SQ, SQ);
}
//Increase Score
Score += 100;
console.log(Score)
}
// if (this.x > 0) {
//     // x = nextPiece();
// }
}
update();
}
}
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
drawBoard();
}

let dropStart = Date.now();
let gameOver = false;
let speed = 1000;
let PrevScore = 0;
function drop() {
let now = Date.now();
let delta = now - dropStart;
if (delta > speed) {
// p.x = 3;
// p.y = -1
p.moveDown()
dropStart = Date.now();
}
if (!gameOver) {
requestAnimationFrame(drop)
}
document.getElementById("sc").innerHTML = Score;
if (Score - PrevScore > 1000) {
speed -= 10;
PrevScore = Score;
}
}
drop()
document.addEventListener("keydown", CONTROL);
function CONTROL(event) {
if (event.keyCode == 37) {
if (!gameOver) {
event.preventDefault();
p.moveLeft();
// dropStart = Date.now();
}
} else if (event.keyCode == 38) {
if (!gameOver) {
event.preventDefault();
p.rotate();
// dropStart = Date.now();
}
} else if (event.keyCode == 39) {
if (!gameOver) {
event.preventDefault();
p.moveRight()
// dropStart = Date.now();
}
} else if (event.keyCode == 40) {
if (!gameOver) {
event.preventDefault();
Score++;
p.moveDown();
}
}
}
document.getElementById("gameCanvas").addEventListener("click", function(e) {
e.preventDefault();
p.rotate();
dropStart = Date.now();
})
let TouchX, TouchY, MoveX = 0,
MoveY = 0,
XDiff, YDiff;
document.getElementById("GameArea").addEventListener("touchstart", function(e) {
TouchX = e.touches[0].clientX
TouchY = e.touches[0].clientY
// console.log("Tx: ", TouchX, "Ty: ", TouchY)
document.getElementById("GameArea").addEventListener('touchmove', function(e) {
e.preventDefault() // prevent scrolling when inside DIV
XDiff = TouchX - MoveX;
YDiff = TouchY - MoveY;
if (Math.abs(e.touches[0].clientX - MoveX) > 15) {
if (Math.abs(XDiff) > Math.abs(YDiff)) {
if (XDiff < 0) {
//Right Swipe;
p.moveRight();
} else {
// Left Swipe
p.moveLeft();
}
}
} else if (Math.abs(e.touches[0].clientY - MoveY) > 0) {
if (Math.abs(XDiff) < Math.abs(YDiff)) {
if (XDiff < 0) {
//Down Swipe;
Score++;
p.moveDown();
}
}
}
MoveX = e.touches[0].clientX;
MoveY = e.touches[0].clientY;
// console.log("x: ", e.touches[0].clientX, "y: ", e.touches[0].clientY);

}, { passive: false })
}, false);

function drawSquarePreview(x, y, color) {
pre.fillStyle = color;
pre.fillRect(x * SQ, y * SQ, SQ, SQ);
pre.strokeStyle = "black";;
pre.lineWidth = 1;
pre.beginPath();
pre.strokeRect(x * SQ + 1, y * SQ + 1, SQ - 2, SQ - 2);
pre.strokeRect(x * SQ + 2, y * SQ + 2, SQ - 4, SQ - 4);
// pre.strokeRect(x * SQ + 2.5, y * SQ + 2.5, SQ - 5, SQ - 5);


pre.lineWidth = 1;
pre.beginPath();
pre.strokeRect(x * SQ + 6.5, y * SQ + 6.5, SQ - 13, SQ - 13);

pre.lineWidth = 1;
pre.beginPath();
pre.moveTo(x * SQ + 2, y * SQ + 2)
pre.lineTo(x * SQ + 6.5, y * SQ + 6.5);
pre.stroke();
pre.beginPath();
pre.moveTo(x * SQ + SQ - 2, y * SQ + 2)
pre.lineTo(x * SQ + SQ - 6.5, y * SQ + 6.5);
pre.stroke();
// Down
pre.beginPath();
pre.moveTo(x * SQ + 2, y * SQ - 2 + SQ)
pre.lineTo(x * SQ + 6.5, y * SQ - 6.5 + SQ);
pre.stroke();
pre.beginPath();
pre.moveTo(x * SQ + SQ - 2, y * SQ - 2 + SQ)
pre.lineTo(x * SQ - 6.5 + SQ, y * SQ - 6.5 + SQ);
}
function PreviewPiece(Tetromino, color) {
this.tetromino = Tetromino;
this.color = color;
this.tetrominoN = 0;
this.activeTetromino = this.tetromino[this.tetrominoN];
this.x = 0;
this.y = 0;
this.draw = function() {
for (r = 0; r < this.activeTetromino.length; r++) {
for (c = 0; c < this.activeTetromino.length; c++) {
if (this.activeTetromino[r][c]) {
drawSquarePreview(this.x + c, this.y + r, this.color);
}
}
}
}
this.clear = function() {
pre.clearRect(0, 0, preview.width, preview.height);
}
}

回答我自己的问题很有趣,但我在梦中找到了答案,所以让我把它放在这里,这样如果有人想知道答案,就可以得到帮助。因为,

社区就是这么做的。

所以我们不要再浪费时间了。

检查编辑后游戏的外观

因此,首先我创建了一个名为shadow的新原型函数,然后使用for循环返回for循环实例(tetromino碰撞的地方(减1来检查tetromino在哪里碰撞。因为我们想像这个一样在碰撞前一步画出阴影

this.shadow = function() {
for (i = 1; i < ROW; i++) {
if (this.collide(0, i, this.activeTetromino)) {
return i - 1 //i is the point of collision therefore we draw piece one step before collision
} else { continue; }
}
}

然后,我更新了绘图功能像这个

this.draw = function() {
let sha = this.shadow();
for (r = 0; r < this.activeTetromino.length; r++) {
for (c = 0; c < this.activeTetromino.length; c++) {
if (this.activeTetromino[r][c]) {
drawSquare(this.x + c, this.y + r, this.color, "piece");
// this will also work without the if condition 
if (this.y + r !== this.y + r + sha) {
drawSquare(this.x + c, this.y + sha + r, this.color, "piece", "shadow")
}
}
}
}
}

在这个函数中,我将shadow函数返回的值存储在一个名为sha的变量中,然后绘制将y变为this.y + sha + r的tetromino片段,并传递一个额外的参数来绘制shadow&你猜对了,drawSquare函数中有一个更新,可以将正方形绘制成不同的狭缝,这样玩家就可以判断出哪块是阴影,哪块是实际的碎片。在这里,你可以做任何事情来改变它,比如只给它灰色,或者只画笔划边界,或者改变全局Apha值,所以我选择像这个一样改变全局alpha值

function drawSquare(x, y, color, stroke, T) {
ctx.globalAlpha = 1;
if (stroke === "piece") {
if (T === "shadow") { ctx.globalAlpha = 0.4; }
ctx.fillStyle = color;
ctx.fillRect(x * SQ, y * SQ, SQ, SQ);
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
if (T === "shadow") { ctx.lineWidth = 3; };
ctx.beginPath();
ctx.strokeRect(x * SQ + 1, y * SQ + 1, SQ - 2, SQ - 2);
ctx.strokeRect(x * SQ + 2, y * SQ + 2, SQ - 4, SQ - 4);
// ctx.strokeRect(x * SQ + 2.5, y * SQ + 2.5, SQ - 5, SQ - 5);


ctx.lineWidth = 1;
if (T === "shadow") { ctx.lineWidth = 2; };
ctx.beginPath();
ctx.strokeRect(x * SQ + 6.5, y * SQ + 6.5, SQ - 13, SQ - 13);

ctx.lineWidth = 2;
// if (T === "shadow") { ctx.lineWidth = 5; };
ctx.beginPath();
ctx.moveTo(x * SQ + 2, y * SQ + 2)
ctx.lineTo(x * SQ + 6.5, y * SQ + 6.5);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x * SQ + SQ - 2, y * SQ + 2)
ctx.lineTo(x * SQ + SQ - 6.5, y * SQ + 6.5);
ctx.stroke();
// Downn
ctx.beginPath();
ctx.moveTo(x * SQ + 2, y * SQ - 2 + SQ)
ctx.lineTo(x * SQ + 6.5, y * SQ - 6.5 + SQ);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x * SQ + SQ - 2, y * SQ - 2 + SQ)
ctx.lineTo(x * SQ - 6.5 + SQ, y * SQ - 6.5 + SQ);
ctx.stroke();

}
}

希望这能帮助你,如果你正在寻找这个。如果您有更好的优化方法,请随时回答。我不是一个很好的解释者,这是我的问题,也是这个表格的答案,所以请原谅任何语法错误。

最新更新