业务需求

     游戏2D型号有圆形和矩形,推断说白了就是碰撞检测 :
     1.圆形跟圆形是否有相交
     2.圆形跟矩形是否相交 
     3.矩形和矩形是否相交
     
     先明确要实现的原理,才干有思路写代码
     第1个最好推断,推断两个圆中心点的矩形是否小于这两个圆的半径之和
     第2个纠结了我一下,只是也不难先看图圆跟矩形关系有4种情况,例如以下图

 仅仅要推断圆心到矩形4条边的距离都小于圆的半径或者圆心在矩形内则它们相交,还要推断圆心在矩形内是防止出现上面第四张图那样的特殊情况
圆心到边的距离有例如以下两种情况

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaDM0ODU5MjUzMg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

两个箭头表示点到线段的距离,另外一种情况是特殊情况
第3个推断。有下面几种情况

我发现有个规律,两个矩形相交必定有一方的顶点在对方的图形内
推断矩形是否包括某个点这个简单:仅仅要推断点到矩形4条边的距离之和是否等于矩形长与宽的和(高中数学知识)
只是推断上面这些比不是说实现就完结了,还要考虑性能问题,游戏对性能要求高。并且有对应的算法,AABB,OBB算法
我仅仅讲OBB碰撞

方向包围盒(Oriented bounding box),简称OBB。方向包围盒类似于AABB。可是具有方向性、能够旋转。AABB不能旋转。

如图3所看到的。


推断两个图形(凸边形)是否相离:肯定有一条直线能让这两个图形在这条直线上的投影是不相交(换个说法:仅仅要有一条直线能把他们分离出来)例如以下图



推断公式:BE>(BC+DE)=(1/2AC+1/2DF)=1/2(AC+DF)      ---》 BE>1/2(AC+DF) 可推出两个矩形是分离了;
上图是两个矩形,o1和o2分别为两个矩形的中心,x直线是平行于A1A2的一条直线,你能够把他看做坐标轴x,当中x跟y直线垂直,非常明显有一条直线y把两个矩形分开了所以这两个矩形是不相交的
由于A1A2平行于x轴,所以矩形1在直线x轴的投影的长度为线段AC, 矩形2在x轴的投影为DF,仅仅要推断AC跟DF不相交,说明肯定有一条直线能把他们分离出来,如图中的y轴
还有个更加简单的方法,那就是这两个矩形的中心点连线o1o2的投影是否大于 两个矩形在x轴的投影的绝对值的一半的和   o1o2在x轴的投影为BE  仅仅要推断 BE>(BC+DE)=(1/2AC+1/2DF)=1/2(AC+DF)
就说明这两个矩形在x轴(也相当于直线A3A4,A1A2)的投影不相交,x轴不是真正意义上的x轴,我仅仅是让大家看得明了一点。

由于矩形是有规则的图形,仅仅要分别给他们检測矩形每一条边的垂直线的投影就能够了,两个矩形总共8条边,由于每一个矩形的边都有两两平行,和两两垂直。所以我们仅仅要检測两个矩形到每一个矩形相邻两条边的投影是否相交,仅仅要4次检測中有当中一次两个矩形的投影没有相交就能够推断这两个矩形没有相交,反之就是相交

由于要用到高中数学知识向量运算,数学公式:
                                                         公式一:
                                  两点 A(x1,y1) 。B(x2,y2)
                                  向量a=AB=(x2-x1,y2-y1) 。

                                 公式二:
                                  向量a=(xa,ya),b=(xb,yb)
                                  cosA=a*b/(|a|*|b|) 
                                                           向量a到B的投影=     |a|*cosA=|a|*( a*b/(|a|*|b|) )=
 (a.b)/|b|
                                  a*b=xa*xb+ya*yb;
                                 |b|=sqrt(xb*xb+yb*yb);
                                  投影=(a.b)/|b|=(xa*xb+ya*yb)/sqrt(xb*xb+yb*yb)
                                 注意 :我们须要的投影我们仅仅取正数 
 求矩形在一条直线的投影大小=矩形相邻两条边在直线上的投影的绝对值的和;
比方上图矩形B1B2B3B4在x投影=|B3B1在x轴投影|+|B1B2在x轴投影|=+|B1B2在x轴投影|+|B2B4在x轴投影|
上图我就不加画了,画多了花,你们看一下也应该明确,图上的x轴换成直线A1A2或者A3A4是一样的效果。所以大家千万别被这个迷惑,我仅仅是想让大家看明确点

原理明确了。代码就不是问题了。你学什么语言就用什么语言,由于我学的是java,那我就用java实现
一下是我的源代码,大家仅仅要看两个矩形碰撞那里就能够了

我这是以4个顶点确定一个矩形,client须要这么做的,事实上我认为一个中心点,长,宽,旋转的角度来确定应该矩形比較好,不然四个顶点。万一传错了当中一个,拼成的不是矩形那不是出问题了。


package game;

/**
* @author hjn
*
*/
public abstract class AbstractShape implements IShape { protected int x; protected int y; public AbstractShape(int x, int y) {
super();
this.x = x;
this.y = y;
} public int getX() {
return x;
} public void setX(int x) {
this.x = x;
} public int getY() {
return y;
} public void setY(int y) {
this.y = y;
} }
package game;

/**
* @author hjn
*
*/
public interface IShape { int getX(); int getY(); boolean collision(IShape other); boolean contains(int px, int py); IShape copy(); void moveTo(int px, int py); }
/**
*
*/
package game; /**
* 圆形
*
* @author hjn
*
*/
public class Circle extends AbstractShape { /** 半径 */
private int radius; public Circle(int px, int py, int radius) {
super(px, py);
this.radius = radius;
}
/**
* 跟别的图像是否有重叠
*/
@Override
public boolean collision(IShape other) {
if (other instanceof Circle) {
Circle otherCircle = (Circle) other;
return (this.radius + otherCircle.radius) * (this.radius + otherCircle.radius) >= ((this.x - otherCircle.x) * (this.x - otherCircle.x) + (this.y - otherCircle.y) * (this.y - otherCircle.y)); }else if(other instanceof Rectangle){
return other.collision(this);
}
return false;
} /**
* 是否包括某个点
*/
@Override
public boolean contains(int px, int py) {
return this.radius * radius - ((this.x - px) * (this.x - px) + (this.y - py) * (this.y - py)) >= 0;
} public int getRadius() {
return radius;
} public void setRadius(int radius) {
this.radius = radius;
} @Override
public Circle copy() {
return new Circle(radius, radius, radius);
} @Override
public void moveTo(int px, int py) {
this.x = px;
this.y = py;
}
}
/**
*
*/
package game; /**
* 矩形
*
* @author hjn
*
*/
public class Rectangle extends AbstractShape { /** 矩形的顶点 */
private int[][] vertex; public Rectangle(int px, int py, int[][] vertex) {
super(px, py);
this.vertex = vertex;
} /**
* 是否有重叠,圆心到矩形6条边的距离小于圆的半径而且圆心不在矩形内那么这个矩形跟这个圆重叠
*/
@Override
public boolean collision(IShape other) {
try {
if (other instanceof Circle) {
Circle circle = (Circle) other;
boolean fla = this.getLength(circle.x, circle.y, vertex[0][0],
vertex[0][1], vertex[1][0], vertex[1][1])
- circle.getRadius() < 0;
if (fla) {
return true;
}
boolean fla2 = this.getLength(circle.x, circle.y, vertex[0][0],
vertex[0][1], vertex[2][0], vertex[2][1])
- circle.getRadius() < 0;
if (fla2) {
return true;
}
boolean fla3 = this.getLength(circle.x, circle.y, vertex[0][0],
vertex[0][1], vertex[3][0], vertex[3][1])
- circle.getRadius() < 0;
if (fla3) {
return true;
}
boolean fla4 = this.getLength(circle.x, circle.y, vertex[1][0],
vertex[1][1], vertex[2][0], vertex[2][1])
- circle.getRadius() < 0;
if (fla4) {
return true;
}
boolean fla5 = this.getLength(circle.x, circle.y, vertex[1][0],
vertex[1][1], vertex[3][0], vertex[3][1])
- circle.getRadius() < 0;
if (fla5) {
return true;
}
boolean fla6 = this.getLength(circle.x, circle.y, vertex[2][0],
vertex[2][1], vertex[3][0], vertex[3][1])
- circle.getRadius() < 0;
if (fla6) {
return true;
}
boolean fla7 = this.contains(circle.x, circle.y);
if (fla7) {
return true;
}
} else if (other instanceof Rectangle) {
Rectangle otherRectangle = (Rectangle) other;
return this.collisionOBB(otherRectangle);
}
} catch (Exception e) {
// 数组下标越界
e.printStackTrace();
return false;
}
return false;
} /**
* OBB矩形碰撞检測
* @param rectangle 矩形2
* @return
*/
private boolean collisionOBB(Rectangle rectangle){
int[][] vertex2=rectangle.vertex;
/*矩形2相邻两条边的向量*/
int wx1=vertex2[0][0]-vertex2[1][0];
int wy1=vertex2[0][1]-vertex2[1][1];
int wx2=vertex2[1][0]-vertex2[2][0];
int wy2=vertex2[1][1]-vertex2[2][1];
/*两个矩形中心点连接向量*/
int centerX=(vertex2[0][0]+vertex2[2][0])/2-(vertex[0][0]+vertex[2][0])/2;
int centerY=(vertex2[0][0]+vertex2[2][0])/2-(vertex[0][0]+vertex[2][0])/2;
/*矩形一第一条边的向量*/
int x11=vertex[0][0]-vertex[1][0];
int y11=vertex[0][1]-vertex[1][1];
/*矩形一在第一条边的投影*/
double le1=Math.sqrt(x11*x11+y11*y11);
/*矩形2相邻两条边在矩形1第一条边上的投影,比如projection211表示第2个矩形的第1条边在矩形1上第1条边的投影*/
double projection2111=this.getProjection(wx1, wy1, x11, y11);
double projection2211=this.getProjection(wx2, wy2, x11, y11);
/*中心点连接向量的投影*/
double centerProjection1=this.getProjection(centerX, centerY, x11, y11);
/*两个矩形投影之和*/
double total=projection2111+projection2211+le1;
/*假设中心点向量投影大于矩形投影之和的一半那肯定没有碰撞*/
if(centerProjection1>total/2){
return false;
} int x12=vertex[1][0]-vertex[2][0];
int y12=vertex[1][1]-vertex[2][1];
double le2=Math.sqrt(x12*x12+y12*y12);
double projection2112=this.getProjection(wx1, wy1, x12, y12);
double projection2212=this.getProjection(wx2, wy2, x12, y12);
double centerProjection2=this.getProjection(centerX, centerY, x12, y12);
if(centerProjection2>(projection2112+projection2212+le2)/2){
return false;
}
/*反过来矩形1在矩形2相邻两条边的投影的一半跟中心点向量的投影大小对照,不一一写凝视了*/
int wx11=vertex[0][0]-vertex[1][0];
int wy11=vertex[0][1]-vertex[1][1];
int wx12=vertex[1][0]-vertex[2][0];
int wy12=vertex[1][1]-vertex[2][1]; int x21=vertex2[1][0]-vertex2[2][0];
int y21=vertex2[1][1]-vertex2[2][1];
double le3=Math.sqrt(x21*x21+y21*y21);
double projection1121=this.getProjection(wx11, wy11, x21, y21);
double projection1221=this.getProjection(wx12, wy12, x21, y21);
double centerProjection3=this.getProjection(centerX, centerY, x21, y21);
if(centerProjection3>(projection1121+projection1221+le3)/2){
return false;
} int x22=vertex2[1][0]-vertex2[2][0];
int y22=vertex2[1][1]-vertex2[2][1];
double le4=Math.sqrt(x22*x22+y22*y22);
double projection1122=this.getProjection(wx11, wy11, x22, y22);
double projection1222=this.getProjection(wx12, wy12, x22, y22);
double centerProjection4=this.getProjection(centerX, centerY, x22, y22);
if(centerProjection4>(projection1122+projection1222+le4)/2){
return false;
}
return true;
}
/**
* 求向量1在向量2的投影
* @param x1 向量1的x坐标
* @param y1 向量1的y坐标
* @param x2 向量2的x坐标
* @param y2 向量2的y坐标
* @return 投影的绝对值
*/
private double getProjection(int x1,int y1,int x2,int y2){
double t=(x1*x2+y1*y2)/(Math.sqrt(x1*x1+y1*y1)*Math.sqrt(x2*x2+y2*y2));
double length=Math.sqrt(x1*x1+y1*y1)*t;
return Math.abs(length);
} /**
* 是否包括某一点
*/
@Override
public boolean contains(int px, int py) {
double l = this.getLength(px, py, vertex[0][0], vertex[0][1],
vertex[1][0], vertex[1][1]);
double l1 = this.getLength(px, py, vertex[1][0], vertex[1][1],
vertex[2][0], vertex[2][1]);
double l2 = this.getLength(px, py, vertex[2][0], vertex[2][1],
vertex[3][0], vertex[3][1]);
double l3 = this.getLength(px, py, vertex[3][0], vertex[3][1],
vertex[0][0], vertex[0][1]);
double total = l1 + l2 + l3 + l;
double width = this.getLength(vertex[0][0], vertex[0][1], vertex[1][0],
vertex[1][1]);
double height = this.getLength(vertex[3][0], vertex[3][1],
vertex[0][0], vertex[0][1]);
return total == (width + height);
} /* 两点间的距离 */
private double getLength(int x1, int y1, int x2, int y2) {
return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
} /**
* 点到线段的距离
*
* @param x
* 点x坐标
* @param y
* 点y坐标
* @param x1
* 线段顶点1x坐标
* @param y1
* 线段顶点1y坐标
* @param x2
* 线段顶点2x坐标
* @param y2
* 线段顶点2y坐标
* @return
*/
private double getLength(int x, int y, int x1, int y1, int x2, int y2) {
double cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
if (cross <= 0) {
return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
}
double d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
if (cross >= d2) {
return Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
}
double r = cross / d2;
double px = x1 + (x2 - x1) * r;
double py = y1 + (y2 - y1) * r;
return Math.sqrt((x - px) * (x - px) + (py - y) * (py - y)); } public int[][] getVertex() {
return vertex;
} public void setVertex(int[][] vertex) {
this.vertex = vertex;
} @Override
public Rectangle copy() {
return new Rectangle(x, y, vertex);
} @Override
public void moveTo(int px, int py) { int vx = px - this.x;
int vy = py - this.y; int[][] copyOfVertex = new int[this.vertex.length][2];
for (int i = 0; i < this.vertex.length; i++) {
copyOfVertex[i][0] = vx + this.vertex[i][0];
copyOfVertex[i][1] = vy + this.vertex[i][1];
} this.x = px;
this.y = py;
} }
package game;

public class Test {
//依据两点能够获取向量
//依据一点能够获得
//依据两点获取 @org.junit.Test
public void test() {
Circle c1=new Circle(0, 0, 71);
Circle c2=new Circle(0, 76, 2);
/*圆是否包括圆*/
System.out.println("圆包括圆"+c1.collision(c2)); /*矩形的4个点*/
int[][] in=new int[4][2];
in[0][0]=0;
in[0][1]=100; in[1][0]=100;
in[1][1]=0; in[2][0]=200;
in[2][1]=100; in[3][0]=100;
in[3][1]=200; Rectangle rectangle1=new Rectangle(100, 100, in);
/*矩形是否包括圆*/
System.out.println("矩形是否包括圆:"+(rectangle1.collision(c1)));
/*圆包括矩形*/
System.out.println("圆包括矩形:"+(c1.collision(rectangle1)));
/*矩形形包括点*/
System.out.println("矩形形包括点:"+rectangle1.contains(55, 49)); /*矩形的4个点*/
int[][] in2=new int[4][2];
in2[0][0]=0;
in2[0][1]=44; in2[1][0]=0;
in2[1][1]=0; in2[2][0]=44;
in2[2][1]=0; in2[3][0]=44;
in2[3][1]=44; Rectangle rectangle2=new Rectangle(24, 24, in2);
long start=System.currentTimeMillis();
rectangle2.collision(rectangle1);
for(int i=0;i<100000;i++){
//c1.collision(c2);
//c1.collision(rectangle1);
rectangle2.collision(rectangle1);
}
long end =System.currentTimeMillis();
System.out.println((end-start));
System.out.println("矩形包括矩形:"+rectangle2.collision(rectangle1));
}
}

版权声明:本文博主原创文章。博客,未经同意不得转载。

游戏碰撞OBB算法(java代码)的更多相关文章

  1. CCF认证201803-2 碰撞的小球 java代码实现。

    问题描述 数轴上有一条长度为L(L为偶数)的线段,左端点在原点,右端点在坐标L处.有n个不计体积的小球在线段上,开始时所有的小球都处在偶数坐标上,速度方向向右,速度大小为1单位长度每秒. 当小球到达线 ...

  2. 排序算法Java代码实现(一)—— 选择排序

    以下几篇随笔都是记录的我实现八大排序的代码,主要是贴出代码吧,讲解什么的都没有,主要是为了方便我自己复习,哈哈,如果看不明白,也不要说我坑哦! 本片分为两部分代码: 常用方法封装 排序算法里需要频繁使 ...

  3. 算法-java代码实现基数排序

    基数排序 第11节 基数排序练习题 对于一个int数组,请编写一个基数排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组.保证元素均小于等于2000. 测试样例: [1 ...

  4. 算法-java代码实现计数排序

    计数排序   第10节 计数排序练习题 对于一个int数组,请编写一个计数排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组. 测试样例: [1,2,3,5,2,3], ...

  5. 算法-java代码实现希尔排序

    希尔排序 第8节 希尔排序练习题 对于一个int数组,请编写一个希尔排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组.保证元素小于等于2000. 测试样例: [1,2 ...

  6. 算法-java代码实现堆排序

    堆排序 第7节 堆排序练习题 对于一个int数组,请编写一个堆排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组. 测试样例: [1,2,3,5,2,3],6 [1,2 ...

  7. 算法-java代码实现快速排序

    快速排序 对于一个int数组,请编写一个快速排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组. 测试样例: [1,2,3,5,2,3],6 [1,2,2,3,3,5] ...

  8. 算法-java代码实现归并排序

    归并排序 对于一个int数组,请编写一个归并排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组. 测试样例: [1,2,3,5,2,3],6 [1,2,2,3,3,5] ...

  9. 排序算法Java代码实现(二)—— 冒泡排序

    本篇内容: 冒泡排序 冒泡排序 算法思想: 冒泡排序的原理是:从左到右,相邻元素进行比较. 每次比较一轮,就会找到序列中最大的一个或最小的一个.这个数就会从序列的最右边冒出来. 代码实现: /** * ...

随机推荐

  1. Common Lisp第三方库介绍 | (R "think-of-lisper" 'Albertlee)

    Common Lisp第三方库介绍 | (R "think-of-lisper" 'Albertlee) Common Lisp第三方库介绍 一个丰富且高质量的开发库集合,对于实际 ...

  2. Twenty Newsgroups Classification实例任务之TrainNaiveBayesJob(一)

    接着上篇blog,继续看log里面的信息如下: + echo 'Training Naive Bayes model' Training Naive Bayes model + ./bin/mahou ...

  3. HDOJ 4862 Jump

    K路径覆盖问题,最小费用最大流.... ,费用0,Y部有N*M个节点,每一个节点向汇点连一条边,流量1,费用0,假设X部的节点x能够在一步之内到达Y部的节点y,那么就连边x->y,费用为从x格子 ...

  4. C语言内存对齐

    转:http://blog.csdn.net/embeddedman/article/details/7429976 首先由一个程序引入话题:  1 //环境:vc6 + windows sp2 2  ...

  5. python基础教程_学习笔记10:异常

    异常 什么是异常 Python用异常对象来表示异常情况.遇到错误后,会引发异常.假设异常对象并未被处理或捕捉,程序就会用所谓的回溯(Traceback,一种错误信息)终止运行: >>> ...

  6. POJ 4003 Bob’s Race && HDU4123 Bob’s Race (dfs+rmq)

    Bob’s Race Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 378   Accepted: 119 Descript ...

  7. Java面向对象基础二

    1.对象的用法 2.多对象的创建方法 3.匿名对象的创建和用法

  8. 解决SQL查询总是超时已过期

    解决SQL查询总是超时已过期 .在WIN8里提示:OLE DB 或 ODBC 错误 : 查询超时已过期; HYT00 1.由于数据库设计问题造成SQL数据库新增数据时超时 症状:   Microso ...

  9. ThinkPHP---RBAC

    一.什么是RBAC 基于角色的访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注. 在RBAC中,权限与角色相关联,用户通 ...

  10. Android.mk编译.apk .so .jar .a第三方.apk .so .jar .a的方法

    一.编译一个简单的APK LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # Build all java files in the java s ...