如何阻止玩家移出Canvas且避免角色出现卡顿?
问题:Canvas角色触碰到边界后卡住无法移动
我有一个运行正常的特殊移动函数,尝试实现阻止玩家移出Canvas的功能。通过检测玩家的x、y坐标是否在Canvas范围内来实现,核心代码如下:
const canvas = document.createElement("canvas"); // ... canvas.width = window.innerWidth; canvas.height = window.innerHeight; // ... move() { this.angle += this.rotv; this.rotv *= this.drag; this.vx += this.ax; this.vy += this.ay; this.ax *= this.drag; this.ay *= this.drag; this.vx *= this.drag; this.vy *= this.drag; if (this.x < canvas.width - this.width + 3.75 && this.x > this.width + 3.75) { this.x += Math.cos(this.angle) * this.vx; } if (this.y < canvas.height - this.height + 3.75 && this.y > this.height + 3.75) { this.y += Math.sin(this.angle) * this.vy; } },
但当角色处于Canvas边界时会卡住无法移动,仅可旋转。请问哪里出错了?该如何修复?
错误原因
当前的边界检测逻辑是仅当角色完全处于安全区域内时才允许移动,一旦角色触碰到边界(比如this.x等于或超出canvas.width - this.width + 3.75),后续所有移动操作都会被禁止——哪怕你想往Canvas内部移动也不行,这就导致角色卡在边界无法动弹。
正确的逻辑应该是:允许角色先执行移动操作,若移动后超出边界,再将位置修正到边界上,而非直接禁止移动。
修复方案
修改move函数,调整执行顺序:先完成移动计算,再对x、y坐标进行边界约束,确保角色始终处于Canvas范围内:
move() { this.angle += this.rotv; this.rotv *= this.drag; this.vx += this.ax; this.vy += this.ay; this.ax *= this.drag; this.ay *= this.drag; this.vx *= this.drag; this.vy *= this.drag; // 先执行移动操作 this.x += Math.cos(this.angle) * this.vx; this.y += Math.sin(this.angle) * this.vy; // 定义边界范围 const minX = this.width + 3.75; const maxX = canvas.width - this.width + 3.75; const minY = this.height + 3.75; const maxY = canvas.height - this.height + 3.75; // 约束x坐标在合法范围内 this.x = Math.max(minX, Math.min(this.x, maxX)); // 约束y坐标在合法范围内 this.y = Math.max(minY, Math.min(this.y, maxY)); },
完整修复后的代码
const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); document.body.appendChild(canvas); canvas.width = window.innerWidth; canvas.height = window.innerHeight; var projectileArray = [] var keydown = false const kbd = { ArrowLeft: false, ArrowUp: false, ArrowRight: false, ArrowDown: false, }; const noHoldDown = { Space: false, } function Projectile(x,y,speed,direction,duration) { Object.assign(this, {x,y,speed,direction,duration}); this.draw = ctx => { ctx.arc(this.x, this.y, 3.75, 0, 2 * Math.PI); ctx.fillStyle = 'white'; ctx.fill(); } this.update = ctx => { ctx.beginPath(); this.x += Math.cos(this.direction) * this.speed; this.y += Math.sin(this.direction) * this.speed; this.draw(ctx); this.duration--; } this.isDone = () => this.duration <= 0; } const ship = { angle: 0, color: "white", x: canvas.width / 2, y: canvas.height / 2, width: 10, height: 12, drag: 0.9, accSpeed: 0.025, rotSpeed: 0.007, rotv: 0, ax: 0, ay: 0, vx: 0, vy: 0, rotateLeft() { this.rotv -= this.rotSpeed; }, rotateRight() { this.rotv += this.rotSpeed; }, accelerate() { this.ax += this.accSpeed; this.ay += this.accSpeed; }, decelerate() { this.ax -= this.accSpeed; this.ay -= this.accSpeed; }, shoot() { let mySpeed = Math.sqrt(this.vx * this.vx + this.vy * this.vy) let bullet = new Projectile(this.x, this.y, 3 + mySpeed, this.angle, 500) projectileArray.push(bullet); }, move() { this.angle += this.rotv; this.rotv *= this.drag; this.vx += this.ax; this.vy += this.ay; this.ax *= this.drag; this.ay *= this.drag; this.vx *= this.drag; this.vy *= this.drag; // 先执行移动 this.x += Math.cos(this.angle) * this.vx; this.y += Math.sin(this.angle) * this.vy; // 边界约束 const minX = this.width + 3.75; const maxX = canvas.width - this.width + 3.75; const minY = this.height + 3.75; const maxY = canvas.height - this.height + 3.75; this.x = Math.max(minX, Math.min(this.x, maxX)); this.y = Math.max(minY, Math.min(this.y, maxY)); }, draw(ctx) { ctx.save(); ctx.lineWidth = 3; ctx.translate(this.x, this.y); ctx.rotate(this.angle); ctx.beginPath(); ctx.moveTo(this.height, 0); ctx.lineTo(-this.height, this.width); ctx.lineTo(-this.height, -this.width); ctx.closePath(); ctx.strokeStyle = this.color; ctx.stroke(); ctx.restore(); } }; document.addEventListener("keydown", event => { if (event.code in kbd) { event.preventDefault(); kbd[event.code] = true; } }); document.addEventListener("keydown", event => { if (event.code in noHoldDown) { if (!keydown) { keydown = true; ship.shoot(); } } }); document.addEventListener('keyup', event => { if (event.code in noHoldDown) { keydown = false; } }); document.addEventListener("keyup", event => { if (event.code in kbd) { event.preventDefault(); kbd[event.code] = false; } }); (function update() { ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); const shipActions = { ArrowLeft: "rotateLeft", ArrowUp: "accelerate", ArrowDown: "decelerate", ArrowRight: "rotateRight", }; for (const key in shipActions) { if (kbd[key]) { ship[shipActions[key]](); } } ship.move(); ship.draw(ctx); for (var i = 0; i < projectileArray.length; i++) { let bullet = projectileArray[i]; bullet.update(ctx) } projectileArray = projectileArray.filter(bullet => !bullet.isDone()); requestAnimationFrame(update); })();
内容的提问来源于stack exchange,提问作者gamer merch
相关产品推荐
相关产品推荐

