Canvas JS游戏实现角色触地跳跃 禁止空中无限连跳
Canvas角色无限连跳问题修复方案
问题原因
代码里虽然提前声明了isGrounded落地状态变量,但全程没有给这个变量赋过值,W键的按下逻辑也没加任何拦截,只要按键触发就直接给角色设置向上的初速度,自然会出现空中无限连跳的问题。
修复逻辑
- 在所有落地判定节点(触碰画布底部、触碰平台顶部)正确更新
isGrounded状态为true,上升、下落的非落地状态统一设为false - 跳跃触发(按W键)前增加校验:仅当
isGrounded为true时才允许执行跳跃逻辑,跳跃触发后立刻将isGrounded设为false,直到下次落地才重置状态
具体修改点
- 修改
Player类的update方法,碰到底部地面时同步更新落地状态:
update() { this.draw(); this.position.x += this.velocity.x; this.position.y += this.velocity.y; if (this.position.y + this.height + this.velocity.y >= canvas.height) { this.velocity.y = 0 // 站在画布底部时标记为落地 isGrounded = true } else { this.velocity.y += gravity; // 离开底部时先标记为非落地,后续结合平台碰撞结果更新 isGrounded = false } }
- 修改平台碰撞检测逻辑,站在平台上时标记为落地:
// 平台碰撞检测 platforms.forEach(platform => { if (player.position.y + player.height <= platform.position.y && player.position.y + player.height + player.velocity.y >= platform.position.y && player.position.x + player.width >= platform.position.x && player.position.x <= platform.position.x + platform.width) { player.velocity.y = 0 // 站在平台上时标记为落地 isGrounded = true } })
- 修改W键按下的事件逻辑,增加落地校验:
case 87: keys.up.pressed = true; // 仅落地状态允许触发跳跃 if (isGrounded) { player.velocity.y = -10 // 跳跃后立刻标记为非落地,防止同一次停留期间重复触发 isGrounded = false } break;
修复后完整可运行代码
<canvas></canvas> <script> const canvas = document.querySelector("canvas"); const c = canvas.getContext('2d') canvas.width = window.innerWidth; canvas.height = window.innerHeight; const gravity = 0.5; let isGrounded; class Player { constructor() { this.position = { x: 100, y: 100 } this.width = 100 this.height = 100 this.velocity = { x: 0, y: 1 } } draw() { c.fillStyle = 'black' c.fillRect(this.position.x, this.position.y, this.width, this.height) } update() { this.draw(); this.position.x += this.velocity.x; this.position.y += this.velocity.y; if (this.position.y + this.height + this.velocity.y >= canvas.height) { this.velocity.y = 0 isGrounded = true } else { this.velocity.y += gravity; isGrounded = false } } } class Platform { constructor({x, y}) { this.position = {x, y} this.width = 400 this.height = 20 } draw() { c.fillStyle = 'black' c.fillRect(this.position.x, this.position.y, this.width, this.height) } } const player = new Player() const platforms = [ new Platform({x: 150, y: 800}), new Platform({x: 700, y: 900}), ]; const keys = { right: {pressed: false}, left: {pressed: false}, up: {pressed: false} } let scrollOffset = 0 function animate() { requestAnimationFrame(animate) c.clearRect(0, 0, canvas.width, canvas.height) player.update(); platforms.forEach(platform => platform.draw()) if (keys.right.pressed && player.position.x < 400) { player.velocity.x = 5 } else if (keys.left.pressed && player.position.x > 100) { player.velocity.x = -5 } else { player.velocity.x = 0 if (keys.right.pressed) { scrollOffset += 5; platforms.forEach(platform => platform.position.x -= 5); } else if (keys.left.pressed) { scrollOffset -= 5 platforms.forEach(platform => platform.position.x += 5); } } // 平台碰撞检测 platforms.forEach(platform => { if (player.position.y + player.height <= platform.position.y && player.position.y + player.height + player.velocity.y >= platform.position.y && player.position.x + player.width >= platform.position.x && player.position.x <= platform.position.x + platform.width) { player.velocity.y = 0 isGrounded = true } }) if (scrollOffset > 300) { console.log('u win') } } animate() window.addEventListener('keydown', ({keyCode}) => { switch (keyCode) { case 65: keys.left.pressed = true; break; case 83: break; case 68: keys.right.pressed = true; break; case 87: keys.up.pressed = true; if (isGrounded) { player.velocity.y = -10 isGrounded = false } break; } }) window.addEventListener("keyup", ({keyCode}) => { switch (keyCode) { case 65: keys.left.pressed = false; break; case 83: break; case 68: keys.right.pressed = false; break; case 87: keys.up.pressed = false; break; } }); </script>
后续如果要扩展二段跳、土狼时间等跳跃机制,只需要基于
isGrounded状态增加跳跃计数、落地时间窗口逻辑即可,不需要重构现有碰撞判断代码。
内容的提问来源于stack exchange,提问作者GeorgeAnghel114
相关产品推荐
相关产品推荐

