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