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

寻求p5.js中DOM矩形对象真实物理碰撞模拟的优化方案

寻求DOM矩形对象物理碰撞模拟的优化方案

我正在构建一个物理模拟系统,让DOM对象具备重力感应、拖拽抛掷功能,还能和其他对象产生真实的碰撞反应。

这类系统的核心难点是DOM矩形元素的碰撞检测与真实碰撞响应处理——矩形的几何特性(中心到边界距离不恒定)导致它的碰撞逻辑远比圆形复杂。

我已经在p5.js里实现了圆形对象的碰撞模拟,但换成链接这类DOM矩形元素后,处理变得非常繁琐。目前已经完成了用DOM链接替换圆形的功能,但碰撞逻辑存在诸多问题,现有代码如下:

{ "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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 14:40:08