# 2D游戏中的碰撞检测：圆形与矩形碰撞检测（Javascript&C++版）

（以上事情均发生在1月11日和1月12日，所以lufy看到文章开头不要以为走错家门了……）

# 一，原理介绍

## 情况一，矩形全部都在一个象限内，如图：

function isSameQuadrant(cood,objA,objB){
var coodX = cood.x;
var coodY = cood.y;
var xoA = objA.x
,yoA = objA.y
,xoB = objB.x
,yoB = objB.y;

if(xoA-coodX>0 && xoB-coodX>0){
if((yoA-coodY>0 && yoB-coodY>0) || (yoA-coodY<0 && yoB-coodY<0)){
return true;
}
return false;
}else if(xoA-coodX<0 && xoB-coodX<0){
if((yoA-coodY>0 && yoB-coodY>0) || (yoA-coodY<0 && yoB-coodY<0)){
return true;
}
return false;
}else{
return false;
}
}

{x:点的x坐标, y:点的y坐标}

# 二，Javascript版算法&测试代码

function hitTestRectArc(rectObj,arcObj,rectVec,arcR){
var rw = rectObj.getWidth()
,rh = rectObj.getHeight()
,ar = arcObj.getWidth()*0.5
,rx = rectObj.x
,ry = rectObj.y
,ax = arcObj.x
,ay = arcObj.y;

if(typeof rectVec != UNDEFINED){
rx += (rw - rectVec[0])*0.5;
ry += (rh - rectVec[1])*0.5;
rw = rectVec[0];
rh = rectVec[1];
}
if(typeof arcR != UNDEFINED){
ax += (ar - arcR);
ay += (ar - arcR);
ar = arcR;
}

var rcx = rx+rw*0.5,rcy = ry+rh*0.5;
var rltx = rx
,rlty = ry
,rlbx = rx
,rlby = ry+rh
,rrtx = rx+rw
,rrty = ry
,rrbx = rx+rw
,rrby = ry+rh;

if(
{x:ax,y:ay},
{x:rltx,y:rlty},
{x:rrbx,y:rrby}
)
){
var dX1 = Math.abs(ax-rltx),dY1 = Math.abs(ay-rlty);
var dX2 = Math.abs(ax-rlbx),dY2 = Math.abs(ay-rlby);
var dX3 = Math.abs(ax-rrtx),dY3 = Math.abs(ay-rrty);
var dX4 = Math.abs(ax-rrbx),dY4 = Math.abs(ay-rrby);

if(
(((dX1*dX1) + (dY1*dY1)) <= (ar*ar))
||(((dX2*dX2) + (dY2*dY2)) <= (ar*ar))
||(((dX3*dX3) + (dY3*dY3)) <= (ar*ar))
||(((dX4*dX4) + (dY4*dY4)) <= (ar*ar))
){
return true;
}
return false;
}else{
var result = false;
var squareX = ax
,squareY = ay
,squareW = ar*2
,squareH = squareW;
if(
(Math.abs(squareX-rcx) <= (squareW+rw)*0.5)
&&(Math.abs(squareY-rcy) <= (squareH+rh)*0.5)
){
result = true;
}
return result;
}
}

var rw = rectObj.getWidth()
,rh = rectObj.getHeight()
,ar = arcObj.getWidth()*0.5
,rx = rectObj.x
,ry = rectObj.y
,ax = arcObj.x
,ay = arcObj.y;

init(50,"mylegend",500,250,main);

function main(){
LGlobal.setDebug(true);

var back = new LSprite();
back.graphics.drawRect(5,"green",[0,0,LStage.width,LStage.height],true,"lightblue");

var cObj = new LSprite();
cObj.x = 200;
cObj.y = 120;
cObj.graphics.drawArc(0,"",[0,0,50,0,2*Math.PI],true,"red");

var rObj = new LSprite();
rObj.x = 250;
rObj.y = 70;
rObj.alpha = 0.8;
rObj.graphics.drawRect(0,"",[0,0,100,100],true,"green");

trace(hitTestRectArc(rObj,cObj));

rObj.x = e.offsetX-rObj.getWidth()*0.5;
rObj.y = e.offsetY-rObj.getHeight()*0.5;
trace(hitTestRectArc(rObj,cObj));
});
}


# 三，C++版

C++版我用的是Qt，所以大家运行要在Qt creator里编译运行。

HitTestAlg.h里的代码：

#ifndef HITTESTALG_H
#define HITTESTALG_H

#include <math.h>
#include <QPoint>
#include <QRect>

class CMath
{

public:

static int pow(int base, int powerOf)
{
return (int)::pow((double)base, (double)powerOf);
}

static int sqrt(int n)
{
return (int)::sqrt((double)n);
}

static int abs(int n)
{
n = n < 0 ? -n : n;
return n;
}

static int distance(const QPoint& pt1, const QPoint& pt2)
{
return CMath::sqrt(CMath::pow(CMath::abs(pt1.x() - pt2.x()), 2) + CMath::pow(CMath::abs(pt1.y() - pt2.y()), 2));
}

};

class CArc
{

protected:

QPoint	m_ptCenter;

public:

CArc() : m_nRadius(0), m_ptCenter(0, 0){}
CArc(int radius, int centerX, int centerY) : m_nRadius(radius), m_ptCenter(centerX, centerY){}
~CArc(){}

void setCenter(const QPoint& center){m_ptCenter = center;}
void setCenter(int centerX, int centerY){m_ptCenter = QPoint(centerX, centerY);}
QPoint center() const {return m_ptCenter;}
QRect rect() const {return QRect(center().x() - radius(), center().y() - radius(), 2 * radius(), 2 * radius());}

};

class CHitTestAlg
{

protected:

QRect	m_rtRect;
CArc	m_arArc;

protected:

{
bool bRes = false;
int nRectLeft = m_rtRect.left(), nRectTop = m_rtRect.top(), nRectRight = m_rtRect.right(), nRectBottom = m_rtRect.bottom();
int nArcCenterX = m_arArc.center().x(), nArcCenterY = m_arArc.center().y();
if((nRectLeft - nArcCenterX >= 0 && nRectRight - nArcCenterX >= 0 && nRectTop - nArcCenterY <= 0 && nRectBottom - nArcCenterY <= 0)
|| (nRectLeft - nArcCenterX <= 0 && nRectRight - nArcCenterX <= 0 && nRectTop - nArcCenterY <= 0 && nRectBottom - nArcCenterY <= 0)
|| (nRectLeft - nArcCenterX <= 0 && nRectRight - nArcCenterX <= 0 && nRectTop - nArcCenterY >= 0 && nRectBottom - nArcCenterY >= 0)
|| (nRectLeft - nArcCenterX >= 0 && nRectRight - nArcCenterX >= 0 && nRectTop - nArcCenterY >= 0 && nRectBottom - nArcCenterY >= 0)
){
bRes = true;
}
return bRes;
}

bool hitTestRect() const
{
QRect rtArc = m_arArc.rect();
bool bRes = false;
if(CMath::abs(m_rtRect.center().x() - rtArc.center().x()) <= CMath::abs((m_rtRect.width() + rtArc.width()) / 2)
&& CMath::abs(m_rtRect.center().y() - rtArc.center().y()) <= CMath::abs((m_rtRect.height() + rtArc.height()) / 2)
){
bRes = true;
}
return bRes;
}

bool hitTestAngleArc() const
{
bool bRes = false;
QPoint ptRectTopLeft = m_rtRect.topLeft(), ptRectTopRight = m_rtRect.topRight()
, ptRectBottomLeft = m_rtRect.bottomLeft(), ptRectBottomRight = m_rtRect.bottomRight()
, ptArcCenter = m_arArc.center();

if(CMath::distance(ptRectTopLeft, ptArcCenter) <= nArcRadius
|| CMath::distance(ptRectTopRight, ptArcCenter) <= nArcRadius
|| CMath::distance(ptRectBottomLeft, ptArcCenter) <= nArcRadius
|| CMath::distance(ptRectBottomRight, ptArcCenter) <= nArcRadius
){
bRes = true;
}
return bRes;
}

public:

CHitTestAlg(const QRect& rect, const CArc& arc) : m_rtRect(rect), m_arArc(arc){}
~CHitTestAlg(){}

bool hitTest() const
{
bool bRes = false;
bRes = hitTestAngleArc();
}else{
bRes = hitTestRect();
}
return bRes;
}

};

#endif // HITTESTALG_H

mainwindow.h里的代码：

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QWidget>
#include "HitTestAlg.h"

class MainWindow : public QWidget
{
Q_OBJECT

protected:

QRect		m_rtRect;
CArc		m_arArc;
bool		m_bHit;

protected:

virtual void mouseReleaseEvent(QMouseEvent *mouseEvent);
virtual void paintEvent(QPaintEvent *paintEvent);

public:

MainWindow(QWidget *parent = 0);
~MainWindow();

};

#endif // MAINWINDOW_H

mainwindow.cpp里的代码：

#include <QDebug>
#include <QMouseEvent>
#include <QBrush>
#include <QPainter>
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
: QWidget(parent)
, m_rtRect(0, 0, 100, 50)
, m_arArc(50, 200, 200)
, m_bHit(false)
{
QWidget::showMaximized();
}

MainWindow::~MainWindow()
{

}

void MainWindow::mouseReleaseEvent(QMouseEvent *mouseEvent)
{
if(mouseEvent){
QPoint ptPos = mouseEvent->pos();
QRect rtRect;
rtRect.setX(ptPos.x() - m_rtRect.width() / 2);
rtRect.setY(ptPos.y() - m_rtRect.height() / 2);
rtRect.setWidth(m_rtRect.width());
rtRect.setHeight(m_rtRect.height());
m_rtRect = rtRect;
m_bHit = CHitTestAlg(m_rtRect, m_arArc).hitTest();
QWidget::update();
}
}

void MainWindow::paintEvent(QPaintEvent *paintEvent)
{
Q_UNUSED(paintEvent)

QPainter xPainter(this);
{
xPainter.save();
QBrush xBrush; xBrush.setColor(Qt::red); xBrush.setStyle(Qt::SolidPattern);
QPen xPen; xPen.setColor(Qt::black); xPen.setStyle(Qt::SolidLine);
xPainter.setBrush(xBrush);
xPainter.setPen(xPen);
xPainter.restore();
}
{
xPainter.save();
QBrush xBrush; xBrush.setColor(Qt::darkGreen); xBrush.setStyle(Qt::SolidPattern);
QPen xPen; xPen.setColor(Qt::black); xPen.setStyle(Qt::SolidLine);
xPainter.setBrush(xBrush);
xPainter.setPen(xPen);
xPainter.drawRect(m_rtRect);
xPainter.restore();
}
{
xPainter.save();
QString sContent = QString("Hit Test: %1").arg(m_bHit ? "true" : "false");
QFont ftFont("Tahoma", 12, QFont::DemiBold, true);
xPainter.setFont(ftFont);
xPainter.drawText(20, m_arArc.rect().bottom() + 30, sContent);
xPainter.restore();
}
}

main.cpp里的代码：

#include <QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();

return a.exec();
}


Qt做的界面感觉还是不错的，哈哈~~

----------------------------------------------------------------

http://blog.csdn.net/yorhomwang