p5.js中旋转平移图形的碰撞检测及坐标变换问题咨询
Duet游戏复刻:平移旋转后的碰撞检测解决方案
一、平移/旋转对图形坐标的具体影响
p5.js的translate()和rotate()是修改绘图上下文的坐标系,而非直接改变图形的原始坐标值:
translate(width/2, height/2)会把绘图原点从屏幕左上角(0,0)移到屏幕中心,之后你绘制的所有图形,代码里写的坐标都是相对于这个新原点的。rotate(angle)会让整个坐标系绕当前原点(也就是屏幕中心)旋转指定角度,此时图形在代码里的相对坐标会跟着坐标系旋转,但它们在**屏幕绝对坐标系(左上角为原点)**中的实际位置需要通过数学计算推导。
以你代码中的红蓝球为例,假设它们相对于中心的初始偏移是(0, -d)和(0, d)(d是球心到中心的距离),旋转角度为angle,那么每个球的屏幕绝对坐标计算公式为:
const centerX = width / 2; const centerY = height / 2; const d = 60; // 假设你的球到中心的距离是60 // 蓝球绝对坐标 blueAbsX = centerX + (0 * cos(angle) - (-d) * sin(angle)); blueAbsY = centerY + (0 * sin(angle) + (-d) * cos(angle)); // 红球绝对坐标 redAbsX = centerX + (0 * cos(angle) - d * sin(angle)); redAbsY = centerY + (0 * sin(angle) + d * cos(angle));
本质是用旋转矩阵将相对坐标转换为绝对坐标,通用公式为:
绝对X = 中心X + (相对X * cos(angle) - 相对Y * sin(angle)) 绝对Y = 中心Y + (相对X * sin(angle) + 相对Y * cos(angle))
二、球体与障碍物的碰撞检测实现
核心思路是:先算出红蓝球的屏幕绝对坐标,再用这个坐标和障碍物(白色矩形)做碰撞判断。
1. 矩形与圆形的碰撞检测逻辑
如果你的障碍物是矩形(比如垂直长条),可以用以下步骤检测:
- 对于每个障碍物矩形,找到矩形边界上离球心最近的点
(closestX, closestY):// rect是障碍物对象,包含x, y, width, height const closestX = constrain(ballAbsX, rect.x, rect.x + rect.width); const closestY = constrain(ballAbsY, rect.y, rect.y + rect.height); - 计算球心到这个最近点的距离,若距离小于等于球的半径,则判定为碰撞:
const distance = dist(ballAbsX, ballAbsY, closestX, closestY); if (distance <= ballRadius) { // 触发碰撞逻辑,比如游戏结束 gameOver = true; }
2. 整合到你的代码中
- 首先在
draw()函数里,每次旋转后,先计算两个球的绝对坐标,不要直接用绘图时的相对坐标。 - 遍历所有障碍物,对每个球执行上述碰撞检测逻辑。
简化的代码示例:
let angle = 0; const ballRadius = 20; const d = 60; const obstacles = [ {x: 100, y: 0, width: 20, height: height} // 示例障碍物 ]; let gameOver = false; function draw() { background(0); translate(width/2, height/2); rotate(angle); // 计算红蓝球绝对坐标 const centerX = width/2; const centerY = height/2; const blueX = centerX + (0 * cos(angle) - (-d) * sin(angle)); const blueY = centerY + (0 * sin(angle) + (-d) * cos(angle)); const redX = centerX + (0 * cos(angle) - d * sin(angle)); const redY = centerY + (0 * sin(angle) + d * cos(angle)); // 绘制球 fill(0,0,255); ellipse(0, -d, ballRadius*2, ballRadius*2); fill(255,0,0); ellipse(0, d, ballRadius*2, ballRadius*2); // 绘制障碍物(需转成相对坐标系) fill(255); for (let rect of obstacles) { rect(rect.x - centerX, rect.y - centerY, rect.width, rect.height); } // 碰撞检测 if (!gameOver) { for (let rect of obstacles) { // 检测蓝球 let closestX = constrain(blueX, rect.x, rect.x + rect.width); let closestY = constrain(blueY, rect.y, rect.y + rect.height); if (dist(blueX, blueY, closestX, closestY) <= ballRadius) { gameOver = true; } // 检测红球 closestX = constrain(redX, rect.x, rect.x + rect.width); closestY = constrain(redY, rect.y, rect.y + rect.height); if (dist(redX, redY, closestX, closestY) <= ballRadius) { gameOver = true; } } } angle += 0.01; }
内容的提问来源于stack exchange,提问作者Grant Miller
相关产品推荐
相关产品推荐

