怎样进行2D旋转矩形的碰撞检測。能够使用一种叫OBB的检測算法(Oriented bounding box)方向包围盒。这个算法是基于SAT(Separating Axis Theorem)分离轴定律的。而OBB不不过计算矩形的碰撞检測。而是一种算法模型。

简单解释一下概念,包围盒和分离轴定律。

包围盒:是依据物体的集合形状。来决定盒子的大小和方向,这样能够选择最紧凑的盒子来代表物体。见下图

黑色的就是包围盒,能够是凸多边形,最贴近检測物体就可以。

分离轴定律:两个凸多边形物体,假设我们能找到一个轴,使得两个在物体在该轴上的投影互不重叠。则这两个物体之间没有碰撞发生,该轴为Separating Axis

那么用一般去检測那些轴呢,垂直于多边形每条边的轴。例如以下图:

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

所以,分离轴定律变成。两个多边形在全部轴上的投影都发生重叠,则判定为碰撞。否则。没有发生碰撞。

以下。我仅仅考虑矩形的情况。怎样检測分离轴。

非常明显,矩形4条边,有4条检測轴,那么2个矩形就有8个。

可是矩形有2个轴是反复的,所以仅仅须要检測2条轴就能够了,既是矩形的两条互相垂直的边所在的轴。

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

如上图,推断碰撞,我们须要推断2个矩形在4个轴上的投影是否重叠。这里有2种可能的方式。

第一种,把每一个矩形的4个顶点投影到一个轴上。这样算出4个顶点最长的连线距离,以后相同对待第二个矩形。最后推断2个矩形投影距离是否重叠。

另外一种方式,把2个矩形的半径距离投影到轴上,以后把2个矩形的中心点连线投影到轴上。以后推断2个矩形的中心连线投影,和2个矩形的半径投影之和的大小。本文使用这样的方式。

这里用到一些向量的数学知识。例如以下图:

P点为矩形在X轴上的投影点,矩形在垂直轴上的投影点为原点。这里也能看出来,点P所在的矩形轴, 在X轴上的投影长度为OP,假设矩形逆时针绕远点O旋转。OP在X轴上的投影长度变小,直到为0。OP垂直于X轴。也就是,OP在X轴上的投影长度的最大与最小值。这也解释了。为什么我们选择检測轴为垂直于多边形边的轴,由于在这些轴上我们能取到极值,中间的那些轴就不是必需检測了。

怎样表示轴。我们须要用向量,正确的使用单位向量。可以简化模型,减少思考的难度。例如以下图:

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

如果P点的坐标为(px, py), 那么向量P就是(px, py)。点P在X轴上的投影点Q坐标是(qx, qy)。那么向量Q就是(qx, qy)。我们如果X轴上的单位向量是(1, 0)。

那么向量P和X轴上单位向量点乘有:

向量P * X轴单位向量 = |P| * |X轴单位向量| * cosPQ  = px * 1 + py * 0 = px

又由于单位向量的长度等于1所以,px就是向量Q的长度。这是很有意义的,我们就得到一个规律,就是把一个向量点乘一个单位向量。我们得到的是这个向量在这个单位向量上的投影长度。用代码表示为:

/**
* dot-multiply
*/
private float dot(float[] axisA, float[] axisB) {
return Math.abs(axisA[0] * axisB[0] + axisA[1] * axisB[1]);
}

这里float[] 存放的是一个点的x ,y 坐标。axisB 为单位向量,这个结果就是axisA向量在,单位向量axisB上投影的长度。

以下我们看一下,单位向量怎样表示:

单位向量是用单位圆来描写叙述的。如果这个圆的半径为1。那么圆上的不论什么一个坐标到原点构成的向量都能够看作一个单位向量。而且长度为1。这般,明显的点P就是一个单位向量。点P在单位圆上移动,那么这个单位向量就在旋转,向量P就和角A建立了关系。

非常明显的得出。cosA 就是向量P的X坐标。sinA 就是向量P的Y坐标。

这样我们就能够得出,单位向量P为(cosA,sinA)。

这个模型的意义就是把单位向量P能够看成矩形的条边。

例如以下图:

那么矩形的还有一个边相应的单位向量S怎样表示呢,向量S和向量P是垂直的,我们能够得出, S(-sinA, cosA), 向量S 点乘 向量P  = 0

至此。我们就能够通过一个旋转角度,得到一个矩形的2个检測轴的单位向量。

代码例如以下:

// unit vector of x axis
private float[] axisX;
// unit vector of y axis
private float[] axisY; // 0 -360
private float rotation; /**
* Set axis x and y by rotation
*
* @param rotation float 0 - 360
*/
public OBB setRotation(float rotation) {
this.rotation = rotation; this.axisX[0] = MathUtils.cos(rotation);
this.axisX[1] = MathUtils.sin(rotation); this.axisY[0] = -MathUtils.sin(rotation);
this.axisY[1] = MathUtils.cos(rotation); return this;
}

下一步怎样计算矩形的半径投影呢,什么又是半径投影呢,看下图:

橙色线段,是矩形的2条检測轴。3张图是矩形旋转的3个特殊位置截图。

蓝色线段就是矩形半径投影。事实上就是,矩形在X轴上最远处的交点,数学上意义就是2条检測轴的投影之和。

2条检測轴的向量和就是中心点到矩形一个顶点的向量,所以投影半径也是中心点到矩形顶点的向量投影长度。注意向量的方向会影响投影长度。

依照中间那幅图,2条检測轴向量和的投影是,2条检測轴投影的差值。假设把当中一个轴,旋转180度,那么2个检測轴和的投影就是,2条轴投影的和值。

至此。假设我们把矩形在随意角度的2条轴向量投影到单位向量上,依据前面的单位向量规律。我们就得到了轴向量在单位向量上投影的长度,而单位向量的长度为1,那么我们得到的就是轴向量与单位向量的比例。在用这个比例乘以轴向量的长度,就得到了轴的投影长度。就能求出轴半径的长度了。

如图

2个矩形检測过程中,每次以一个矩形的检測轴为坐标系,投影还有一个矩形的检測轴。

图中。蓝色线段为左边矩形的半径投影,黄色线段为右边矩形检測轴。

我们须要把右边2条检測轴投影到蓝色线段所在X轴的单位向量,得到投影比例。以后在乘以2条检測轴的长度,就能够得到右边矩形的半径投影。

红色线段为2个矩形的中心点连心,计算其在X轴的投影长度。比較中心点连线的投影长度与2矩形的半径投影长度之和。假设连线投影大。那么在这条轴上没有碰撞,否则碰撞。半径投影代码例如以下:

private float halfWidth;

private float halfHeight;

/**
* Get axisX and axisY projection radius distance on axis
*/
public float getProjectionRadius(float[] axis) { // axis, axisX and axisY are unit vector // projected axisX to axis
float projectionAxisX = this.dot(axis, this.axisX);
// projected axisY to axis
float projectionAxisY = this.dot(axis, this.axisY); return this.halfWidth * projectionAxisX + this.halfHeight * projectionAxisY;
}

推断2矩形终于是否碰撞,须要依次检測4个分离轴,假设在一个轴上没有碰撞,则2个矩形就没有碰撞。代码例如以下:

/**
* OBB is collision with other OBB
*/
public boolean isCollision(OBB obb) {
// two OBB center distance vector
float[] centerDistanceVertor = {
this.centerPoint[0] - obb.centerPoint[0],
this.centerPoint[1] - obb.centerPoint[1]
}; float[][] axes = {
this.axisX,
this.axisY,
obb.axisX,
obb.axisY,
}; for(int i = 0; i < axes.length; i++) {
// compare OBB1 radius projection add OBB2 radius projection to centerDistance projection
if(this.getProjectionRadius(axes[i]) + obb.getProjectionRadius(axes[i])
<= this.dot(centerDistanceVertor, axes[i])) {
return false;
}
} return true;
}

最后,给出OBB完整的代码封装

java代码:

/**
* @author scott.cgi
* @since 2012-11-19
*
* Oriented bounding box
*/
public class OBB { private float[] centerPoint; private float halfWidth; private float halfHeight; // unit vector of x axis
private float[] axisX;
// unit vector of y axis
private float[] axisY; // 0 -360
private float rotation; private float scaleX;
private float scaleY; private float offsetAxisPointDistance; /**
* Create default OBB
*
* @param x bornCenterX x
* @param y bornCenterY Y
* @param halfWidth
* @param halfHeight
*/
public OBB(float bornCenterX, float bornCenterY, float halfWidth, float halfHeight) { this.halfWidth = halfWidth;
this.halfHeight = halfHeight; this.scaleX = 1.0f;
this.scaleY = 1.0f; this.centerPoint = new float[] {
bornCenterX,
bornCenterY
}; this.axisX = new float[2];
this.axisY = new float[2]; float[] offsetAxisPoint = new float[] {
bornCenterX - Director.getHalfScreenWidth(),
bornCenterY - Director.getHalfScreenHeight()
}; this.offsetAxisPointDistance = (float) Math.sqrt(this.dot(offsetAxisPoint, offsetAxisPoint)); this.setRotation(0.0f);
} /**
* Create default OBB with born in center screen
*
* @param halfWidth
* @param halfHeight
*/
public OBB(float halfWidth, float halfHeight) {
this(Director.getHalfScreenWidth(), Director.getHalfScreenHeight(), halfWidth, halfHeight);
} /**
* Get axisX and axisY projection radius distance on axis
*/
public float getProjectionRadius(float[] axis) { // axis, axisX and axisY are unit vector // projected axisX to axis
float projectionAxisX = this.dot(axis, this.axisX);
// projected axisY to axis
float projectionAxisY = this.dot(axis, this.axisY); return this.halfWidth * this.scaleX * projectionAxisX + this.halfHeight * this.scaleY * projectionAxisY;
} /**
* OBB is collision with other OBB
*/
public boolean isCollision(OBB obb) {
// two OBB center distance vector
float[] centerDistanceVertor = {
this.centerPoint[0] - obb.centerPoint[0],
this.centerPoint[1] - obb.centerPoint[1]
}; float[][] axes = {
this.axisX,
this.axisY,
obb.axisX,
obb.axisY,
}; for(int i = 0; i < axes.length; i++) {
// compare OBB1 radius projection add OBB2 radius projection to centerDistance projection
if(this.getProjectionRadius(axes[i]) + obb.getProjectionRadius(axes[i])
<= this.dot(centerDistanceVertor, axes[i])) {
return false;
}
} return true;
} /**
* dot-multiply
*/
private float dot(float[] axisA, float[] axisB) {
return Math.abs(axisA[0] * axisB[0] + axisA[1] * axisB[1]);
} /**
* Set axis x and y by rotation
*
* @param rotation float 0 - 360
*/
public OBB setRotation(float rotation) {
this.rotation = rotation; this.axisX[0] = MathUtils.cos(rotation);
this.axisX[1] = MathUtils.sin(rotation); this.axisY[0] = -MathUtils.sin(rotation);
this.axisY[1] = MathUtils.cos(rotation); this.setCenter(this.centerPoint[0], this.centerPoint[1]); return this;
} /**
* Set OBB center point and will add offsetAxis value
*/
public OBB setCenter(float x, float y) {
float offsetX = this.offsetAxisPointDistance * MathUtils.cos(this.rotation);
float offsetY = this.offsetAxisPointDistance * MathUtils.sin(this.rotation); this.centerPoint[0] = x + offsetX;
this.centerPoint[1] = y + offsetY; return this;
} /**
* Set OBB scale x, y
*/
public OBB setScale(float scaleX, float scaleY) {
this.scaleX = scaleX;
this.scaleY = scaleY; return this;
} public float getRotation() {
return this.rotation;
} public float getCenterX() {
return this.centerPoint[0];
} public float getCenterY() {
return this.centerPoint[1];
} public float getHalfWidth() {
return this.halfWidth * this.scaleX;
} public float getHalfHeight() {
return this.halfHeight * this.scaleY;
} }

c
代码:

/*
* OBBRect.h
*
* Created on: 2013-2-11
* Author: scott.cgi
*/ #ifndef OBBRect_Rect_H_
#define OBBRect_Rect_H_ #include <stdbool.h> #include "Mojoc/Graphics/Draw/Rect.h"
#include "Mojoc/Toolkit/Def/CodeStyle.h"
#include "Mojoc/Toolkit/Utils/AMathUtils.h"
#include "Mojoc/Toolkit/Def/StructMember.h" #ifdef __cplusplus
extern "C" {
#endif typedef struct OBBRect OBBRect; /**
* Oriented bounding box in Rect shape
* Use openGL world coordinate system
*/
struct OBBRect { float centerX;
float centerY; /** Set origin(0,0) when obbRect create */
float originX;
float originY; /** Clockwise [0 - 360] */
float rotation;
float scaleX;
float scaleY; Get(
/** Unit vector of x axis */
StructMember(Vector2, xAxis); /** Unit vector of y axis */
StructMember(Vector2, yAxis); Rect* rect; /** Distance of center point to origin(0, 0 */
float offsetDistance;
/** Degree of vector which center point to origin(0, 0 */
float offsetDegree; float halfWidth;
float halfHeight;
); }; typedef struct {
OBBRect* (*create) (Rect* rect);
void (*init) (Rect* rect, Out(OBBRect* obbRect)); /**
* Set obbRect rotation or origin x,y called updateCenter for
* update obbRect center x,y
*/
void (*updateCenter)(OBBRect* obbRect); /**
* Set obbRect rotation
*/
void (*setRotation) (OBBRect* obbRect, float rotation); /**
* OBBRect is collision with other OBBRect
*/
bool (*isCollision) (OBBRect* obbRect, OBBRect* otherObb); /**
* Get real width with scaleX
*/
float (*getWidth) (OBBRect* obbRect); /**
* Get real height with scaleY
*/
float (*getHeight) (OBBRect* obbRect); } _AOBBRect; extern _AOBBRect AOBBRect; #ifdef __cplusplus
}
#endif #endif /* OBBRect_H_ */
/*
* OBBRect.c
*
* Created on: 2013-2-11
* Author: scott.cgi
*/ #include <malloc.h>
#include <math.h> #include "Mojoc/Physics/OBBRect.h"
#include "Mojoc/Toolkit/Utils/AMathUtils.h"
#include "Mojoc/Toolkit/Platform/Log.h" static void updateCenter(OBBRect* obbRect) {
obbRect->centerX = obbRect->originX +
obbRect->offsetDistance * obbRect->scaleX *
AMathUtils_Cos(obbRect->rotation + obbRect->offsetDegree); obbRect->centerY = obbRect->originY +
obbRect->offsetDistance * obbRect->scaleY *
AMathUtils_Sin(obbRect->rotation + obbRect->offsetDegree);
} static void setRotation(OBBRect* obbRect, float rotation) {
obbRect->xAxis->x = AMathUtils_Cos(rotation);
obbRect->xAxis->y = AMathUtils_Sin(rotation); obbRect->yAxis->x = -AMathUtils_Sin(rotation);
obbRect->yAxis->y = AMathUtils_Cos(rotation); obbRect->rotation = rotation;
} /**
* Get axisX and axisY projection radius distance on unit axis
*/
static inline float getProjectionRadius(OBBRect* obbRect, Vector2* axis) { // axis, axisX and axisY are unit vector // projected axisX to axis
float projectionAxisX = AMathUtils_DotMultiply2(axis->x, axis->y, obbRect->xAxis->x, obbRect->xAxis->y);
// projected axisY to axis
float projectionAxisY = AMathUtils_DotMultiply2(axis->x, axis->y, obbRect->yAxis->x, obbRect->yAxis->y); return obbRect->halfWidth * obbRect->scaleX * projectionAxisX +
obbRect->halfHeight * obbRect->scaleY * projectionAxisY; } static bool isCollision(OBBRect* obbRect, OBBRect* otherObb) {
// two OBBRect center distance vector
Vector2 distanceVec = {
obbRect->centerX - otherObb->centerX,
obbRect->centerY - otherObb->centerY,
}; Vector2* axes[] = {
obbRect->xAxis,
obbRect->yAxis,
otherObb->xAxis,
otherObb->yAxis,
}; for (int i = 0; i < 4; i++) {
// compare OBBRect1 radius projection add OBBRect2 radius projection to centerDistance projection
if (getProjectionRadius(obbRect, axes[i]) + getProjectionRadius(otherObb, axes[i]) <=
AMathUtils_DotMultiply2(distanceVec.x, distanceVec.y, axes[i]->x, axes[i]->y)) { return false;
}
} return true;
} static float getWidth(OBBRect* obbRect) {
return (obbRect->halfWidth * 2) * obbRect->scaleX;
} static float getHeight(OBBRect* obbRect) {
return (obbRect->halfHeight * 2) * obbRect->scaleY;
} static inline void initOBBRect(OBBRect* obbRect, Rect* rect) {
obbRect->rect = rect; obbRect->halfWidth = (rect->right - rect->left) / 2;
obbRect->halfHeight = (rect->bottom - rect->top) / 2; obbRect->scaleX = 1.0f;
obbRect->scaleY = 1.0f; obbRect->originX = 0.0f;
obbRect->originY = 0.0f; obbRect->centerX = rect->left + obbRect->halfWidth;
obbRect->centerY = rect->top - obbRect->halfHeight; obbRect->offsetDistance = sqrtf(AMathUtils_DotMultiply2(obbRect->centerX, obbRect->centerY, obbRect->centerX, obbRect->centerY));
obbRect->offsetDegree = AMathUtils_Atan2(obbRect->centerX, obbRect->centerY); LogD("centerX = %f, centerY = %f", obbRect->centerX, obbRect->centerY);
LogD("offsetDistance = %f, offsetDegree = %f",
obbRect->offsetDistance, obbRect->offsetDegree); setRotation(obbRect, 0.0f);
} static OBBRect* create(Rect* rect) {
OBBRect* obbRect = (OBBRect*) malloc(sizeof(OBBRect));
initOBBRect(obbRect, rect); return obbRect;
} static void init(Rect* rect, Out(OBBRect* obbRect)) {
initOBBRect(obbRect, rect);
} _AOBBRect AOBBRect = {
create,
init, updateCenter,
setRotation,
isCollision,
getWidth,
getHeight,
};

矩形旋转碰撞,OBB方向包围盒算法实现的更多相关文章

  1. 旋转矩形碰撞检测 OBB方向包围盒算法

    在cocos2dx中进行矩形的碰撞检测时需要对旋转过的矩形做碰撞检查,由于游戏没有使用Box2D等物理引擎,所以采用了OBB(Oriented bounding box)方向包围盒算法,这个算法是基于 ...

  2. 游戏碰撞OBB算法(java代码)

    业务需求      游戏2D型号有圆形和矩形,推断说白了就是碰撞检测 :      1.圆形跟圆形是否有相交      2.圆形跟矩形是否相交       3.矩形和矩形是否相交           ...

  3. PDF怎么编辑,如何旋转PDF页面方向

    很多的时候,无论是工作中,还是在学习中都会遇到PDF文件,对于PDF文件,熟悉的小伙伴知道,在编辑PDF文件的时候,是需要使用到PDF编辑软件的,那么,在编辑PDF文件的时候,需要旋转文件的页面,这时 ...

  4. libgdx判断矩形重叠碰撞

    有两种方式. 1. 排除法,排除四种不可能重叠的情况就是了. public static boolean IsOverlap( Rectangle rect1, Rectangle rect2 ){ ...

  5. 2.纯 CSS 创作一个矩形旋转 loader 特效

    原文地址:2.纯 CSS 创作一个矩形旋转 loader 特效 扩展后地址:https://scrimba.com/c/cNJVWUR  扩展地址:https://codepen.io/pen/ HT ...

  6. 2d游戏中的射线与矩形检测碰撞

    cc.exports.LineCollideRect(startLine,endLine,rect)--向量与矩形检测碰撞 --获取矩形的四个顶点位置 local p = {cc.p(rect.x,r ...

  7. unity2D以最小的角度旋转到目标方向(y方向为角色的主方向)

    一.使用向量原理转换到目标方向 为了让角色的自身y转向目标方向,并且以最小角度旋转,要点是获得当前方向与目标方向的叉值,从而判断应该旋转的方向 float rotateSpeed; //相对目标位置运 ...

  8. 17.纯 CSS 创作炫酷的同心矩形旋转动画

    原文地址:https://segmentfault.com/a/1190000014807564 感想: 这个特效不难,但是这想法可能想不到,哈哈,怎么又废了. HTML代码: <div cla ...

  9. 前端每日实战:2# 视频演示如何用纯 CSS 创作一个矩形旋转 loader 特效

    效果预览 按下右侧的"点击预览"按钮在当前页面预览,点击链接全屏预览. https://codepen.io/zhang-ou/pen/vjLQMM 可交互视频教程 此视频是可以交 ...

随机推荐

  1. http2.0笔记

    二进制分帧层 定义了如何封装 HTTP 消息并在客户端与服务器之间传输 http2.0的消息传输特点: 流 已建立的连接上的双向字节流 消息 与逻辑消息对应的完整的一系列数据帧 帧 http2.0通信 ...

  2. 无边无状态栏窗口(使用GetWindowLongPtr设置GWL_EXSTYLE)

    通过SetWindowLongPtr来设置窗口样式 var NewStyle: Integer; begin Application.Initialize; Application.MainFormO ...

  3. getResource(String name)用法及源码分析

    Project获取资源需要一个启点,加载资源的动作是由ClassLoader来完成的.Class对象和当前线程对象可以找到当前加载资源的ClassLoader,通过ClassLoader的getRes ...

  4. A Very Easy Triangle Counting Game

    题意:在圆上取n个点,相邻两个点之间连线,(注意,n和1相邻),然后所有点对(i ,i+2)相连,问能形成的不同的三角形有多少个? 思路:找规律 n=3,cnt=1; n=4,cnt=8; n=5 c ...

  5. UVA 11054 Wine trading in Gergovia 葡萄酒交易 贪心+模拟

    题意:一题街道上很多酒店,交易葡萄酒,正数为卖出葡萄酒,负数为需要葡萄酒,总需求量和总售出量是相等的,从一家店到另外一家店需要路费(路费=距离×运算量),假设每家店线性排列且相邻两店之间距离都是1,求 ...

  6. uva 10602 Editor Nottoobad(排序)

    题目连接:10602 Editor Nottoobad 题目大意:要输入n个单词,现在有三种操作, 1.输入一个字符,需要按下一次按键.  2.通过声控删除一个字符.3.通过声控复制一遍上面的单词.现 ...

  7. Swift - 自定义UIActivity分享

    UIActivity可以十分方便地将文字.图片等内容进行分享,比如分享到微信.微博.发送邮件.短信等等.我们不仅可以分享内容出来,也可以在自己的App里添加自己的分享按钮或隐藏已有的分享按钮来实现定制 ...

  8. 【IOS实例小计】打开google地图-web

    -(void)openMaps:(id)sender{ NSString *addressText = @"1 Queen st, Auckland,NZ"; addressTex ...

  9. Cocos2d-x 3.0 创建一个场景,并设置现场的时候,项目开始执行上主动

    头 #ifndef __TEST_H__ #define __TEST_H__ #include "cocos2d.h" USING_NS_CC; class Test : pub ...

  10. 简单的 "双缓冲" 绘图的例子(研究一下)

    所谓双缓冲就是先画到内存画布(如: TBitmap), 然后再转帖到目的地. 譬如下面小程序: procedure TForm1.FormCreate(Sender: TObject); begin ...