繁体   English   中英

为什么我的对象在我的 p5.js 项目中重叠?

[英]Why are my objects overlapping in my p5.js project?

我正在做一个用球模拟物理的项目。 这是项目的 p5 编辑器的链接

我的问题如下,当我添加很多球(比如 200 个)时,球在堆积,但其中一些最终会塌陷,我不知道为什么。

有人可以解释它为什么这样做以及如何解决这个问题吗?

谢谢。

这是草图的代码。

document.oncontextmenu = function () {
    return false;
}

let isFlushing = false;
let isBallDiameterRandom = false;
let displayInfos = true;
let displayWeight = false;
let clickOnce = false;

let FRAME_RATE = 60;
let SPEED_FLUSH = 3;
let Y_GROUND;
let lastFR;

let balls = [];

function setup() {
    frameRate(FRAME_RATE);
    createCanvas(window.innerWidth, window.innerHeight);

    Y_GROUND = height / 20 * 19;
    lastFR = FRAME_RATE;
}

function draw() {
    background(255);

    if (isFlushing) {
        for (let i = 0; i < SPEED_FLUSH; i++) {
            balls.pop();
        }

        if (balls.length === 0) {
            isFlushing = false;
        }
    }

    balls.forEach(ball => {
        ball.collide();
        ball.move();
        ball.display(displayWeight);
        ball.checkCollisions();
    });

    if (mouseIsPressed) {
        let ballDiameter;

        if (isBallDiameterRandom) {
            ballDiameter = random(15, 101);
        } else {
            ballDiameter = 25;
        }

        if (canAddBall(mouseX, mouseY, ballDiameter)) {
            isFlushing = false;
            let newBall = new Ball(mouseX, mouseY, ballDiameter, balls);

            if (mouseButton === LEFT && !clickOnce) {
                balls.push(newBall);
                clickOnce = true;
            }

            if (mouseButton === RIGHT) {
                balls.push(newBall);
            }
        }
    }

    drawGround();

    if (displayInfos) {
        displayShortcuts();
        displayFrameRate();
        displayBallCount();
    }
}

function mouseReleased() {
    if (mouseButton === LEFT) {
        clickOnce = false;
    }
}

function keyPressed() {
    if (keyCode === 32) {//SPACE
        displayInfos = !displayInfos;
    }

    if (keyCode === 70) {//F
        isFlushing = true;
    }

    if (keyCode === 71) {//G
        isBallDiameterRandom = !isBallDiameterRandom;
    }

    if (keyCode === 72) {//H
        displayWeight = !displayWeight;
    }
}

function canAddBall(x, y, d) {
    let isInScreen =
        y + d / 2 < Y_GROUND &&
        y - d / 2 > 0 &&
        x + d / 2 < width &&
        x - d / 2 > 0;

    let isInAnotherBall = false;

    for (let i = 0; i < balls.length; i++) {
        let d = dist(x, y, balls[i].position.x, balls[i].position.y);

        if (d < balls[i].w) {
            isInAnotherBall = true;
            break;
        }
    }

    return isInScreen && !isInAnotherBall;
}

function drawGround() {
    strokeWeight(0);
    fill('rgba(200,200,200, 0.25)');
    rect(0, height / 10 * 9, width, height / 10);
}

function displayFrameRate() {
    if (frameCount % 30 === 0) {
        lastFR = round(frameRate());
    }

    textSize(50);
    fill(255, 0, 0);

    let lastFRWidth = textWidth(lastFR);
    text(lastFR, width - lastFRWidth - 25, 50);
    textSize(10);
    text('fps', width - 20, 50);
}

function displayBallCount() {
    textSize(50);
    fill(255, 0, 0);
    text(balls.length, 10, 50);
    let twBalls = textWidth(balls.length);
    textSize(10);
    text('balls', 15 + twBalls, 50);
}

function displayShortcuts() {
    let hStart = 30;
    let steps = 15;
    let maxTW = 0;
    let controlTexts = [
        'LEFT CLICK : add 1 ball',
        'RIGHT CLICK : add 1 ball continuously',
        'SPACE : display infos',
        'F : flush balls',
        'G : set random ball diameter (' + isBallDiameterRandom + ')',
        'H : display weight of balls (' + displayWeight + ')'
    ];

    textSize(11);
    fill(0);

    for (let i = 0; i < controlTexts.length; i++) {
        let currentTW = textWidth(controlTexts[i]);

        if (currentTW > maxTW) {
            maxTW = currentTW;
        }
    }

    for (let i = 0; i < controlTexts.length; i++) {
        text(controlTexts[i], width / 2 - maxTW / 2 + 5, hStart);
        hStart += steps;
    }

    fill(200, 200, 200, 100);
    rect(width / 2 - maxTW / 2,
        hStart - (controlTexts.length + 1) * steps,
        maxTW + steps,
        (controlTexts.length + 1) * steps - steps / 2
    );
}

这是 Ball class 的代码。

class Ball {
    constructor(x, y, w, e) {
        this.id = e.length;
        this.w = w;
        this.e = e;

        this.progressiveWidth = 0;
        this.rgb = [
            floor(random(0, 256)),
            floor(random(0, 256)),
            floor(random(0, 256))
        ];
        this.mass = w;
        this.position = createVector(x + random(-1, 1), y);
        this.velocity = createVector(0, 0);
        this.acceleration = createVector(0, 0);

        this.gravity = 0.2;
        this.friction = 0.5;
    }

    collide() {
        for (let i = this.id + 1; i < this.e.length; i++) {
            let dx = this.e[i].position.x - this.position.x;
            let dy = this.e[i].position.y - this.position.y;
            let distance = sqrt(dx * dx + dy * dy);
            let minDist = this.e[i].w / 2 + this.w / 2;

            if (distance < minDist) {
                let angle = atan2(dy, dx);
                let targetX = this.position.x + cos(angle) * minDist;
                let targetY = this.position.y + sin(angle) * minDist;

                this.acceleration.set(
                    targetX - this.e[i].position.x,
                    targetY - this.e[i].position.y
                );
                this.velocity.sub(this.acceleration);
                this.e[i].velocity.add(this.acceleration);

                //TODO : Effets bizarre quand on empile les boules (chevauchement)

                this.velocity.mult(this.friction);
            }
        }
    }

    move() {
        this.velocity.add(createVector(0, this.gravity));
        this.position.add(this.velocity);
    }

    display(displayMass) {
        if (this.progressiveWidth < this.w) {
            this.progressiveWidth += this.w / 10;
        }

        stroke(0);
        strokeWeight(2);
        fill(this.rgb[0], this.rgb[1], this.rgb[2], 100);
        ellipse(this.position.x, this.position.y, this.progressiveWidth);

        if (displayMass) {
            strokeWeight(1);
            textSize(10);
            let tempTW = textWidth(int(this.w));
            text(int(this.w), this.position.x - tempTW / 2, this.position.y + 4);
        }
    }

    checkCollisions() {
        if (this.position.x > width - this.w / 2) {
            this.velocity.x *= -this.friction;
            this.position.x = width - this.w / 2;
        } else if (this.position.x < this.w / 2) {
            this.velocity.x *= -this.friction;
            this.position.x = this.w / 2;
        }

        if (this.position.y > Y_GROUND - this.w / 2) {
            this.velocity.x -= this.velocity.x / 100;
            this.velocity.y *= -this.friction;
            this.position.y = Y_GROUND - this.w / 2;
        } else if (this.position.y < this.w / 2) {
            this.velocity.y *= -this.friction;
            this.position.y = this.w / 2;
        }
    }
}

当球的质量总和大于球的弹性时,我看到这种重叠发生。 至少看起来是这样。 我用较小的池制作了一个副本,因此重现问题不需要太多时间。

在下面的示例中,有 6 个球(质量为 150 个单位)压在基排上,我们看到基排中的 13 个球重叠。 基行的宽度约为。 300 像素,仅够容纳 12 个直径为 25 的球。我认为这显示了 model 的局限性:球显示为圆形,但确实有一定的弹性,它们应该显示变形。 很难说如何在不实现绘制复杂形状的情况下解决此问题。 也许摩擦更少?

已经有 19 个球在基排产生重叠

顺便说一句:你在那里建造的伟大的物理引擎:-)

与此同时,我可以用更少的球制作另一个截图。 其中三个(eq. 75 个单位)的重量足以在基行中产生重叠。

15个球足以重现问题

我将球的大小加倍并更改了水池的尺寸,以发现引擎中存在更严重的错误。 我看到球在压力下受到如此沉重的压力,以至于它们的“体积”(面积)没有足够的空间。 要么他们必须内爆,要么它的弹性反作用力必须对整个场景产生更大的影响。 如果你仔细观察底部空间最小的球的钟摆运动,你会发现它们非常猛烈,但显然没有机会到达外面。

球没有足够的空间容纳它们的大小

难道是你的评价令

balls.forEach(ball => {
    ball.collide();
    ball.move();
    ball.display(displayWeight);
    ball.checkCollisions();
});

无法以现实的方式传播碰撞

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM