【前端】打砖块游戏:实现细节介绍

打砖块游戏:实现细节介绍

在本文中,我将详细介绍如何使用HTML、CSS和JavaScript技术构建一个简单的打砖块游戏。我们将重点讨论游戏的三个核心技术方面:碰撞检测、画图和事件监听。
完整代码我放在:github可以直接拉取代码测试。
请添加图片描述

游戏概览

打砖块游戏中,玩家通过控制底部的板来弹跳一个球,目标是摧毁所有显示在画布顶部的砖块。游戏结束的条件是球触到底部边界。

1. 画图功能

画图是游戏中可视化元素的基础,包括绘制球、板和砖块。我们使用Canvas API来实现这一功能。

球的绘制:

function drawBall() {
    ctx.beginPath();
    ctx.arc(x, y, ballRadius, 0, Math.PI*2);
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

这段代码创建了一个圆形,它代表游戏中的球。ctx.arc方法用于在画布上画一个完整的圆。

板的绘制:

function drawPaddle() {
    ctx.beginPath();
    ctx.rect(paddleX, canvas.height-paddleHeight, paddleWidth, paddleHeight);
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

此函数绘制一个矩形,代表玩家控制的板。

砖块的绘制:

function drawBricks() {
    for(let c = 0; c < brickColumnCount; c++) {
        for(let r = 0; r < brickRowCount; r++) {
            if(bricks[c][r].status == 1) {
                let brickX = (c*(brickWidth+brickPadding))+brickOffsetLeft;
                let brickY = (r*(brickHeight+brickPadding))+brickOffsetTop;
                ctx.beginPath();
                ctx.rect(brickX, brickY, brickWidth, brickHeight);
                ctx.fillStyle = "#0095DD";
                ctx.fill();
                ctx.closePath();
            }
        }
    }
}

在这里,我们通过循环遍历所有砖块,并只绘制那些状态为1(未被击中)的砖块。

2. 碰撞检测

碰撞检测是游戏互动性的核心,用于判断球是否撞击了砖块或其他游戏元素。

function collisionDetection() {
    for(let c = 0; c < brickColumnCount; c++) {
        for(let r = 0; r < brickRowCount; r++) {
            let b = bricks[c][r];
            if(b.status == 1) {
                if(x > b.x && x < b.x + brickWidth && y > b.y && y < b.y + brickHeight) {
                    dy = -dy;
                    b.status = 0;
                }
            }
        }
    }
}

这段代码检查每个砖块,如果球的位置与砖块的位置重叠,并且该砖块尚未被击中(status = 1),则反转球的垂直移动方向并改变砖块状态。

3. 事件监听

为了让玩家能够控制板的移动,我们需要监听键盘事件。

function keyDownHandler(e) {
    if(e.key == "Right" || e.key == "ArrowRight") {
        rightPressed = true;
    } else if(e.key == "Left" || e.key == "ArrowLeft") {
        leftPressed = true;
    }
}

function keyUpHandler(e) {
    if(e.key == "Right" || e.key == "ArrowRight") {
        rightPressed = false;
    } else if(e.key == "Left" || e.key == "ArrowLeft") {
        leftPressed = false;
    }
}

document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);

这些代码设置了键盘事件监听器,当玩家按下或释放左右箭头键时,会相应地更新控制板的状态。

游戏逻辑与动画

游戏的主循环通过 draw 函数实现,该函数不仅负责调用之前描述的绘图函数,还负责更新游戏状态和重新绘制画面,以创建平滑的动画效果。

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);  // 清除整个画布
    drawBricks();
    drawBall();
    drawPaddle();
    collisionDetection();

    // 处理球碰到画布边缘的情况
    if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) {
        dx = -dx;
    }
    if(y + dy < ballRadius) {
        dy = -dy;
    } else if(y + dy > canvas.height-ballRadius) {
        if(x > paddleX && x < paddleX + paddleWidth) {
            dy = -dy;
        } else {
            document.location.reload();  // 球触底时重载页面,游戏重新开始
        }
    }

    // 更新板的位置
    if(rightPressed && paddleX < canvas.width-paddleWidth) {
        paddleX += 7;
    } else if(leftPressed && paddleX > 0) {
        paddleX -= 7;
    }

    x += dx;  // 更新球的位置
    y += dy;

    requestAnimationFrame(draw);  // 请求下一帧动画
}

draw 函数首先清空整个画布,以便新的画面可以绘制。接着,它调用之前定义的 drawBricks, drawBall, drawPaddle, 和 collisionDetection 函数来绘制游戏元素并处理碰撞逻辑。此外,它检查球是否触及画布边缘,并适当地调整球的移动方向。如果球触底,则游戏会重新加载。

通过 requestAnimationFrame(draw) 调用,浏览器会在适当的时间再次调用 draw 函数,这样可以创建出连续的动画效果,同时也确保游戏的动画与浏览器的刷新率同步,为玩家提供平滑且响应迅速的游戏体验。

完整代码

一下是完整的代码实现,拷贝后可以直接使用
index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>打砖块游戏</title>
    <style>
        canvas {
            display: block;
            margin: 0 auto;
            background: #eee;
            border: 2px solid #333;
        }
    </style>
</head>
<body>
    <canvas id="gameCanvas" width="480" height="320"></canvas>
    <script src="game.js"></script>
</body>
</html>

game.js

const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

let ballRadius = 10;
let x = canvas.width/2;
let y = canvas.height-30;
let dx = 2;
let dy = -2;
let paddleHeight = 10;
let paddleWidth = 75;
let paddleX = (canvas.width-paddleWidth) / 2;
let rightPressed = false;
let leftPressed = false;
let brickRowCount = 5;
let brickColumnCount = 3;
let brickWidth = 75;
let brickHeight = 20;
let brickPadding = 10;
let brickOffsetTop = 30;
let brickOffsetLeft = 30;
let bricks = [];
for(let c=0; c<brickColumnCount; c++) {
    bricks[c] = [];
    for(let r=0; r<brickRowCount; r++) {
        bricks[c][r] = { x: 0, y: 0, status: 1 };
    }
}

function drawBall() {
    ctx.beginPath();
    ctx.arc(x, y, ballRadius, 0, Math.PI*2);
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

function drawPaddle() {
    ctx.beginPath();
    ctx.rect(paddleX, canvas.height-paddleHeight, paddleWidth, paddleHeight);
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

function drawBricks() {
    for(let c=0; c<brickColumnCount; c++) {
        for(let r=0; r<brickRowCount; r++) {
            if(bricks[c][r].status == 1) {
                let brickX = (c*(brickWidth+brickPadding))+brickOffsetLeft;
                let brickY = (r*(brickHeight+brickPadding))+brickOffsetTop;
                bricks[c][r].x = brickX;
                bricks[c][r].y = brickY;
                ctx.beginPath();
                ctx.rect(brickX, brickY, brickWidth, brickHeight);
                ctx.fillStyle = "#0095DD";
                ctx.fill();
                ctx.closePath();
            }
        }
    }
}

function collisionDetection() {
    for(let c=0; c<brickColumnCount; c++) {
        for(let r=0; r<brickRowCount; r++) {
            let b = bricks[c][r];
            if(b.status == 1) {
                if(x > b.x && x < b.x+brickWidth && y > b.y && y < b.y+brickHeight) {
                    dy = -dy;
                    b.status = 0;
                }
            }
        }
    }
}

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    drawBricks();
    drawBall();
    drawPaddle();
    collisionDetection();

    if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) {
        dx = -dx;
    }
    if(y + dy < ballRadius) {
        dy = -dy;
    } else if(y + dy > canvas.height-ballRadius) {
        if(x > paddleX && x < paddleX + paddleWidth) {
            dy = -dy;
        }
        else {
            document.location.reload();
        }
    }

    if(rightPressed && paddleX < canvas.width-paddleWidth) {
        paddleX += 7;
    }
    else if(leftPressed && paddleX > 0) {
        paddleX -= 7;
    }

    x += dx;
    y += dy;
    requestAnimationFrame(draw);
}

document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);

function keyDownHandler(e) {
    if(e.key == "Right" || e.key == "ArrowRight") {
        rightPressed = true;
    }
    else if(e.key == "Left" || e.key == "ArrowLeft") {
        leftPressed = true;
    }
}

function keyUpHandler(e) {
    if(e.key == "Right" || e.key == "ArrowRight") {
        rightPressed = false;
    }
    else if(e.key == "Left" || e.key == "ArrowLeft") {
        leftPressed = false;
    }
}

draw();
总结

通过上述的实现细节,我们展示了如何使用前端技术创建一个基本的打砖块游戏。通过简单的HTML结构、CSS样式以及JavaScript中的绘图、碰撞检测和事件监听技术,开发者可以构建出具有基本互动性的网页游戏。希望这个示例能为你在前端游戏开发领域的学习和探索提供帮助和启发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值