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

问题描述 投票:0回答:1

我正在做一个用球模拟物理的项目。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
    );
}

这里是球类的代码。

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;
        }
    }
}
javascript physics p5.js
1个回答
2
投票

当球的质量总和大于球的弹性时,我看到了这种重叠的情况。至少看起来是这样。I 复制 用一个较小的池子,这样就不需要那么多时间来重现这个问题。

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

already 19 balls produce overlapping in the base row

另外:你建立的物理引擎很不错 :-)

同时我还能用更少的球做另一张截图。其中三个球的重量(相当于75个单位)足以在基本行中产生重叠。

15 balls are enough to reproduce the issue

我把球的大小增加了一倍,并改变了球池的尺寸,以检测出引擎中存在一个更严重的错误。我看到球在压力下被压得很重,以至于它们的 "体积"(面积)没有足够的空间。要么他们有内爆或它的弹性反力必须有更大的影响的整个场景。如果你仔细观察底部空间最小的球所做的摆动,你会发现它们的摆动非常剧烈,但显然没有机会到达外面。

the balls have not enough space for their size

难道是你的评价顺序

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

不能够 传播对撞 以现实的方式?

© www.soinside.com 2019 - 2024. All rights reserved.