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

<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;
  }

}

问题分析

  1. 未隔离画布状态:直接旋转画布会影响后续所有绘图操作,且未恢复状态,导致画布持续变换出现缩放异常。
  2. 旋转原点错误:未以玩家中心为旋转点,旋转后角色位置偏移。
  3. 变量未定义:rotate、angle、jumps、gravityAcceleration未声明,导致逻辑失效。
  4. 逻辑重复: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保持不变


关键说明

  1. 画布状态管理:save()和restore()确保旋转只影响当前玩家绘制,不会导致整个画布持续变换。
  2. 中心旋转:通过两次平移将旋转原点移到玩家中心,避免旋转后角色偏移。
  3. 平滑旋转动画:使用currentAngle和targetAngle实现渐进式旋转,更贴合Geometry Dash的视觉效果。
  4. 统一逻辑:将跳跃和旋转逻辑封装在Player类中,避免全局变量混乱。

内容的提问来源于stack exchange,提问作者Калоян Янев

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 06:00:12