寻求p5.js中DOM矩形对象真实物理碰撞模拟的优化方案
寻求DOM矩形对象物理碰撞模拟的优化方案
我正在构建一个物理模拟系统,让DOM对象具备重力感应、拖拽抛掷功能,还能和其他对象产生真实的碰撞反应。
这类系统的核心难点是DOM矩形元素的碰撞检测与真实碰撞响应处理——矩形的几何特性(中心到边界距离不恒定)导致它的碰撞逻辑远比圆形复杂。
我已经在p5.js里实现了圆形对象的碰撞模拟,但换成链接这类DOM矩形元素后,处理变得非常繁琐。目前已经完成了用DOM链接替换圆形的功能,但碰撞逻辑存在诸多问题,现有代码如下:
menu.json 配置
{ "links": [ { "label": "who are you?", "section_link": "who" }, { "label": "what do you do?", "section_link": "what" }, { "label": "how do you do that?", "section_link": "how?" }, { "label": "why?", "section_link": "why" }, { "label": "where?", "section_link": "where" } ] }
p5.js 核心代码
let numBalls = 13; let spring = 0.05; let gravity = 0.05; let friction = -0.9; let balls = []; let menuData; function preload() { menuData = loadJSON('menu.json'); } function setup() { createCanvas(500, 500); for (let i = 0; i < menuData.links.length; i++) { balls[i] = new Ball( random(width), random(height), random(30, 40), i, balls ); } noStroke(); fill(255, 204); } function draw() { background(0); balls.forEach(ball => { ball.collide(); ball.move(); ball.display(); }); } function mousePressed() { balls.forEach(ball => { if ( ball.onBall(mouseX, mouseY)) ball.startDrag(); } ); } function mouseDragged() { balls.forEach(ball => { if (ball.dragging) { ball.x = mouseX; ball.y = mouseY; } }); } function mouseReleased() { balls.forEach(ball => { if (ball.dragging) { // 根据鼠标释放时的速度计算球体新速度 ball.vx = (mouseX - ball.mousex) / 10; ball.vy = (mouseY - ball.mousey) / 10; // 结束拖拽状态 ball.dragging = false; } }); } class Ball { constructor(xin, yin, din, idin, oin) { this.x = xin; this.y = yin; this.vx = 0; this.vy = 0; this.diameter = din; this.id = idin; this.others = oin; this.dragging = false; this.anchor = createA(menuData.links[this.id].href, menuData.links[this.id].label); } onBall(x, y) { // 获取锚点元素的边界矩形 let rect = this.anchor.elt.getBoundingClientRect(); // 检测点是否在矩形范围内 return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom; } startDrag() { this.dragging = true; this.mousex = mouseX; this.mousey = mouseY; } collide() { for (let i = this.id + 1; i < menuData.links.length; i++) { // 计算矩形间的距离和最小碰撞距离 let dx = this.others[i].anchor.elt.getBoundingClientRect().x - this.anchor.elt.getBoundingClientRect().x; let dy = this.others[i].anchor.elt.getBoundingClientRect().y - this.anchor.elt.getBoundingClientRect().y; let distance = sqrt(dx * dx + dy * dy); let minDist = (this.others[i].anchor.elt.getBoundingClientRect().width + this.anchor.elt.getBoundingClientRect().width) / 2; // 检测是否碰撞 if (distance < minDist) { // 计算碰撞角度 let angle = atan2(dy, dx); // 计算避免重叠的目标位置 let targetX = this.anchor.elt.getBoundingClientRect().x + cos(angle) * minDist; let targetY = this.anchor.elt.getBoundingClientRect().y + sin(angle) * minDist; // 计算碰撞力 let ax = (targetX - this.others[i].anchor.elt.getBoundingClientRect().x) * spring; let ay = (targetY - this.others[i].anchor.elt.getBoundingClientRect().y) * spring; this.vx -= ax; this.vy -= ay; this.others[i].vx += ax; this.others[i].vy += ay; } } } move() { if (this.dragging) { this.x = mouseX; this.y = mouseY; } else { // 重力作用 this.vy += gravity; // 根据速度更新位置 this.x += this.vx; this.y += this.vy; // 边界碰撞处理 if (this.x + this.diameter / 2 > width) { this.x = width - this.diameter / 2; this.vx *= friction; } else if (this.x - this.diameter / 2 < 0) { this.x = this.diameter / 2; this.vx *= friction; } if (this.y + this.diameter / 2 > height) { this.y = height - this.diameter / 2; this.vy *= friction; } else if (this.y - this.diameter / 2 < 0) { this.y = this.diameter / 2; this.vy *= friction; } } } display() { this.anchor.style('position: relative'); this.anchor.position(this.x, this.y); this.anchor.style('z-index: 3'); this.anchor.parent('maradona'); this.anchor.style('border-style: solid'); this.anchor.style('border-color: red'); } }
希望能得到更优的矩形碰撞处理方案。
内容的提问来源于stack exchange,提问作者giampo
相关产品推荐
相关产品推荐

