#StackBounty: #2d #collision-detection #javascript #collision-avoidance #collision-prediction Wall sliding in JavaScript; works, but ge…

Bounty: 50

this is a follow up to this other question: How do I handle player collision with corners of a wall

In inspiration of the code given in its answer, I tried to write some new code.

Basically, in the original, the wall sliding works very well on the inside of the walls, but I wanted to make it work on the outside as well, so I made a new basic code engine, based on his technique:

var aD =[]
var r
function start() {
    r = new CanvasRenderer(can),
        my = new scene();
    window.my = my
    eventHandler();
    my.add(new mesh({
        verts: [
            0,   0,
            100, 15,
            115, 60,
            50, 100,
            20, 75,2,8
        ],
        position: {
            x: 100,
            y:100
        },
        scale: {

            x:4,y:5
        },
        color:"orange",
        onupdate(me) {
        //  me.position.x++
        }
    }));
    var g = false
    my.add(new mesh({
        primitive:"rect",
        name: "player",
        scale: {
            x: 50,
            y:50
        },
        position: {
            x: 311,
            y:75
        },
        origin: {
            x:0.5,
            y:0.5
        },
        onupdate(me) {
            var upKey = keys[38],
                downKey = keys[40],
                rightKey = keys[39],
                leftKey = keys[37],
                drx  = 0,
                dx = 0,
                speed = 5,
                turningSpeed = 3
            
            drx = leftKey ? -1 : rightKey ? 1 : 0
            forward = upKey ? 1 : downKey ? -1 : 0

            me.rotation.x += (
                (drx * Math.PI / 180 * turningSpeed )
            )
            me.rotation.y = 1;

            var xDir = Math.cos(me.rotation.x)
            var yDir = Math.sin(me.rotation.x)
            
            me.position.x += xDir  * forward * speed
            me.position.y += yDir * forward * speed

            for(var i = 0; i < my.objects.length; i++) {
                let cur = my.objects[i];
                if(cur.name !== me.name) {
                    cur.lineSegments.forEach(l => {
                        var col = checkCollision(
                            me.position.x,
                            me.position.y,
                            me.scale.x/2,
                            l
                        )
                        
                        if(col) {
        
                            me.position.y=col.y
                            me.position.x = col.x
                        }
                     });
                }
            }


        
            
        }

    }));
    
    let i = setInterval(() => render(r, my), 16);
    r.on("resize", () => render(r, my));

}

function checkCollision(x1, y1, rad,l) {
        var dist = distance2(
                            l.start[0],
                            l.start[1],
                            
                            l.end[0],
                            l.end[1]
                        ),
                            vec1 = [
                                x1 - l.start[0],
                                y1 - l.start[1]
                            ],

                            vec2 = [
                                l.end[0] - l.start[0],
                                l.end[1] - l.start[1]
                            ],

                            percentOfWall = (
                                Math.max(
                                    0,
                                    Math.min(
                                        1, 
                                        dot(
                                            vec1[0],
                                            vec1[1],

                                            vec2[0],
                                            vec2[1]
                                        ) / dist
                                    )
                                )
                            ),
                            projection = [
                                l.start[0] + percentOfWall * vec2[0],
                                l.start[1] + percentOfWall * vec2[1],
                            ],
                            acDist = Math.sqrt(distance2(
                                x1, y1,
                                projection[0], projection[1]
                            ))
aD.push( () => {
                        r.ctx.beginPath()
                        r.ctx.fillStyle="green"
                        r.ctx.arc(projection[0], projection[1], 5, 0, Math.PI*2);
                        r.ctx.fill()
                        r.ctx.closePath();
                        })

                    
                    if(acDist < rad) {
                        var mag = Math.sqrt(dist),
                            delt = [
                            l.end[0] - l.start[0],
                            l.end[1] - l.start[1]
                        ],
                            normal = [
                            delt[0] / mag,
                            delt[1] / mag
                        ]
                        
                        return {
                        
                            x: projection[0] + 

                            rad * (normal[1] ),
                        
                             y:projection[1] + 
                            rad* (-normal[0] ),
                            projection,
                            normal
                        }
                    }

                    
}


function dot(x1, y1, x2, y2) {
    return (
        x1 * x2 + y1 * y2
    )
}

function distance2(x1, y1, x2, y2) {
    let dx = (x1 - x2), dy = (y1 - y2);
    return (
        dx * dx + dy * dy
    );
}

function render(r,s) {
//r.ctx.clearRect(0,0,r.ctx.canvas.width,r.ctx.canvas.height)
    s.update();
    r.render(s)
    aD.forEach(x=>x());
    aD = []
}

onload = start;

function eventHandler() {
    window.keys = {};
    addEventListener("keyup" , e=> {
        keys[e.keyCode] = false;
            
    });

    addEventListener("keydown" , e=> {
        keys[e.keyCode] = true;
    });
}

function CanvasRenderer(dom) {
    if(!dom) dom = document.createElement("canvas");
    
    var events = {}, self = this;
    function rsz() {
        dom.width = dom.clientWidth;
        dom.height = dom.clientHeight;
        self.dispatchEvent("resize");
    }
    
    window.addEventListener("resize", rsz); 

    let ctx = dom.getContext("2d");

    function render(scene) {
        ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
        for(let i = 0; i < scene.objects.length; i++) {
            let o = scene.objects[i],
                verts = o.realVerts;

            
            ctx.beginPath();
            ctx.moveTo(
                verts[0] , 

                verts[1]
            );
            verts.forEach((v, i, ar) => {
                let y = i;
                


                ctx.lineTo(
                    v[0] , 

                    v[1]
                );
                
            });
            ctx.lineTo(
                verts[0],
                verts[1] 
            );
            ctx.fillStyle = o.color || "blue";
            ctx.lineWidth = 1;
            ctx.fill()
            ctx.stroke();
            ctx.closePath();
        }
    }

    Object.defineProperties(this, {
        domElement: {
            get: () => dom
        },  
        ctx: {
            get: () => ctx
        },
        render: {
            get: () => render
        },
        on: {
            get: () => (nm, cb) => {
                if(!events[nm]) {
                    events[nm] = [];
                }
                events[nm].push(data => {
                    if(typeof cb == "function") {
                        cb(data);
                    }
                });
            }       
        },
        dispatchEvent: {
            get: () => (name, data) => {
                if(events[name]) {
                    events[name].forEach(x => {
                        x(data);
                    });
                }
            }
        }
    });
    
    rsz();

}

function scene() {
    let objects = [];
    Object.defineProperties(this, {
        add: {
            get: () => obj => {
                objects.push(obj);
            }
        },
        objects: {
            get: () => objects
        },
        update: {
            get: () => () => {                
                objects.forEach(x => {
                    if(typeof x.update == "function") {
                        x.update();
                    }
                });
                
            }
        }
    });
}

function mesh(data={}) {
    let verts = [],
        self = this,
        holder = {
            position:{},
            scale: {
                
            },
            rotation: {},
            origin:{}
        },
        actual = {
    
        },
        position = {},
        scale = {},
        rotation = {},
        origin = {},
        color,
        name,
        primitive,
        eventNames = "update",
        events = {},
        drawPrimitive = {
            circle(ctx) {
                ctx.beginPath();
                ctx.arc(
                    self.position.x,
                    self.position.y,
                    5, 
                    0,
                    360 * Math.PI / 180
                );
                ctx.closePath();
            },
            rect(ctx) {
                ctx.strokeRect(
                    self.position.x,
                    self.position.y,
                    30, 30
                );
            }
        },
        width = 1,
        height = 1,
        primitiveToVerts = {
            rect: () =>  [
                    0, 0,
                    width , 0,
                    width, height,
                    0, height
            ]
        },
        realVerts = verts,
        lineSegments = [],
        o = this;
    
    function updateRealVerts() {
            
            let  actualVerts = [],
                originedVerts = [],
                adjustedVerts = [],
                rotatedVerts = [],
                stepSize = o.step || 2,
                curVerts = [];
            
            o.verts.forEach((v, i) => {
                curVerts.push(v);
                if(
                    (i - 1) % stepSize === 0 &&
                    i !== 0
                ) {
                    actualVerts.push(curVerts);
                    curVerts = [];
                }
            });
            actualVerts = actualVerts.filter(x => x.length == stepSize);
            
            originedVerts = actualVerts.map(v => [
                v[0] - o.origin.x,
                v[1] - o.origin.y,
                v[2] - o.origin.z
            ]);
    
            rotatedVerts = originedVerts.map(v => 
                [

                    v[0] * Math.cos(o.rotation.x) - 
                    v[1] * Math.sin(o.rotation.x),

                    v[0] * Math.sin(o.rotation.x) + 
                    v[1] *Math.cos(o.rotation.x),
v[2]
                ]
            );

            adjustedVerts = rotatedVerts.map(v => 
                [
                    v[0] * 
                    o.scale.x + 
                    o.position.x,
    
                    v[1] * 
                    o.scale.y + 
                    o.position.y,

                    v[2] * 
                    o.scale.z + 
                    o.position.z,
                ]
            );

            realVerts = adjustedVerts;
            updateLineSegments();
    }   

    function updateLineSegments() {
                let lines = [];
                for(let i = 0, a = realVerts; i < a.length;i++) {
                    let start = [], end = []
                    if(i < a.length - 1) {
                        start = a[i];
                        end = a[i + 1];
                    } else {
                        start = a[i];
                        end = a[0];
                    }

                    lines.push({
                        start, end
                    })
                }
                lineSegments = lines;
    }
    Object.defineProperties(position, {
        x: {
            get: () => holder.position.x || 0,
            set: v => holder.position.x = v
        },
        y: {
            get: () => holder.position.y || 0,
            set: v => holder.position.y = v
        },
        z: {
            get: () => holder.position.z || 0,
            set: v => holder.position.z = v
        }
    });

    Object.defineProperties(scale, {
        x: {
            get: () => holder.scale.x || 1,
            set: v => holder.scale.x = v
        },
        y: {
            get: () => holder.scale.y || 1,
            set: v => holder.scale.y = v
        },
        z: {
            get: () => holder.scale.z || 1,
            set: v => holder.scale.z = v
        }
    });

    Object.defineProperties(rotation, {
        x: {
            get: () => holder.rotation.x || 0,
            set: v => holder.rotation.x = v
        },
        y: {
            get: () => holder.rotation.y || 0,
            set: v => holder.rotation.y = v
        },
        z: {
            get: () => holder.rotation.z || 0,
            set: v => holder.rotation.z = v
        }
    });

    Object.defineProperties(origin, {
        x: {
            get: () => holder.origin.x || 0,
            set: v => holder.origin.x = v
        },
        y: {
            get: () => holder.origin.y || 0,
            set: v => holder.origin.y = v
        },
        z: {
            get: () => holder.origin.z || 0,
            set: v => holder.origin.z = v
        }
    });
    

    Object.defineProperties(this, {
        verts: {
            get: ()=>verts,
            set(v) {
                verts = v
            }
        },
        name: {
            get: ()=>name,
            set(v) {
                name = v
            }
        },
        primitive: {
            get: ()=>primitive,
            set(v) {
                primitive = v;
                let newVerts = primitiveToVerts[v];
                if(newVerts) {
                    this.verts = newVerts();
                }
            }
        },
        width: {
            get: ()=>width,
            set(v) {
                width = v
            }
        },
        height: {
            get: ()=>height,
            set(v) {
                height = v
            }
        },
        position: {
            get: () => position,
            set: v => {
                position.x = v.x || 0;
                position.y = v.y || 0;
                position.z = v.z || 0;
            }
        },
        scale: {
            get: () => scale,
            set: v => {
                scale.x = v.x || v.x === 0 ? v.x : 1;
                scale.y = v.y  || v.y === 0 ? v.y : 1;
                scale.z = v.z  || v.z === 0 ? v.z : 1;
            }
        },
        rotation: {
            get: () => rotation,
            set: v => {
                rotation.x = v.x || 0;
                rotation.y = v.y || 0;
                rotation.z = v.z || 0;
            }
        },
        origin: {
            get: () => origin,
            set: v => {
                origin.x = v.x || 0;
                origin.y = v.y || 0;
                origin.z = v.z || 0;
            }
        },
        color: {
            get: () => color,
            set: v => {
                color = v;
            }
        },
        realVerts: {
            get: () => realVerts
        },
        lineSegments: {
            get: () => lineSegments
        },
        update: {
            get: () => () => {
                if(events["update"]) {
                    events.update.forEach(x => {
                        updateRealVerts();
                        x(this);
                    });
                }
            }
        },
        on: {
            get: () => (nm, fnc) => {
                if(!events[nm]) events[nm] = [];
                events[nm].push(stuff => {
                    if(typeof fnc == "function") {
                        fnc(stuff);
                    }
                });
            }
        }
    });

    eventNames.split(" ").forEach(x => {
        var name = "on" + x;
        if(!this.hasOwnProperty(name)) {
            Object.defineProperty(this, name, {
                get: () => events[name],
                set(v) {
                    events[x] = [
                        data => {
                            typeof v == "function" && v(data)
                        }
                    ];
                }
            });
        }
    });

    for(let k in data) {
        this[k] = data[k]
    }

    updateRealVerts();

}
canvas{
    width:100%;
    height:100%;
    position:absolute;
    top:0;  
    left:0px
}

.wow{
    float:right;
    z-index:1298737198
}
<meta charset="utf-8">
<button onclick="start()" class=wow>ok</button>
<canvas id=can>

</canvas>

See line 71 for the collision detection implementation call (and the return value of the function there).

The problem is, as you can hopefully see (just fullscreen it and use arrow keys to move, try colliding with the orange mesh at the corners) that it slides fine, but when it gets to the corners, it gets stuck at them.

Any ideas how to fix this — without using any kind of external libraries etc. (only what’s available in the snippet)?


Get this bounty!!!

#StackBounty: #unity #c# #collision-detection #physics #collision-avoidance How do I keep a rigidbody from making unnecessary collision…

Bounty: 50

I have been having trouble with a custom character controller I am making. The root of the problem lies with collisions. I am trying to make movement code, but I have a problem with ground movement. Pay attention to this gif. It might be hard to notice due to the low frame rate, but the collider registers collisions with objects (in red) that do not point out of the ground. They share the same y-position as the ground. I’ve printed the normals of the collisions, and there are a lot of normals that are perpendicular to the normal of the ground (which is causing the rigidbody to get stuck).

The problem lies with collisions in unity. If I were to print the y-position of the bottom of my collider, it would be just a tad lower than the y-position of the ground. The collider actually rests below the ground by an almost negligible amount (which is why it doesn’t always collide with these objects).

Here is what I have tried: I created a function that runs at the beginning of every FixedUpdate() that uses current collisions to change the y-position of the rigidbody. It only runs on collisions that would ground the player (meaning that they are below the collider). The problem with this approach is that the same issue can occur with walls, meaning that the collider can get stuck on walls. I cannot create another function that would do the same for walls for various reasons.

Here is what would fix the issue: I need help figuring out how I can make collisions more discrete in unity. Colliders rest slightly below objects when ontop of them. They can also “sink” just a little into walls. In other words, colliders can rest into other colliders, and I need this to stop.

Any help is appreciated. Thank you.


Get this bounty!!!