原生js实现的OBB包围盒碰撞算法

网上看了很多OBB算法实例结果都不是js实现的 要不然实现的就是一大堆库看着是真的头痛。

基本原理请看这篇博客:

https://blog.csdn.net/qing101hua/article/details/52997160

好了下面直接上代码 可以直接复制下来保存到本地运行:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        body{
            margin: 0;
            padding: 0;
            position: relative;
            background-color: #333333;
            width: 100%;
            height: 100%;
        }
        html{
            width: 100%;
            height: 100%;
        }
        canvas{
            position: absolute;
            left: 50%;
            top: 50%;
            margin-left: -400px;
            margin-top: -300px;
            background-color: white;
        }
    </style>
</head>
<body>
<canvas id ="cvs" width="800" height="600">

</canvas>
<script type="text/javascript">
    function each(arr,f){
        for(var i = 0;i<arr.length;i++){
            f.call(arr[i],i,arr[i]);
        }
    }
    function test(dom){
        this.dom = dom;
        this.ctx = dom.getContext("2d");
        this.width = dom.width;
        this.height = dom.height;
        this.clear = function(){
            this.ctx.clearRect(0,0,this.width,this.height);
        };

        this.spriteArr = [];
        this.addSprite = function(sprite){
            this.spriteArr.push(sprite);
        };
        this.frame = function(){
            this.clear();
            var x = 0;
            var y = 0;
            this.ctx.beginPath();
            for(var i=0;i<this.spriteArr.length;i++){
                this.ctx.save();
                x = this.spriteArr[i].x+this.spriteArr[i].pivot.x*this.spriteArr[i].width;
                y = this.spriteArr[i].y+this.spriteArr[i].pivot.y*this.spriteArr[i].height;
                this.ctx.translate(x,y);
                this.ctx.rotate(Math.PI/180*this.spriteArr[i].angle);
                this.ctx.fillStyle = this.spriteArr[i].color;
                this.ctx.fillRect(this.spriteArr[i].x-x,this.spriteArr[i].y-y,this.spriteArr[i].width,this.spriteArr[i].height);
                this.ctx.restore();
            }
            this.ctx.closePath();

        }
    }
    function sprite(x,y,w,h){
        this.angle = 0;
        this.x = x;
        this.y = y;
        this.width = w;
        this.height = h;
        this.color = "#cccccc";
        this.pivot={
            x:0.5,
            y:0.5
        };
        this.getP = function(axis,points){
            var scalars = [],
                    v = new Vector();
            points.forEach(function(point) {
                v.x = point.x;
                v.y = point.y;
                scalars.push(v.dotProduct(axis))
            });

            return new Projection(Math.min.apply(Math, scalars),
                    Math.max.apply(Math, scalars))
        }
    }

    var Vector = function(point) {
        if (point === undefined) {
            this.x = 0;
            this.y = 0;
        }
        else {
            this.x = point.x;
            this.y = point.y;
        }
    };

    Vector.prototype = {
        // 获取向量大小
        getMagnitude: function() {
            return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2))
        },

        add: function(vector) {
            var v = new Vector();
            v.x = this.x + vector.x;
            v.y = this.y + vector.y;
            return v
        },

        subtract: function(vector) {
            var v = new Vector();
            v.x = this.x - vector.x;
            v.y = this.y - vector.y;
            return v
        },

        dotProduct: function(vector) {
            return this.x * vector.x + this.y * vector.y
        },

        // 由两点生成边
        edge: function(vector) {
            return this.subtract(vector)
        },

        // 垂直,即投影轴
        perpendicular: function() {
            var v = new Vector();
            v.x = this.y;
            v.y = 0 - this.x;
            return v
        },

        normalize: function() {
            var v = new Vector(0, 0),
                    m = this.getMagnitude();

            if(m !== 0) {
                v.x = this.x / m;
                v.y = this.y /m;
            }
            return v
        },

        // 投影轴的单位向量
        normal: function() {
            var p = this.perpendicular();
            return p.normalize();
        }
    };
    // E vector.js

    // S projection.js
    var Projection = function(min, max) {
        this.min = min;
        this.max = max;
    };

    Projection.prototype = {
        overlaps: function(projection) {
            return this.max > projection.min && projection.max > this.min;
        }
    };
    function collsion(){
        //获取旋转中心相对于canvas坐标系
        this.getPivotPoint = function(sp){
            return {
                x:sp.x+sp.pivot.x*sp.width,
                y:sp.y+sp.pivot.y*sp.height
            }
        };
        //获取精灵坐标系的左上角位置
        this.getPivotLocal = function(sp){
            var pivotPoint ={
                x:sp.x+sp.pivot.x*sp.width,
                y:sp.y+sp.pivot.y*sp.height
            };
            var localPoint = {x:sp.x -pivotPoint.x,y:sp.y-pivotPoint.y};
            return localPoint;
        };
        //获取旋转角度 相对于精灵坐标系
        this.getRotate = function(sp){
            var localPoint = this.getPivotLocal(sp);
            var angle = 0;
            if(localPoint.x>0&&localPoint.y==0){
                return 0;
            }else if(localPoint.y<0&&localPoint.x==0){
                return 90;
            }else if(localPoint.x<0&&localPoint.y==0){
                return 180
            }else if(localPoint.x==0&&localPoint.y>0){
                return 270
            }
            if(localPoint.x>0&&localPoint.y<0){
                angle=Math.atan(Math.abs(localPoint.y)/Math.abs(localPoint.x))*180/Math.PI;
            }else if(localPoint.x<0&&localPoint.y<0){
                angle=Math.atan(Math.abs(localPoint.x)/Math.abs(localPoint.y))*180/Math.PI+90;
            }else if(localPoint.x<0&&localPoint.y>0){
                angle=Math.atan(Math.abs(localPoint.y)/Math.abs(localPoint.x))*180/Math.PI+180;
            }else if(localPoint.x>0&&localPoint.y>0){
                angle=Math.atan(Math.abs(localPoint.x)/Math.abs(localPoint.y))*180/Math.PI+270;
            }
            return Math.round(angle);
        };
        //获取旋转过后精灵的左上角坐标
        this.getRotatePoint = function(sp){
            var getRotatePoint = this.getPivotPoint(sp);
            var newRotate = ((sp.angle%360)+360)%360;
            var rotatePoint = {
                x: (sp.x-getRotatePoint.x)*Math.cos(newRotate/180*Math.PI) - (sp.y-getRotatePoint.y)*Math.sin(newRotate/180*Math.PI)+getRotatePoint.x,
                y: (sp.x-getRotatePoint.x)*Math.sin(newRotate/180*Math.PI) + (sp.y-getRotatePoint.y)*Math.cos(newRotate/180*Math.PI)+getRotatePoint.y
            };
            return rotatePoint;
        };
        this.getRotatePoint2 = function(p1,p2,angle){
            var getRotatePoint = p1;
            var newRotate = ((angle%360)+360)%360;
            var rotatePoint = {
                x: (p2.x-getRotatePoint.x)*Math.cos(newRotate/180*Math.PI) - (p2.y-getRotatePoint.y)*Math.sin(newRotate/180*Math.PI)+getRotatePoint.x,
                y: (p2.x-getRotatePoint.x)*Math.sin(newRotate/180*Math.PI) + (p2.y-getRotatePoint.y)*Math.cos(newRotate/180*Math.PI)+getRotatePoint.y
            };
            return rotatePoint;
        };
        this.transitionPoint = function(p1,x,y){
            return {
                x:p1.x + x,
                y:p1.y + y
            };
        };
        //极坐标位移
        this.polarCoordinates=function(point,angle,distance){
            angle=(angle%360+360)%360;
            var p2={x:0,y:0};
            if(angle>0&&angle<90){
                p2.x=point.x+Math.cos(angle*2*Math.PI/360)*distance;
                p2.y=point.y+Math.sin(angle*2*Math.PI/360)*distance;
            }else if(angle>90&&angle<180){
                p2.x=point.x-Math.sin((angle-90)*2*Math.PI/360)*distance;
                p2.y=point.y+Math.cos((angle-90)*2*Math.PI/360)*distance;
            }else if(angle>180&&angle<270){
                p2.x=point.x-Math.cos((angle-180)*2*Math.PI/360)*distance;
                p2.y=point.y-Math.sin((angle-180)*2*Math.PI/360)*distance;
            }else if(angle>270&&angle<360){
                p2.x=point.x+Math.sin((angle-270)*2*Math.PI/360)*distance;
                p2.y=point.y-Math.cos((angle-270)*2*Math.PI/360)*distance;
            }
            if(angle==0||angle==360){
                p2.x=point.x+distance;
                p2.y=point.y;
            }else if(angle==90){
                p2.x=point.x;
                p2.y=point.y+distance;
            }else if(angle==180){
                p2.x=point.x-distance;
                p2.y=point.y;
            }else if(angle==270){
                p2.x=point.x;
                p2.y=point.y-distance;
            }
            return p2;
        };
        this.getRotatePoints = function(sp){
            var arr = [];
            arr.push(this.getRotatePoint(sp));
            arr.push(this.polarCoordinates(arr[0],sp.angle,sp.width));
            arr.push(this.polarCoordinates(arr[1],sp.angle+90,sp.height));
            arr.push(this.polarCoordinates(arr[0],sp.angle+90,sp.height));
            return arr;
        };
        this.getKeyMax = function(arr,key){
            var max = 0;
            arr.forEach(function(item,i){
                if(i==0){
                    max = item[key]
                }
                if(item[key]>=max){
                    max= item[key];
                }
            });
            return max;
        };
        this.getKeyMin = function(arr,key){
            var max = 0;
            arr.forEach(function(item,i){
                if(i==0){
                    max = item[key]
                }
                if(item[key]<=max){
                    max= item[key];
                }
            });
            return max;
        };
        this.isXj = function(a1,a2,b1,b2){
            if(a1 < a2 && b1 < b2) {
                if(a2 < b1 || a1 > b2) {
                    return false;
                }else {
                    return true;
                }
            }
        };
        this.pointToVec = function(vecs){
            var returnArr = [];
            var len = vecs.length-1;

            for(var i =0;i<len;i++){
                var v1 = new Vector(vecs[i]);
                var v2 = new Vector(vecs[i+1]);
                returnArr.push(v1.edge(v2).normalize());
            }
            var v1 = new Vector(vecs[vecs.length-1]);
            var v2 = new Vector(vecs[0]);
            returnArr.push(v1.edge(v2).normalize());
            return returnArr;
        };
        this.vecToS = function(vecs,points){
            var returnArr = [];
            var len = vecs.length;
            var len2 = points.length;
            var sP = [];
            for(var i=0;i<len;i++){
                for(var q = 0;q<len2;q++){
                    var v = new Vector(points[q]);
                    sP.push(v.dotProduct(vecs[i]));
                }
                returnArr.push(new Projection(Math.min.apply(Math,sP),Math.max.apply(Math,sP)));
            }
            return returnArr;
        };
        this.obb = function(sp1,sp2){
            var pointArr1 = this.getRotatePoints(sp1);
            var pointArr2 = this.getRotatePoints(sp2);
            var vec1 = this.pointToVec(pointArr1);
            var vec2 = this.pointToVec(pointArr2);
            var allVec = vec1.concat(vec2);
            var len = allVec.length-1;
            for(var i=0;i<len;i++){
                var axis = allVec[i];
                var p1 = sp1.getP(axis,pointArr1);
                var p2 =  sp2.getP(axis,pointArr2);
                if(!p1.overlaps(p2)){
                    return false;
                }
            }
            return true;
        };
    }
    var testEx = new test(document.getElementById("cvs"));
    var sp1 =new sprite(250,200,119,300);
    var sp2 = new sprite(0,200,278/2,244/2);
    sp1.pivot.x =0;
    sp1.pivot.y =0;

    var colObj = new collsion();
    testEx.addSprite(sp1);
    testEx.addSprite(sp2);
    var arrs=null;
    var bool = true;
    function amt(){
        sp2.angle+=1;

        testEx.frame();
        if (bool==true) {
            sp1.angle+=1;
        }
        else {
            sp1.angle-=1;
        }
        if(colObj.obb(sp1,sp2)){
            bool = !bool;
        }
        requestAnimationFrame(amt);
    }
    amt();

</script>
</body>
</html>

主要原理是用一个精灵对象当主坐标系 然后将另一个精灵的坐标通过变换转化为主精灵坐标中的点然后我们判断投影是否有相交 如果出现有没有相交的情况就直接判断为没有碰撞

猜你喜欢

转载自blog.csdn.net/baidu_38766085/article/details/82779126