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

Canvas恐龙跳跃异常:按下跳跃键后立即落回地面的JS问题排查

问题分析与修复方案

你的角色跳起来立刻落地,核心问题出在碰撞检测逻辑错误和重力参数设置不当,还有几个细节遗漏,下面逐个解决:

1. 修正碰撞检测逻辑

当前colisionDetection函数的条件完全写反了:你判断「角色底部还没到地面」就强制拉回地面,导致角色刚起跳就被重置。正确逻辑应该是当角色底部碰到/超过地面时,才触发落地处理:

function colisionDetection(){
    // 角色底部接触或超出地面时,触发落地逻辑
    if(dino.y + dino.h >= board.height){
        gravity = 0;
        velocityY = 0;
        dino.onGround = true;
        // 固定角色在地面位置,避免超出画布
        dino.y = board.height - dino.h;
    } else {
        // 角色在空中时,恢复重力
        gravity = 1;
    }
}

2. 调整重力与跳跃参数

  • 原gravity = -1方向错误:画布y轴向下递增,重力应该让角色y值增加(向下),所以设为正的小数值(比如1),避免下落过快。
  • 原jumpY = 100数值过大:会导致角色瞬间位移,改成15左右,跳跃更自然。

3. 初始化frames变量

frames未初始化会导致动画逻辑出错,在代码开头补充:

let frames = 0;

4. 补充缺失的函数

代码里调用了drawBase、drawPipes和placePipes但未定义,会触发控制台报错,补充基础实现(可根据需求调整样式):

function drawBase(){
    context.fillStyle = '#888';
    context.fillRect(base.x, base.y, base.w, base.h);
    // 地面滚动逻辑(可选)
    if(state.current == state.game){
        base.x -= base.dx;
        base.x = base.x <= -base.w/2 ? 0 : base.x;
    }
}

function drawPipes(){
    context.fillStyle = '#2ecc71';
    pipeArray.forEach(pipe => {
        context.fillRect(pipe.x, pipe.y, pipe.width, pipe.height);
        if(state.current == state.game) pipe.x += velocityX;
    });
}

function placePipes(){
    if(state.current != state.game) return;
    pipeArray.push({
        x: boardWidth,
        y: boardHeight - pipeHeight - 30, // 和地面保持间距
        width: pipeWidth,
        height: pipeHeight
    });
    // 移除超出画布的管道,避免内存占用
    if(pipeArray.length > 5) pipeArray.shift();
}

5. 简化跳跃逻辑

原跳跃逻辑里直接修改dino.y是多余的,update函数会自动更新位置,只需要设置velocityY即可:

document.addEventListener('keydown', (evt) => {
    let keyPressed = evt.keyCode;
    if(keyPressed == 32 && state.current == state.game && dino.onGround == true){
        console.log('space key pressed');
        velocityY = -jumpY; // 负数值实现向上跳
        dino.onGround = false;
        gravity = 1; // 起跳时立刻恢复重力
    }
});

完整修正后的代码

let board;
let context;
let boardWidth = 600;
let boardHeight = 300;
let frames = 0; // 初始化frames变量

let dinoX = 30;
let dinoY = boardHeight - 60;
let dinoWidth = 50;
let dinoHeight = 50;

let pipeX = 900;
let pipeY = boardHeight - 50;
let pipeWidth = 40;
let pipeHeight = 30;
let pipeArray = [];

let velocityX = -1;
let jumpY = 15; // 调整跳跃力度
let pressedSpace = 0;
let gravity = 1; // 修正重力方向
let velocityY = 0;
let intervalId;

let base = {
    x : 0,
    y : boardHeight - 30,
    w : 1400,
    h : 20,
    dx : 1
}
let dino = {
    animation : [
        // 临时用纯色矩形替代图片,可替换为实际图片资源
        {draw: (ctx, x, y, w, h) => {ctx.fillStyle='#3498db'; ctx.fillRect(x,y,w,h)}},
        {draw: (ctx, x, y, w, h) => {ctx.fillStyle='#e74c3c'; ctx.fillRect(x,y,w,h)}},
        {draw: (ctx, x, y, w, h) => {ctx.fillStyle='#f1c40f'; ctx.fillRect(x,y,w,h)}},
        {draw: (ctx, x, y, w, h) => {ctx.fillStyle='#3498db'; ctx.fillRect(x,y,w,h)}}
    ],
    x : dinoX,
    y : dinoY,
    w : dinoWidth,
    h : dinoHeight,
    onGround : true,
    frame : 0
}
let state = {
    current : 0,
    getReady : 0,
    game : 1,
    over : 2
}
window.onload = function() {
    board = document.querySelector('#canvas');
    boardWidth = board.width;
    boardHeight = board.height;
    context = board.getContext('2d');

    setInterval(placePipes, 1500);
    requestAnimationFrame(update);
};

document.addEventListener('click', (evt) => {
    switch(state.current){
        case state.getReady:
            state.current = state.game;
            break;
        case state.game:
            break;
        case state.over:
            state.current = state.getReady;
            break;
    }
})

// update function
function update(){
    requestAnimationFrame(update);
    context.clearRect(0, 0, board.width, board.height);

    drawBase();
    drawDino();
    drawPipes();
    frames++;

    if(state.current == state.game){
        velocityY += gravity;
        dino.y = Math.max(dino.y + velocityY, 0);
        colisionDetection();
    }
};

// jump button
document.addEventListener('keydown', (evt) => {
    let keyPressed = evt.keyCode;
    if(keyPressed == 32 && state.current == state.game && dino.onGround == true){
        console.log('space key pressed');
        velocityY = -jumpY;
        dino.onGround = false;
        gravity = 1;
    }
});

// colision detector
function colisionDetection(){
    if(dino.y + dino.h >= board.height){
        gravity = 0;
        velocityY = 0;
        dino.onGround = true;
        dino.y = board.height - dino.h;
    } else {
        gravity = 1;
    }
};

function drawDino(){
    let dinoA = dino.animation[dino.frame];
    dinoA.draw(context, dino.x, dino.y, dino.w, dino.h);

    if(state.current == state.game){
        dino.period = 10; // 调整动画帧率,避免切换过快
        dino.frame += frames % dino.period == 0 ? 1 : 0;
        dino.frame = dino.frame % dino.animation.length;
    }
};

// 补充缺失的drawBase函数
function drawBase(){
    context.fillStyle = '#888';
    context.fillRect(base.x, base.y, base.w, base.h);
    if(state.current == state.game){
        base.x -= base.dx;
        base.x = base.x <= -base.w/2 ? 0 : base.x;
    }
}

// 补充缺失的drawPipes函数
function drawPipes(){
    context.fillStyle = '#2ecc71';
    pipeArray.forEach(pipe => {
        context.fillRect(pipe.x, pipe.y, pipe.width, pipe.height);
        if(state.current == state.game) pipe.x += velocityX;
    });
}

// 补充缺失的placePipes函数
function placePipes(){
    if(state.current != state.game) return;
    pipeArray.push({
        x: boardWidth,
        y: boardHeight - pipeHeight - 30,
        width: pipeWidth,
        height: pipeHeight
    });
    if(pipeArray.length > 5) pipeArray.shift();
}

注:如果你的startRunImg等图片变量未定义,需要先加载图片资源,或者用代码里的矩形占位方式临时替代。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 18:20:54