OBB碰撞检测; SAT不正确; Three.JS

时间:2018-09-13 11:00:19

标签: three.js collision-detection

我正在使用Three.JS和TypeScript实现OBB冲突。我成功实现了一种检测碰撞的方法,但是我决定遵循Lua中的一种实现来创建对碰撞的响应。

我的代码:

private static getAxes(rot: number){
        let playerParallelAxis = new Vector3(Math.sin(rot), 0, Math.cos(rot));
        let playerPerpendicularAxis = new Vector3(Math.cos(rot), 0, Math.sin(rot));

        let blockParallelAxis = new Vector3(0, 0, 1);
        let blockPerpendicularAxis = new Vector3(1, 0, 0);

        return [playerParallelAxis, playerPerpendicularAxis, blockParallelAxis, blockPerpendicularAxis];
    }

    private static getCorners(pos: Vector3, rot?: number){
        let corners: Array<Vector3> = new Array();
        if(isNaN(rot)){
            corners.push(pos.clone());
            corners.push(pos.clone().add(new Vector3(0, 0, 1)));
            corners.push(pos.clone().add(new Vector3(1, 0, 1)));
            corners.push(pos.clone().add(new Vector3(1, 0, 0)));
        }else{

            const radius = Math.sqrt(0.75 * 0.75 + 0.5 * 0.5);
            const phi = Math.PI / 2;
            const theta = Math.atan(0.5/0.75);

             //front right
             corners.push(new Vector3().setFromSpherical(new Spherical(radius, phi, rot - theta)).add(pos).setY(0));

             //front left
             corners.push(new Vector3().setFromSpherical(new Spherical(radius, phi, rot + theta)).add(pos).setY(0));

             //back left
             corners.push(new Vector3().setFromSpherical(new Spherical(radius, phi,  Math.PI + rot - theta)).add(pos).setY(0));

             //back right
             corners.push(new Vector3().setFromSpherical(new Spherical(radius, phi,  Math.PI + rot + theta)).add(pos).setY(0));
        }
        return corners;
    }

    private static isPositionBlock(pos: Vector3){
        for(let i = 0; i < blockPositions.length; i ++){
            if(blockPositions[i].equals(pos)) return true;
        }
        return false;
    }
static test(playerPos: Vector3, playerRot: number){
    playerPos.add(new Vector3(0.5, 0, 0.5));
    let blockPos = playerPos.clone().floor();
    let blockPositions: Array<Vector3> = new Array();
    for(let x = blockPos.x - 1; x <= blockPos.x + 1; x ++){
        for(let z = blockPos.z - 1; z <= blockPos.z + 1; z ++){
            let testPos = new Vector3(x, 0, z);
            if(this.isPositionBlock(testPos)){
                blockPositions.push(testPos);
            }
        }
    }
    let correctionArray: Array<Vector3> = new Array();
    if(blockPositions.length){
        for(let i = 0; i < blockPositions.length; i ++){
            let collisionCorrection = TestCollision.collide(playerPos, playerRot, blockPositions[i]);
            if(collisionCorrection.x !== 0 || collisionCorrection.z !== 0){
                correctionArray.push(collisionCorrection);
            }

        }
    }
    return correctionArray;
}
static collide(playerPos: Vector3, playerRot: number, blockPos: Vector3){
    const between = (val: number, min: number, max: number ): boolean => {
        return min <= val && max >= val;
    };

    let playerCorners = TestCollision.getCorners(playerPos, playerRot);
    let blockCorners = TestCollision.getCorners(blockPos);

    let axes = TestCollision.getAxes(playerRot);

    let mtvs: Array<Vector3> = new Array();

    for(let i = 0; i < axes.length; i ++){

        let playerScalars: Array<number> = new Array();
        let blockScalars: Array<number> = new Array();

        for(let k = 0; k < axes.length; k ++){
            //playerScalars.push(TestCollision.dot(axes[i], playerCorners[k]));
            //blockScalars.push(TestCollision.dot(axes[i], blockCorners[k]));
            playerScalars.push(playerCorners[k].dot(axes[i]));
            blockScalars.push(blockCorners[k].dot(axes[i]));
        }

        let playerMax = Math.max.apply(null, playerScalars);
        let playerMin = Math.min.apply(null, playerScalars);

        let blockMax = Math.max.apply(null, blockScalars);
        let blockMin = Math.min.apply(null, blockScalars);

        // if(blockMin > playerMax || blockMax < playerMin){
        //     return new Vector3(); //there is no collision.
        // }
        if(between(playerMin, blockMin, blockMax) || between(blockMin, playerMin, playerMax)){
            return new Vector3();
        }
        let overlap = playerMax - blockMin;
        if(playerMax > blockMax){
            overlap = - (blockMax - playerMin);
        }

        mtvs.push(axes[i].clone().multiplyScalar(overlap)); //clone not needed??    
    }

    let distance = mtvs[0].lengthSq();
    let shortestVec = mtvs[0];
    for(let i = 1; i < mtvs.length; i++){
        let squareLength = mtvs[i].lengthSq();
        if(squareLength < distance){
            distance = squareLength;
            shortestVec = mtvs[i];
        }
    }
    return shortestVec;
}

我用克隆的矢量调用测试函数,据我所知,所有位置数据都是正确的,

在这种情况下会出现误报。 http://prntscr.com/ktwsqq坦克的右上角与已装满的坦克旁边的空方块相交。因此,我认为这与playerParallelAxis有关,因为该轴应该很容易证明没有碰撞。

Lua实现在这里:https://www.youtube.com/watch?v=IELWpIGtjRg 由于作者无需费心将代码发布到GitHub,因此可以使用屏幕截图抓取代码。

https://prnt.sc/kti65j https://prnt.sc/kti5wc

这是develop分支上的整个课程:https://github.com/DakotaLarson/Tanks-Client/blob/develop/src/arena/CollisionHandler.ts

从包含CollisionHandler的目录的player子目录中的Player类调用

代码。

如果您有任何疑问或需要进一步说明,请告诉我!

修改9/15/18 我可以使用班上已有的代码找到解决方案。不要问我为什么上面的代码不起作用。老实说,我不知道。代码被推送到这里:https://github.com/DakotaLarson/Tanks-Client/blob/develop/src/arena/CollisionHandler.ts

编辑4/8/19 我现在将这段代码分为三类:https://github.com/DakotaLarson/BattleTanks-Client/tree/master/src/arena/collision

0 个答案:

没有答案