You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现点击按钮时在Canvas生成随机大小的水平移动黑色方块?

Block Avoider 游戏实现方案

嘿,我来帮你梳理下这款Block Avoider游戏的实现思路和可运行的代码示例吧!咱们一步步拆解需求,把功能落地:

一、基础结构搭建

首先需要一个Canvas用来渲染游戏元素,再加个按钮触发方块生成。HTML结构很简单:

<!DOCTYPE html>
<html>
<head>
    <title>Block Avoider</title>
    <style>
        canvas { border: 1px solid #333; }
        button { margin: 10px 0; padding: 8px 16px; font-size: 16px; }
    </style>
</head>
<body>
    <button id="spawnBtn">生成黑色方块</button>
    <canvas id="gameCanvas" width="800" height="600"></canvas>
</body>
</html>

二、核心JS逻辑实现

接下来是JavaScript部分,咱们分模块处理:

1. 初始化变量与画布

先获取画布上下文,定义玩家、敌方方块的基础属性:

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

// 玩家蓝色方块属性
const player = {
    x: 50,
    y: canvas.height / 2 - 40,
    width: 40,
    height: 80,
    color: 'blue',
    speed: 5,
    upPressed: false,
    downPressed: false,
    leftPressed: false,
    rightPressed: false
};

// 存储所有黑色方块的数组
const enemyBlocks = [];

2. 玩家移动控制

监听方向键的按下/松开事件,记录状态,然后在循环里更新玩家位置(还要限制不能移出画布边界):

// 监听按键事件
document.addEventListener('keydown', (e) => {
    switch(e.key) {
        case 'ArrowUp': player.upPressed = true; break;
        case 'ArrowDown': player.downPressed = true; break;
        case 'ArrowLeft': player.leftPressed = true; break;
        case 'ArrowRight': player.rightPressed = true; break;
    }
});

document.addEventListener('keyup', (e) => {
    switch(e.key) {
        case 'ArrowUp': player.upPressed = false; break;
        case 'ArrowDown': player.downPressed = false; break;
        case 'ArrowLeft': player.leftPressed = false; break;
        case 'ArrowRight': player.rightPressed = false; break;
    }
});

// 更新玩家位置的函数
function updatePlayer() {
    if (player.upPressed && player.y > 0) {
        player.y -= player.speed;
    }
    if (player.downPressed && player.y + player.height < canvas.height) {
        player.y += player.speed;
    }
    if (player.leftPressed && player.x > 0) {
        player.x -= player.speed;
    }
    if (player.rightPressed && player.x + player.width < canvas.width) {
        player.x += player.speed;
    }
}

3. 生成随机黑色方块

给按钮绑定点击事件,每次生成一个随机大小、随机y轴位置的黑色方块,初始在画布右侧外:

spawnBtn.addEventListener('click', () => {
    const blockWidth = Math.random() * 60 + 20; // 20-80px宽
    const blockHeight = Math.random() * 60 + 20; // 20-80px高
    enemyBlocks.push({
        x: canvas.width, // 初始在画布右侧
        y: Math.random() * (canvas.height - blockHeight), // 随机y位置,不超出画布
        width: blockWidth,
        height: blockHeight,
        color: 'black',
        speed: Math.random() * 2 + 1 // 1-3px的移动速度,增加随机性
    });
});

4. 更新与渲染敌方方块

在游戏循环里让方块向左移动,并且移除已经移出左侧的方块(避免内存浪费):

function updateEnemies() {
    for (let i = enemyBlocks.length - 1; i >= 0; i--) {
        const block = enemyBlocks[i];
        block.x -= block.speed;

        // 移除超出画布左侧的方块
        if (block.x + block.width < 0) {
            enemyBlocks.splice(i, 1);
        }
    }
}

5. 核心碰撞检测

用**轴对齐 bounding box(AABB)**的方法检测玩家与敌方方块的碰撞,这是2D游戏里最常用的碰撞检测方式:

function checkCollision() {
    for (const block of enemyBlocks) {
        if (
            player.x < block.x + block.width &&
            player.x + player.width > block.x &&
            player.y < block.y + block.height &&
            player.y + player.height > block.y
        ) {
            // 碰撞触发逻辑,这里示例是弹出提示并重置游戏
            alert('碰撞啦!游戏结束!');
            resetGame();
        }
    }
}

// 重置游戏的函数
function resetGame() {
    // 重置玩家位置
    player.x = 50;
    player.y = canvas.height / 2 - 40;
    // 清空所有敌方方块
    enemyBlocks.length = 0;
}

6. 游戏主循环

用requestAnimationFrame实现持续的更新与渲染:

function gameLoop() {
    // 清空画布
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 更新所有元素状态
    updatePlayer();
    updateEnemies();
    checkCollision();

    // 渲染玩家方块
    ctx.fillStyle = player.color;
    ctx.fillRect(player.x, player.y, player.width, player.height);

    // 渲染所有敌方方块
    for (const block of enemyBlocks) {
        ctx.fillStyle = block.color;
        ctx.fillRect(block.x, block.y, block.width, block.height);
    }

    // 循环调用
    requestAnimationFrame(gameLoop);
}

// 启动游戏
gameLoop();

额外优化建议

  • 可以给敌方方块添加不同的移动速度,增加游戏难度梯度
  • 加入分数系统,比如每躲避一个方块加1分,碰撞后显示最终得分
  • 可以设置自动生成方块的定时器,不用手动点击按钮,提升游戏流畅度
  • 添加游戏开始界面和结束界面,替换简单的alert提示,提升整体体验

内容的提问来源于stack exchange,提问作者powelc91

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:46:15