简体   繁体   中英

p5.js object collision and objects entangling

I wrote some code in p5.js to see if i can properly make a collision detection system but when i put more than 2 squares in, squares seem to bump each other inside of other squares. I'd like to know if there's anyway to stop this plus, if you have any good pointers on how to do tidy/shorten my code id like to hear them.

My code:

var r; //later defined as an array for the squares
var num; //number of squares
function setup(){
    r = [];
    num = 10;
    createCanvas(windowWidth,windowHeight- 4);
    for(var i = 0;i < num; i++){

        r[i] = new Box(random(width-40),random(height-40),40,40);

    }

}

function draw(){
    background(40);
    for(var i = 0;i < num; i++)    {
        r[i].show();
        for(var j = 0;j<num; j++){

            //this is the if statement evaluating if the left and right of the square is touching each other. i is one square and j is the other. you see in each if statement i have the acceleration being added, this is because if it wasn't then they would be true if the squares were touching each other on any side
            if(r[i].right+r[i].xa >= r[j].left && r[i].bottom >= r[j].top && r[i].top <= r[j].bottom && r[i].left + r[i].xa <= r[j].right){
                r[i].xa *= -1;
                r[j].xa *= -1;

            }
            //this is also just as confusing just read through it carefully
            if(r[i].bottom + r[i].ya >= r[j].top && r[i].right >=r[j].left && r[i].left <= r[j].right && r[i].top + r[i].ya <= r[j].bottom){
                r[i].ya *= -1;
                r[j].ya *= -1;
            }
        }    
    }


}
function Box(x, y, wid, hei){

    this.x = x;//input for square shape
    this.y = y;//ditto
    this.width = wid;//ditto
    this.height= hei;//ditto
    this.xa = random(2,5);//xa is the x acceleration
    this.ya = random(2,5);//ya is the y acceleration
    this.left;
    this.right;
    this.top;
    this.bottom;
    this.show = function(){
        this.left = this.x;     //i define left,right,top,bottom in show function so they get updated
        this.right = this.x +this.width;
        this.top = this.y;
        this.bottom = this.y +this.height;
        push();
        fill(255);
        noStroke();
        rect(this.x,this.y,this.width,this.height);
        pop();//push pop just in case i want to change square colors individually in the future
        this.x += this.xa;//adding acceleration to the squares
        this.y += this.ya;//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        if(this.x > width-this.width||this.x <0){//bouncing off the right and left wall
            this.xa *= -1;
            if(this.x > width/2){// making sure if the square spawns or glitches on the other side of the wall it doesn't get stuck, this checks which side the square is on when it touches the wall then moves it directly on the wall
                this.x = width-this.width;
            }else{
                this.x = 0;
            }
        }
        if(this.y > height-this.height||this.y <0){// same as above but for the y axis
            this.ya *= -1;
            if(this.y > height/2){
                this.y = height-this.height;
            }else{
                this.y = 0;
            }

        }

    }
}
function windowResized(){
    createCanvas(windowWidth,windowHeight- 4);//window resizing adjustment
}

you can view it using this . just copy and paste.

The solution to the unsolvable

Sorry no such thing

Collision solutions are not easy when you have many moving objects in the scene.

Your immediate problem

Your problem if mainly because you are making an assumption on the box's direction of travel when they collide. You multiply the direction by -1 to reverse direction.

All good for 2 objects, but add a 3rd and you will end up with the 3 coming together. Each in turn you change the direction, box1 hits box2 both move away from each other, then in the same frame box1 hits box3 and now box1 and box3 are moving apart.Your speeds are constant so after a three way collision there will always be 2 boxes traveling in the same direction but overlapping.

The overlapping boxes on the next frame detect the overlap and both reverse direction, as they are already traveling in the same direction the direction switch does not help them move apart.

A step forward

Well a step apart, The following modification to the code just ensures that when possible a collision results in the box move away from each other.

function draw() {
    background(40);
    for (var i = 0; i < num; i++) {
        const bx1 = r[i];
        r[i].show();
        for (var j = 0; j < num; j++) {
            if (j !== i) {
                // t for top, b for bottom, r for right and l for left. 1 for first box 2 for second
                // bx for box
                const bx2 = r[j];
                const t1 = bx1.top + bx1.ya;
                const b1 = bx1.bottom + bx1.ya;
                const l1 = bx1.left + bx1.xa;
                const r1 = bx1.right + bx1.xa;
                const t2 = bx2.top + bx2.ya;
                const b2 = bx2.bottom + bx2.ya;
                const l2 = bx2.left + bx2.xa;
                const r2 = bx2.right + bx2.xa;
                // the or's mean that the condition will complete at the first passed clause
                // If not (not over lapping)  AKA is overlapping
                if (!(t1 > b2 || b1 < t2 || l1 > r2 || r1 < l2)) {
                    if (r1 >= l2) {
                        bx1.xa = -Math.abs(bx1.xa);
                        bx2.xa = Math.abs(bx2.xa);
                    }
                    if (l1 <= r2) {
                        bx1.xa = Math.abs(bx1.xa);
                        bx2.xa = -Math.abs(bx2.xa);
                    }

                    if (b1 >= t2) {
                        bx1.ya = -Math.abs(bx1.ya);
                        bx2.ya = Math.abs(bx2.ya);
                    }
                    if (t1 <= b2) {
                        bx1.ya = Math.abs(bx1.ya);
                        bx2.ya = -Math.abs(bx2.ya);
                    }
                }
            }
        }
    }
}

But that only moves the problem away from overlapping, now there are many collision that are wrong as there is no test to determine the point of collision

In the above code you are trying to solve from an unsolvable position. Boxes in real life never overlap. Boxes in real life will slow down and speed up. perfectly flat sides will never collide with more than on side at a time.

To do this you will need to use integration. Its not that hard and is just a process of dividing time into smaller steps. Collide, move, check for overlap, move apart then back to collide.

Verlet integration

Also verlet integration will make it easier. Rather than store a boxes speed as a vector you store the current position and the previous position.

box.x = 10;
box.y = 10;
box.ox = 8;  // the boxes old position
box.oy = 8;

You move a box as follows

sx = box.x - box.ox;
sy = box.y - box.oy;
box.ox = box.x;
box.oy = box.y;
box.x += sx;  // the boxes old position
box.y += sy;

When you hit something you need to change the old position so as to give the next iteration the correct direction

if(box.y > ground){
   box.y = ground - (box.y - ground); // move away from ground same dist as moved into ground
   box.oy = box.y -sy;
}

Do them all in groups. Move all at once, then test for collision at once. Dont move and test one at a time.

Verlet integration is much more forgiving as it lets speed of movement absorb some of the error. Rather than be all in position as the standard vector method does.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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