原生js實現的OBB包圍盒碰撞演算法
阿新 • • 發佈:2018-11-01
網上看了很多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>
主要原理是用一個精靈物件當主座標系 然後將另一個精靈的座標通過變換轉化為主精靈座標中的點然後我們判斷投影是否有相交 如果出現有沒有相交的情況就直接判斷為沒有碰撞