Create Collision Detection with 3D Objects

share link

by Abdul Rawoof A R dot icon Updated: Mar 8, 2023

technology logo
technology logo

Solution Kit Solution Kit  

The process of determining computationally if two or more objects intersect, the time, and the parts of the objects involved in the intersection is known as Collision Detection. 


3D Theory for Collision Detection. To ensure that any space area cannot be occupied by more than one object, we need collision detection depending on the geometry arrays of the objects. The number of tests that must be made and, thus, the CPU resources used is the big issue with collision detection, and this consists of wrapping game entities in a non-rotated, thus axis-aligned box and checking the positions of these boxes in the three-dimensional coordinate space to see if they are overlapping or not. The axis-aligned constraint is there because of performance reasons, and 3D object recognition has several advantages over 2D detection methods, as more accurate information about the environment is obtained for better detection and will see the example, the depth of the images is not considered in the 2D detection, that reduces the detection accuracy. We can detect data collisions by sensing transmissions from the other stations. 


Here is an example of how to create collision detection with 3D objects: 

Fig : Preview of the output that you will get on running this code from your IDE.

Code

In this solution we're using React and Node libraries.

var canvas = document.createElement("canvas");
canvas.width = 500;
canvas.height = 300;
var ctx = canvas.getContext("2d");
document.body.appendChild(canvas);


// block types 
const types = {
    quad : 1,
    split1 : 2, // split from top left to bottom right
    split2 : 3, // split from top right to bottom left
}
/*
// A block object example to define meaning of properties
var blockObject = {
    x : 0, // top left base x pos
    y : 0, // top left base y pos
    z : 0, // top left base z pos
    norm1, // normal of quad or top right or bottom right triangles
    norm2, // normal of quad or top left or bottom left triangles 
    p1 : 0,  // top left
    p2 : 0,  // top right
    p3 : 0,  // bottom right
    p4 : 0,  // bottom left
    type : types.quad,
    pNorm : null, // this is set when a height test is done. It is the normal at the point of the height test
}*/

// compute the surface normal from two vectors on the surface. (cross product of v1,v2)
function getSurfaceNorm(x1,y1,z1,x2,y2,z2){
    // normalise vectors 
    var d1= Math.hypot(x1,y1,z1);
    x1 /= d1;
    y1 /= d1;
    z1 /= d1;
    var d2= Math.hypot(x2,y2,z2);
    x2 /= d2;
    y2 /= d2;
    z2 /= d2;
    var norm = {}
    norm.x = y1 * z2 - z1 * y2;
    norm.y = z1 * x2 - x1 * z2;
    norm.z = x1 * y2 - y1 * x2;
    return norm;
}
// This defines a block with p1-p2 the height of the corners 
// starting top left and clockwise around to p4 bottom left
// If the block is split with 2 slopes then it will be 
// of type.split1 or type.split2. If a single slope then it is a type.quad
// Also calculates the normals
function createBlock(x,y,z,h1,h2,h3,h4,type){
    var norm1,norm2;
    if(type === types.quad){
        norm1 = norm2 = getSurfaceNorm(1, 0, h2 - h1, 0, 1, h4 - h1);
    }else if(type === types.split1){
        norm1 = getSurfaceNorm(1, 0, h2 - h1, 1, 1, h3 - h1);
        norm2 = getSurfaceNorm(0, 1, h2 - h1, 1, 1, h3 - h1);
    }else{
        norm1 = getSurfaceNorm(0, 1, h2-h3, 1, 0, h4 - h3);
        norm2 = getSurfaceNorm(1, 0, h2 - h1, 0, 1, h4 - h1);
    } 
    return {
        p1 : h1,  // top left
        p2 : h2,  // top right
        p3 : h3,  // bottom right
        p4 : h4,  // bottom left
        x,y,z,type,
        norm1,norm2,
    }
}
        
        
// get the height on the block at x,y
// also sets the surface block.pNorm to match the correct normal
function getHeight(block,x,y){
    var b = block; // alias to make codes easier to read.
    if(b.type === types.quad){
        b.pNorm = b.norm1;
        if(b.p1 === b.p2){
            return (b.p3 - b.p1) * (y % 1) + b.p1 + b.z;
        }
        return (b.p2 - b.p1) * (x % 1) + b.p1 + b.z;
    }else if(b.type === types.split1){
        if(x % 1 > y % 1){ // on top right side
            b.pNorm = b.norm1;
            if(b.p1 === b.p2){
                if(b.p1 === b.p3){
                    return b.p1 + b.z;
                }
                return (b.p3 - b.p1) * (y % 1) + b.p1 + b.z;
            }
            if(b.p2 === b.p3){
                return (b.p2 - b.p1) * (x % 1) + b.p1 + b.z;
            }
            return (b.p3 - b.p2) * (y % 1) + (b.p2 - b.p1) * (x % 1) + b.p1 + b.z;
        }
        // on bottom left size
        b.pNorm = b.norm2;
        if(b.p3 === b.p4){
            if(b.p1 === b.p3){
                return b.p1 + b.z;
            }
            return (b.p3 - b.p1) * (y % 1) + b.p1 + b.z;
        }
        if(b.p1 === b.p4){
            return (b.p3 - b.p1) * (x % 1) + b.p1 + b.z;
        }
        var h = (b.p4 - b.p1) * (y % 1);
        var h1 = b.p3 - (b.p4 - b.p1) + h;
        return (h1 - (h + b.p1)) * (x % 1) + (h + b.p1) + b.z;
    }        
    if(1 - (x % 1) < y % 1){ // on bottom right side
        b.pNorm = b.norm1;
        if(b.p3 === b.p4){
            if(b.p3 === b.p2){
                return b.p2 + b.z;
            }
            return (b.p3 - b.p2) * (y % 1) + b.p4 + b.z;
        }
        if(b.p2 === b.p3){
            return (b.p4 - b.p2) * (x % 1) + b.p2 + b.z;
        }
        var h = (b.p3 - b.p2) * (y % 1);
        var h1 = b.p4 - (b.p3 - b.p2) + h;
        return (h + b.p2 - h1) * (x % 1) + h1 + b.z;
    }
    // on top left size
    b.pNorm = b.norm2;    
    if(b.p1 === b.p2){
        if(b.p1 === b.p4){
            return b.p1 + b.z;
        }
        return (b.p4 - b.p1) * (y % 1) + b.p1 + b.z;
    }
    if(b.p1 === b.p4){
        return (b.p2 - b.p1) * (x % 1) + b.p1 + b.z;
    }
    var h = (b.p4 - b.p1) * (y % 1);
    var h1 = b.p2 + h;
    return (h1 - (h + b.p1)) * (x % 1) + (h + b.p1) + b.z;

}
const projection = {
    width : 20,
    depth : 20, // y axis
    height : 8, // z axis
    xSlope : 0.5,
    ySlope : 0.5,
    originX : canvas.width / 2,
    originY : canvas.height / 4,
    toScreen(x,y,z,point = [],pos = 0){
        point[pos] = x * this.width - y * this.depth + this.originX;
        point[pos + 1] = x * this.width * this.xSlope + y * this.depth * this.ySlope -z * this.height + this.originY;
        return point;
    }
}
// working arrays to avoid excessive GC hits
var pointArray = [0,0]
var workArray = [0,0,0,0,0,0,0,0,0,0,0,0,0,0];
function drawBlock(block,col,lWidth,edge){
    var b = block;
    ctx.strokeStyle = col;
    ctx.lineWidth = lWidth;
    ctx.beginPath();
    projection.toScreen(b.x,     b.y,     b.z + b.p1, workArray, 0);
    projection.toScreen(b.x + 1, b.y,     b.z + b.p2, workArray, 2);
    projection.toScreen(b.x + 1, b.y + 1, b.z + b.p3, workArray, 4);
    projection.toScreen(b.x,     b.y + 1, b.z + b.p4, workArray, 6);
    if(b.type === types.quad){
        ctx.moveTo(workArray[0],workArray[1]);
        ctx.lineTo(workArray[2],workArray[3]);
        ctx.lineTo(workArray[4],workArray[5]);
        ctx.lineTo(workArray[6],workArray[7]);
        ctx.closePath();
    }else if(b.type === types.split1){
        ctx.moveTo(workArray[0],workArray[1]);
        ctx.lineTo(workArray[2],workArray[3]);
        ctx.lineTo(workArray[4],workArray[5]);
        ctx.closePath();
        ctx.moveTo(workArray[0],workArray[1]);
        ctx.lineTo(workArray[4],workArray[5]);
        ctx.lineTo(workArray[6],workArray[7]);
        ctx.closePath();
    }else if(b.type === types.split2){
        ctx.moveTo(workArray[0],workArray[1]);
        ctx.lineTo(workArray[2],workArray[3]);
        ctx.lineTo(workArray[6],workArray[7]);
        ctx.closePath();
        ctx.moveTo(workArray[2],workArray[3]);
        ctx.lineTo(workArray[4],workArray[5]);
        ctx.lineTo(workArray[6],workArray[7]);
        ctx.closePath();
    }
    if(edge){
        projection.toScreen(b.x + 1, b.y,     b.z, workArray, 8);
        projection.toScreen(b.x + 1, b.y + 1, b.z, workArray, 10);
        projection.toScreen(b.x,     b.y + 1, b.z, workArray, 12);
        if(edge === 1){ // right edge
            ctx.moveTo(workArray[2],workArray[3]);
            ctx.lineTo(workArray[8],workArray[9]);
            ctx.lineTo(workArray[10],workArray[11]);
            ctx.lineTo(workArray[4],workArray[5]);
        }
        if(edge === 2){ // right edge
            ctx.moveTo(workArray[4],workArray[5]);
            ctx.lineTo(workArray[10],workArray[11]);
            ctx.lineTo(workArray[12],workArray[13]);
            ctx.lineTo(workArray[6],workArray[7]);
        }
        if(edge === 3){ // right edge
            ctx.moveTo(workArray[2],workArray[3]);
            ctx.lineTo(workArray[8],workArray[9]);
            ctx.lineTo(workArray[10],workArray[11]);
            ctx.lineTo(workArray[12],workArray[13]);
            ctx.lineTo(workArray[6],workArray[7]);
            ctx.moveTo(workArray[10],workArray[11]);
            ctx.lineTo(workArray[4],workArray[5]);
        }
        
        
        
    }
    ctx.stroke();
}

function createMap(){
    var base = "0".charCodeAt(0);
    for(var y = 0; y < mapSize.depth; y ++){
        for(var x = 0; x < mapSize.width; x ++){
            var index = y * (mapSize.width + 1) + x;
            var b;
            var p1= map.charCodeAt(index)-base;
            var p2= map.charCodeAt(index+1)-base;
            var p3= map.charCodeAt(index+1+mapSize.width + 1)-base;
            var p4= map.charCodeAt(index+mapSize.width + 1)-base;
            var type;
            if((p1 === p2 && p3 === p4) || (p1 === p4 && p2 === p3)){
                type = types.quad;
            }else if(p1 === p3){
                type = types.split1;
            }else if(p4 === p2){
                type = types.split2;
            }else{
               // throw new RangeError("Map has badly formed block")
               type = types.split2;
            }
            blocks.push(
                b = createBlock(
                    x,y,0,p1,p2,p3,p4,type
                )
            );
        }
    }
}
function drawMap(){
   for(var i = 0; i < blocks.length; i ++){
       var edge = 0;
       if(i % mapSize.width === mapSize.width- 1){
           edge = 1;
       }
       if(Math.floor(i / mapSize.width) === mapSize.width- 1){
           edge |= 2;
       }
       drawBlock(blocks[i],"black",1,edge);
   }
}
function drawBallShadow(ball){
    var i;
    var x,y,ix,iy;
    ctx.globalAlpha = 0.5;
    ctx.fillStyle = "black";
    ctx.beginPath();
    var first = 0;
    for(var i = 0; i < 1; i += 1/8){
        var ang = i * Math.PI * 2;
        x = ball.x + (ball.rad / projection.width ) * Math.cos(ang) * 0.7;
        y = ball.y + (ball.rad / projection.depth ) * Math.sin(ang) * 0.7;
        if(x < mapSize.width && x >= 0 && y < mapSize.depth && y > 0){
            ix = Math.floor(x + mapSize.width) % mapSize.width;
            iy = Math.floor(y + mapSize.depth) % mapSize.depth;
            var block = blocks[ix + iy * mapSize.width];
            var z = getHeight(block,x,y);
            projection.toScreen(x,y,z, pointArray);
            if(first === 0){
                first = 1;
                ctx.moveTo(pointArray[0],pointArray[1]);
            }else{
                ctx.lineTo(pointArray[0],pointArray[1]);
            }
        }
    }
    ctx.fill();
    ctx.globalAlpha = 1;
    
}

function drawBall(ball){
    projection.toScreen(ball.x, ball.y, ball.z, pointArray);
    ctx.fillStyle = ball.col;
    ctx.strokeStyle = "black";
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.arc(pointArray[0],pointArray[1],ball.rad,0,Math.PI * 2);
    ctx.stroke();
    ctx.fill();
    ctx.fillStyle = "white";
    ctx.beginPath();
    ctx.arc(pointArray[0]-ball.rad/2,pointArray[1]-ball.rad/2,ball.rad/4,0,Math.PI * 2);
    ctx.fill();
}
function updateBall(ball){
    // reset ball if out of bounds;
    if(ball.x > mapSize.width || ball.y > mapSize.depth || ball.x < 0 || ball.y < 0){
        ball.x += ball.dx;
        ball.y += ball.dy;
        ball.z += ball.dz;
        ball.dz -= 0.1;
        if(ball.z < -10){
        
            ball.x = Math.random() * 3;
            ball.y = Math.random() * 3;
            ball.dz  = 0;
            // give random speed
            ball.dx = Math.random() * 0.01;
            ball.dy = Math.random() * 0.01;
        }else{
            ball.dist = Math.hypot(ball.x - 20,ball.y - 20, ball.z - 20);        
            return;
        }
        
    }
    // get the block under the ball
    var block = blocks[Math.floor(ball.x) + Math.floor(ball.y) * mapSize.width];
    const lastZ = ball.z;
    // get the height of the black at the balls position
    ball.z = getHeight(block,ball.x,ball.y);
    // use the face normal to add velocity in the direction of the normal
    ball.dx += block.pNorm.x * 0.01;
    ball.dy += block.pNorm.y * 0.01;
    // move the ball up by the amount of its radius
    ball.z += ball.rad / projection.height;
    ball.dz =lastZ - ball.z;
    // draw the shadow and ball
    ball.x += ball.dx;
    ball.y += ball.dy;
    // get distance from camera;
    ball.dist = Math.hypot(ball.x - 20,ball.y - 20, ball.z - 20);
}
function renderBall(ball){
    drawBallShadow(ball);
    drawBall(ball);
}
function copyCanvas(canvas){
    var can = document.createElement("canvas");
    can.width = canvas.width;
    can.height = canvas.height;
    can.ctx = can.getContext("2d");
    can.ctx.drawImage(canvas,0,0);
    return can;    
}
var map = `
    9988888789999
    9887787678999
    9877676567899
    9876765678789
    9876655567789
    8766555456789
    7655554456678
    6654443456789
    6543334566678
    5432345677889
    4321234567789
    4321234567899
    5412345678999
`.replace(/\n| |\t/g,"");
var mapSize = {width : 12, depth : 12}; // one less than map width and depth
var blocks = [];
ctx.clearRect(0,0,canvas.width,canvas.height)
createMap();
drawMap();
var background = copyCanvas(canvas);
var w = canvas.width;
var h = canvas.height;
var cw = w / 2;  // center 
var ch = h / 2;
var globalTime;  // global to this 
var balls = [{
        x : -10,
        y : 0,
        z : 100,
        dx : 0,
        dy : 0,
        dz : 0,
        col : "red",
        rad : 10,
    },{
        x : -10,
        y : 0,
        z : 100,
        dx : 0,
        dy : 0,
        dz : 0,
        col : "Green",
        rad : 10,
    },{
        x : -10,
        y : 0,
        z : 100,
        dx : 0,
        dy : 0,
        dz : 0,
        col : "Blue",
        rad : 10,
    },{
        x : -10,
        y : 0,
        z : 100,
        dx : 0,
        dy : 0,
        dz : 0,
        col : "yellow",
        rad : 10,
    },{
        x : -10,
        y : 0,
        z : 100,
        dx : 0,
        dy : 0,
        dz : 0,
        col : "cyan",
        rad : 10,
    },{
        x : -10,
        y : 0,
        z : 100,
        dx : 0,
        dy : 0,
        dz : 0,
        col : "black",
        rad : 10,
    },{
        x : -10,
        y : 0,
        z : 100,
        dx : 0,
        dy : 0,
        dz : 0,
        col : "white",
        rad : 10,
    },{
        x : -10,
        y : 0,
        z : 100,
        dx : 0,
        dy : 0,
        dz : 0,
        col : "orange",
        rad : 10,
    }
];

// main update function
function update(timer){
    globalTime = timer;
    ctx.setTransform(1,0,0,1,0,0); // reset transform
    ctx.globalAlpha = 1;           // reset alpha
    ctx.clearRect(0,0,w,h);
    ctx.drawImage(background,0,0);
    // get the block under the ball
    for(var i = 0; i < balls.length; i ++){
        updateBall(balls[i]);
    }
    balls.sort((a,b)=>b.dist - a.dist);
    for(var i = 0; i < balls.length; i ++){
        renderBall(balls[i]);
    }
    requestAnimationFrame(update);
}
requestAnimationFrame(update);

Instructions

Follow the steps carefully to get the output easily.

  1. Install the Node.js and React on your IDE(preferable Visual Studio Code).
  2. Create React Application using npx create-react-app foldername.
  3. cd foldername.
  4. Open the folder in IDE.
  5. Copy the code using "copy" button above and paste it in app.js file(remove the earlier code from app.js).
  6. Open the terminal from IDE.
  7. npm start to run the file.


I hope you found this useful.


I found this code snippet by searching for 'create collision detection with 3d object' in kandi. You can try any such use case!

Environment Tested

I tested this solution in the following versions. Be mindful of changes when working with other versions.

  1. The solution is created and tested in Visual Studio Code 1.74.1.
  2. The solution is tested on node v18.12.1 and npm v8.19.2.
  3. React version-18.2.0.


Using this solution, we are able to create collision detection with 3d objects in javascript with simple steps. This process also facilities an easy way to use, hassle-free method to create a hands-on working version of code which would help us to create collision detection with 3d objects in javascript.

Development Libraries

reactby facebook

JavaScript doticonstar image 209050 doticonVersion:v18.2.0doticon
License: Permissive (MIT)

The library for web and native user interfaces

Support
    Quality
      Security
        License
          Reuse

            reactby facebook

            JavaScript doticon star image 209050 doticonVersion:v18.2.0doticon License: Permissive (MIT)

            The library for web and native user interfaces
            Support
              Quality
                Security
                  License
                    Reuse

                      nodeby nodejs

                      JavaScript doticonstar image 95980 doticonVersion:v20.3.0doticon
                      License: Others (Non-SPDX)

                      Node.js JavaScript runtime :sparkles::turtle::rocket::sparkles:

                      Support
                        Quality
                          Security
                            License
                              Reuse

                                nodeby nodejs

                                JavaScript doticon star image 95980 doticonVersion:v20.3.0doticon License: Others (Non-SPDX)

                                Node.js JavaScript runtime :sparkles::turtle::rocket::sparkles:
                                Support
                                  Quality
                                    Security
                                      License
                                        Reuse

                                          You can also search for any dependent libraries on kandi like 'react' and 'node'.

                                          Support

                                          1. For any support on kandi solution kits, please use the chat
                                          2. For further learning resources, visit the Open Weaver Community learning page.


                                          See similar Kits and Libraries