欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-03-13 14:34:47
...

网上看了很多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,
            y:0
        };

    }
    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.obb = function(sp1,sp2){
            var globalPoint = this.getPivotPoint(sp1);
            var pointArr1 = [
                {
                    x:-sp1.width*sp1.pivot.x,
                    y:-sp1.height*sp1.pivot.y
                },
                {
                    x:-sp1.width*sp1.pivot.x+sp1.width,
                    y:-sp1.height*sp1.pivot.y
                },
                {
                    x:-sp1.width*sp1.pivot.x+sp1.width,
                    y:-sp1.height*sp1.pivot.y+sp1.height
                },
                {
                    x:-sp1.width*sp1.pivot.x,
                    y:-sp1.height*sp1.pivot.y+sp1.height
                }
            ];

            var pointArr2 = this.getRotatePoints(sp2);
            for(var i = 0;i<pointArr2.length;i++){
                pointArr2[i] = this.getRotatePoint2(globalPoint,pointArr2[i],-sp1.angle);
                pointArr2[i] = this.transitionPoint( pointArr2[i],-globalPoint.x,-globalPoint.y)
            }
            //debugger;
            var xdSp1 = {
                x1:this.getKeyMin(pointArr1,"x"),
                x2:this.getKeyMax(pointArr1,"x")
            };
            var xdSp2 = {
                x1:this.getKeyMin(pointArr2,"x"),
                x2:this.getKeyMax(pointArr2,"x")
            };

            var ydSp1 = {
                x1:this.getKeyMin(pointArr1,"y"),
                x2:this.getKeyMax(pointArr1,"y")
            };
            var ydSp2 = {
                x1:this.getKeyMin(pointArr2,"y"),
                x2:this.getKeyMax(pointArr2,"y")
            };
            //这里的注释是查看坐标系转化过后的点信息
            /*var tran = 250;
            each(pointArr1,function(){
                testEx.ctx.beginPath();
                testEx.ctx.fillStyle = "#ffff00";
                testEx.ctx.arc(this.x+tran,this.y+tran,5,0,2*Math.PI);
                testEx.ctx.fill();
                testEx.ctx.closePath();
            });
            each(pointArr2,function(){
                testEx.ctx.beginPath();
                testEx.ctx.fillStyle = "#ffff00";
                testEx.ctx.arc(this.x+tran,this.y+tran,5,0,2*Math.PI);
                testEx.ctx.fill();
                testEx.ctx.closePath();
            });*/
           // debugger;
            if(this.isXj(xdSp1.x1,xdSp1.x2,xdSp2.x1,xdSp2.x2)&&this.isXj(ydSp1.x1,ydSp1.x2,ydSp2.x1,ydSp2.x2)){
                return true
            }else{
                return false;
            };
        }
    }
    var testEx = new test(document.getElementById("cvs"));
    var sp1 =new sprite(0,0,100,50);
    var sp2 = new sprite(250,200,100,50);
    var colObj = new collsion();
    sp1.x = 150;
    sp1.y = 250;
    sp1.pivot.x = 0.5;
    sp1.pivot.y = 0.5;
    testEx.addSprite(sp1);
    testEx.addSprite(sp2);

    var arrs=null;
    function amt(){

        testEx.frame();
        sp1.angle+=1;
        sp2.angle+=5;
        if(colObj.obb(sp1,sp2)){
            sp2.color = "#ff0000"
        }else{
            sp2.color = "#cccccc"
        }
        requestAnimationFrame(amt);
    }
    amt();

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

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