<canvas>中实现矩形180度旋转:Geometry Dash游戏跳跃开发问题
Geometry Dash跳跃时矩形180度旋转异常修复
我正在开发一款Geometry Dash风格的游戏,需要实现角色(持续绘制的矩形)跳跃时完成180度旋转的效果。但之前尝试的所有方法都出现异常,甚至导致画布缩小至原尺寸的1/5。求正确的实现方式。
原始代码
HTML文件
<!DOCTYPE html> <html> <link rel="stylesheet" href="style.css"> <head></head> <body> <canvas></canvas> </body> <script src="js/classes/Player.js"></script> <script src="js/classes/events.js"></script> <script src="index.js"></script> </html>
CSS文件
body { background: black; } canvas { background-color: white; }
JavaScript代码
Player类(Player.js)
class Player { constructor() { this.position = { x: 280, y: 100, } this.velocity = { x: 0, y: 0, } this.width = 50 this.height = 50 this.sides = { bottom: this.position.y + this.height, left: this.position.x, right: this.position.x + this.width, top: this.position.y, } this.gravity = 1 } draw() { c.fillStyle = 'red' c.fillRect(this.position.x, this.position.y, this.width, this.height) } update() { this.position.x += this.velocity.x this.position.y += this.velocity.y this.sides.bottom = this.position.y + this.height if (this.sides.bottom + this.velocity.y < canvas.height - this.height) { this.velocity.y += 1 } else this.velocity.y = 0 } }
入口文件(index.js)
const canvas = document.querySelector('canvas') const c = canvas.getContext('2d') canvas.width = 1024 canvas.height = 576 const background = { position: { x: 0, y: 0, } } const player = new Player() const keys = { w: { pressed: false, }, } function animate() { window.requestAnimationFrame(animate) c.clearRect(background.position.x, background.position.y, canvas.width, canvas.height) player.velocity.x = 0 player.draw() player.update() } animate()
事件文件(events.js)
window.addEventListener('keydown', (event) => { switch (event.key) { case 'w': if (player.velocity.y === 0 || jumps > 0) { player.velocity.y = -17.5; rotate = true; angle += Math.PI; // 180 degrees } } }); function update() { player.velocity.y += gravityAcceleration; player.position.x += player.velocity.x; player.position.y += player.velocity.y; if (player.position.y >= canvas.height - player.height) { player.position.y = canvas.height - player.height; player.velocity.y = 0; } }
问题分析
- 未隔离画布状态:直接旋转画布会影响后续所有绘图操作,且未恢复状态,导致画布持续变换出现缩放异常。
- 旋转原点错误:未以玩家中心为旋转点,旋转后角色位置偏移。
- 变量未定义:
rotate、angle、jumps、gravityAcceleration未声明,导致逻辑失效。 - 逻辑重复:
update函数在两个地方存在,重力和碰撞处理不一致。
解决方案
核心修改点
- 在
Player类中添加旋转相关属性,控制旋转动画。 - 使用
ctx.save()和ctx.restore()隔离绘图状态,避免影响全局。 - 以玩家中心为旋转原点,计算旋转后的绘制位置。
- 统一重力和碰撞逻辑,修复未定义变量问题。
修改后的完整代码
Player.js
class Player { constructor() { this.position = { x: 280, y: 100 } this.velocity = { x: 0, y: 0 } this.width = 50 this.height = 50 this.gravity = 1 // 旋转相关属性 this.currentAngle = 0 this.targetAngle = 0 this.rotationSpeed = 0.3 // 旋转速度,可调整 this.isRotating = false } draw(ctx) { ctx.save() // 保存当前画布状态 // 计算玩家中心坐标 const centerX = this.position.x + this.width / 2 const centerY = this.position.y + this.height / 2 // 平移到玩家中心,旋转,再平移回去 ctx.translate(centerX, centerY) ctx.rotate(this.currentAngle) ctx.translate(-centerX, -centerY) // 绘制矩形 ctx.fillStyle = 'red' ctx.fillRect(this.position.x, this.position.y, this.width, this.height) ctx.restore() // 恢复画布状态,不影响其他绘图 } update() { // 处理旋转动画 if (this.isRotating) { if (Math.abs(this.currentAngle - this.targetAngle) < 0.01) { this.currentAngle = this.targetAngle this.isRotating = false } else { // 平滑旋转到目标角度 const direction = this.targetAngle > this.currentAngle ? 1 : -1 this.currentAngle += direction * this.rotationSpeed } } // 重力与移动逻辑 this.position.y += this.velocity.y this.position.x += this.velocity.x // 地面碰撞检测 if (this.position.y + this.height >= canvas.height) { this.position.y = canvas.height - this.height this.velocity.y = 0 } else { this.velocity.y += this.gravity } } // 触发跳跃旋转 jumpAndRotate() { if (this.velocity.y === 0) { // 仅在地面时跳跃 this.velocity.y = -17.5 // 设置目标角度:当前角度+180度(Math.PI) this.targetAngle = this.currentAngle + Math.PI this.isRotating = true } } }
index.js
const canvas = document.querySelector('canvas') const ctx = canvas.getContext('2d') canvas.width = 1024 canvas.height = 576 const player = new Player() function animate() { window.requestAnimationFrame(animate) // 清空整个画布 ctx.clearRect(0, 0, canvas.width, canvas.height) player.update() player.draw(ctx) } animate()
events.js
window.addEventListener('keydown', (event) => { if (event.key === 'w') { player.jumpAndRotate() } })
HTML与CSS保持不变
关键说明
- 画布状态管理:
save()和restore()确保旋转只影响当前玩家绘制,不会导致整个画布持续变换。 - 中心旋转:通过两次平移将旋转原点移到玩家中心,避免旋转后角色偏移。
- 平滑旋转动画:使用
currentAngle和targetAngle实现渐进式旋转,更贴合Geometry Dash的视觉效果。 - 统一逻辑:将跳跃和旋转逻辑封装在Player类中,避免全局变量混乱。
内容的提问来源于stack exchange,提问作者Калоян Янев
相关产品推荐
相关产品推荐

