0
点赞
收藏
分享

微信扫一扫

纯JS和canvas实现俄罗斯方块

水沐由之 2022-04-13 阅读 95
javascript
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>俄罗斯方块</title>
    <style>
        body {
            background-color: #666;
        }

        #canvas {
            display: block;
            margin: 0 auto;
        }

        .tips {
            text-align: center;
            color: #fff;
            margin-top: 10px;
            font-size: 12px;
        }
    </style>
</head>

<body>
    <canvas id="canvas" width="250" height="500"></canvas>
    <div class="tips">↑旋转 ←向左移动 →向右移动 ↓向下移动</div>
    <script>
        window.addEventListener("load", () => {
            const I = [
                [
                    [1],
                    [1],
                    [1],
                    [1]
                ],
                [
                    [1, 1, 1, 1]
                ]
            ]
            const L = [
                [
                    [2, 0],
                    [2, 0],
                    [2, 2]
                ],
                [
                    [2, 2, 2],
                    [2, 0, 0]
                ],
                [
                    [2, 2],
                    [0, 2],
                    [0, 2]
                ],
                [
                    [0, 0, 2],
                    [2, 2, 2]
                ]
            ]
            const J = [
                [
                    [0, 3],
                    [0, 3],
                    [3, 3]
                ],
                [
                    [3, 0, 0],
                    [3, 3, 3]
                ],
                [
                    [3, 3],
                    [3, 0],
                    [3, 0]
                ],
                [
                    [3, 3, 3],
                    [0, 0, 3]
                ]
            ]
            const T = [
                [
                    [4, 4, 4],
                    [0, 4, 0]
                ],
                [
                    [0, 4],
                    [4, 4],
                    [0, 4]
                ],
                [
                    [0, 4, 0],
                    [4, 4, 4]
                ],
                [
                    [4, 0],
                    [4, 4],
                    [4, 0]
                ]
            ]
            const S = [
                [
                    [0, 5, 5],
                    [5, 5, 0]
                ],
                [
                    [5, 0],
                    [5, 5],
                    [0, 5]
                ],
                [
                    [0, 5, 5],
                    [5, 5, 0]
                ],
                [
                    [5, 0],
                    [5, 5],
                    [0, 5]
                ]
            ]
            const Z = [
                [
                    [6, 6, 0],
                    [0, 6, 6]
                ],
                [
                    [0, 6],
                    [6, 6],
                    [6, 0]
                ],
                [
                    [6, 6, 0],
                    [0, 6, 6]
                ],
                [
                    [0, 6],
                    [6, 6],
                    [6, 0]
                ]
            ]
            const O = [
                [
                    [7, 7],
                    [7, 7]
                ]
            ]
            const ctx = canvas.getContext("2d");
            const width = 10;
            const height = 20;
            let shapes = [];
            let shapeIndex = 0;
            let shape = [];
            let grid = [];
            let left = 0;
            let top = 0;
            ctx.scale(canvas.width / width, canvas.height / height);
            function drawGrid() {
                grid.forEach((row, y) => {
                    row.forEach((item, x) => {
                        if (item > 0) {
                            ctx.fillStyle = "#f00";
                            ctx.fillRect(x, y, 1, 1);
                        } else {
                            ctx.fillStyle = "#000";
                            ctx.fillRect(x, y, 1, 1);
                        }
                    })
                })
            }

            function createGrid() {
                grid = [];
                for (let y = 0; y < height; y++) {
                    grid.push([]);
                    for (let x = 0; x < width; x++) {
                        grid[y].push(0);
                    }
                }
            }

            function createRandomShape() {
                let allShape = [I, L, J, T, S, Z, O];
                shapes = allShape[Math.floor(Math.random() * allShape.length)];
                shapeIndex = Math.floor(Math.random() * shapes.length);
                shape = shapes[shapeIndex];
                left = Math.floor((width - shape[0].length) / 2);
                top = 0;
            }

            function allowTransformShape(nextShape) {
                let result = true;
                clearPreviousStep(left, top);
                for (let y = 0; y < nextShape.length; y++) {
                    for (let x = 0; x < nextShape[0].length; x++) {
                        let item = nextShape[y][x];
                        if (item > 0) {
                            if (grid[y + top] === undefined || grid[y + top][x + left] !== 0) {
                                return false;
                            }
                        }
                    }
                }
                return result;
            }

            function transfromShape() {
                let nextShapeIndex = -1;
                if (shapeIndex + 1 === shapes.length) {
                    nextShapeIndex = 0
                } else {
                    nextShapeIndex = shapeIndex + 1;
                }
                if (allowTransformShape(shapes[nextShapeIndex])) {
                    shapeIndex = nextShapeIndex;
                    shape = shapes[shapeIndex];
                    addCurrentStep(left, top);
                    drawGrid();
                } else {
                    addCurrentStep(left, top);
                }
            }

            function addCurrentStep(nextLeft, nextTop) {
                for (let y = 0; y < shape.length; y++) {
                    for (let x = 0; x < shape[0].length; x++) {
                        let item = shape[y][x];
                        if (item > 0) {
                            grid[y + nextTop][x + nextLeft] = item;
                        } else {
                            continue;
                        }
                    }
                }
                left = nextLeft;
                top = nextTop;
            }

            function clearPreviousStep(prevLeft, prevTop) {
                for (let y = 0; y < shape.length; y++) {
                    for (let x = 0; x < shape[0].length; x++) {
                        let item = shape[y][x];
                        if (item > 0) {
                            grid[y + prevTop][x + prevLeft] = 0;
                        } else {
                            continue;
                        }
                    }
                }
            }

            function allowNextStep(nextLeft, nextTop) {
                let result = true;
                for (let y = 0; y < shape.length; y++) {
                    for (let x = 0; x < shape[0].length; x++) {
                        let item = shape[y][x];
                        if (item > 0) {
                            // 左侧碰撞检测
                            if (nextLeft < left && shape[y][x - 1] !== item) {
                                if (grid[y + nextTop][x + nextLeft] !== 0) {
                                    return false;
                                }
                            }
                            // 右侧碰撞检测
                            if (nextLeft > left && shape[y][x + 1] !== item) {
                                if (grid[y + nextTop][x + nextLeft] !== 0) {
                                    return false;
                                }
                            }
                            // 底部碰撞检测
                            if (nextTop > top && (shape[y + 1] === undefined || shape[y + 1][x] === 0)) {
                                if (grid[y + nextTop] === undefined || grid[y + nextTop][x + nextLeft] !== 0) {
                                    return false;
                                }
                            }
                        } else {
                            continue;
                        }
                    }
                }
                return result;
            }

            function move(x, y) {
                if (allowNextStep(left + x, top + y)) {
                    clearPreviousStep(left, top);
                    addCurrentStep(left + x, top + y);
                    drawGrid();
                } else {
                    if (y > 0) {
                        clearFullRow();
                        if (top === 0) {
                            restart();
                            return;
                        }
                        createRandomShape();
                        addCurrentStep(left, top);
                        drawGrid();
                    }
                }
            }

            function restart() {
                createGrid();
                createRandomShape();
                addCurrentStep(left, top);
                drawGrid();
            }

            function clearFullRow() {
                let arr = [];
                grid.forEach((row, y) => {
                    if (row.every((item) => item > 0)) {
                        arr.push(y);
                    }
                })
                if (arr.length > 0) {
                    arr.forEach((y) => {
                        grid.splice(y, 1);
                        grid.unshift(new Array(width).fill(0));
                    })
                }
            }

            createGrid();
            createRandomShape();
            addCurrentStep(left, top);
            drawGrid();

            window.addEventListener("keydown", function () {
                switch (event.keyCode) {
                    case 38: /* 上 */
                        transfromShape();
                        break;
                    case 40: /* 下 */
                        move(0, 1);
                        break;
                    case 37: /* 左 */
                        move(-1, 0);
                        break;
                    case 39: /* 右 */
                        move(1, 0);
                        break;
                }
            })

            let limit = 30;
            let timestamp = 0;
            function starAnimation() {
                timestamp += 1;
                if (timestamp > limit) {
                    timestamp = 0;
                    move(0, 1);
                }
                requestAnimationFrame(starAnimation);
            }
            starAnimation();
        })
    </script>
</body>

</html>
举报

相关推荐

0 条评论