简体   繁体   中英

Detecting object collision with spatial partitioning in JavaScript

I'm trying to create a system of many objects that preform an action when they collide with each other, I'm using the P5.min.js library.

I've set up an array for the grid and an array for the objects, but I can't figure out the right way to go through each grid cell and check only the objects inside that cell before moving on to the next cell.

Here's what I've got so far

let molecules = [];
const numOfMolecules = 100;
let collisions = 0;
let check = 0;
let maxR = 10; //max molecule radius
let minR = 2; //min molecule radius
let numOfCol = 5;
let numOfRow = 5;
let CellW = 600/numOfCol; //gridWidth
let CellH = 600/numOfRow; //gridHeight
let remain = numOfMolecules;
let gridArray = [];



function setup() {
    createCanvas(600, 600);
    background(127);

    for (let i = 0; i < numOfMolecules; i++) {
        molecules.push(new Molecule());
    }
}

function draw() {
    background(127);

    molecules.forEach(molecule => {
        molecule.render();
        molecule.checkEdges();
        molecule.step();
    });

    drawGrid();
    splitIntoGrid();
    collision();
    displayFR();
}

function drawGrid() {
    for (i = 0; i < numOfRow+1; i++){
        for (j = 0; j < numOfCol+1; j++){
            noFill();
            stroke(0);
            rect(CellW*(j-1), CellH*(i-1), CellW, CellH);
        }
    }
}

function splitIntoGrid(){
    for (let i = 0; i < numOfRow; i++){
        for (let j = 0; j < numOfCol; j++){
            tempArray = [];
            molecules.forEach(molecule => {
                if (molecule.position.x > (CellW*j) &&
                    molecule.position.x < (CellW*(j+1)) &&
                    molecule.position.y > (CellH*i) &&
                    molecule.position.y < (CellH*(i+1))) {
                        tempArray.push(molecule.id);
                    }
            });
        }
    }
}

How I'm checking collision between all objects:

function collision() {
    for (let i=0; i < molecules.length; i++){
        for (let j=0; j < molecules.length; j++){
            let diff = p5.Vector.sub(molecules[j].position, molecules[i].position);
            check++;
            if (i != j && diff.mag() <= molecules[j].radius + molecules[i].radius){
                collisions++;
                molecules[j].changeColor();
            }
        }
    }
}

As far as I can see, I need to put these for loops inside another one going through each cell in the grid, but I don't know how to limit the search to which ever tempArray(s) the object is in

If this makes any sense, this is what I'm trying to do

function collision() {
  for (let k = 0; k < gridArray.length; k++){
    for (let i=0; i < gridArray.tempArray.length; i++){
        for (let j=0; j < gridArray.tempArray.length; j++){
            let diff = p5.Vector.sub(gridArray.tempArray[j].position, gridArray.tempArray.position);
            check++;
            if (i != j && diff.mag() <= gridArray.tempArray[j].radius + gridArray.tempArray[i].radius){
                collisions++;
                gridArray.tempArray[j].changeColor();
                gridArray.tempArray[i].changeColor();
            }
        }
    }
  }
}

The grid cell is represented by an array of array gridArray . You need to have a collection of molecules for each grid cell. My recommendation would be to use Set s instead of an Array since the order is irrelevant. The idea is to be able to access the set of molecules on a given grid cell (i,j) with the syntax:

gridArray[i][j]

The following code will create an array of numOfRow arrays:

const numOfRow = 5;

const gridArray = (new Array(numOfRow)).fill([]);

gridArray with look like this:

[ [], [], [], [], [] ]

Inside splitIntoGrid you are checking which molecules are in which grid cells. This is good. However, for each grid cell, you are overwriting the global variable tempArray . Therefore, at the end of the function's execution, tempArray will only hold the molecules of the last grid cell, which isn't what you want. For a given grid cell, we will add the right molecules to a Set associated with this grid cell.

The Set data structure has an #add method which appends a new element to the set:

function splitIntoGrid() {
    for (let i = 0; i < numOfRow; i++) {
        for (let j = 0; j < numOfCol; j++) {
            gridArray[i][j] = new Set();
            molecules.forEach(molecule => {
                if (molecule.position.x > (CellW*j) 
                    && molecule.position.x < (CellW*(j+1))
                    && molecule.position.y > (CellH*i) 
                    && molecule.position.y < (CellH*(i+1))) {
                        gridArray[i][j].add(molecule);
                    }
            });
        }
    }
}

Now you're ready to check for collisions on each grid cells. We will have a total of four loops inside one another. Two to navigate through the grid and two to compare the molecules that are contained inside each grid cell:

function collision() {
  for (let i = 0; i < numOfRow; i++) {
    for (let j = 0; j < numOfCol; j++) {
      gridArray[i][j].forEach(moleculeA => {
        gridArray[i][j].forEach(moleculeB => {
          const diff = p5.Vector.sub(moleculeA.position, moleculeB.position);
          if (moleculeA != moleculeB && diff.mag() <= moleculeA.radius + moleculeB.radius) {
            collisions++;
            moleculeA.changeColor();
            moleculeB.changeColor();
          }
        });
      });
    }
  }
}

In the above code, #forEach comes in handy.

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