最近做一个弹珠游戏,核心就是碰撞检测,网上也看了一些资料,无奈悟性太低,理解不了什么投影法等,静下心来,自己也琢磨了一个思路。
游戏的场景是这样:在一个平面上有一个矩形框,框内有一个圆形弹珠,还有任意多边形,如图1.png
要检测一个多边形与该小球是否碰撞,我的思路是:
- 对于多边形每一条边,通过小球的圆心做垂直线,如果垂直线的长度小于或等于小球的半径则认为小球与该边碰撞
- 对于多边形每一个顶点,与小球的圆心的距离小于或等于小球的半径则认为小球与该边碰撞
边界的碰撞检测是非常简单的,判断小球的左右上下是否等于边框的或左或右或上或下
下面细说代码
- 通过圆心求任意直线的垂直线的交点
public static Point getVerticalPoint(Point o, Point a, Point b) {
double a1 = a.x;
double b1 = a.y;
double a2 = b.x;
double b2 = b.y;
System.out.println("线段从(" + a1 + "," + b1 + ")至(" + a2 + "," + b2 + ")");
// double k = (b2 - b1) / (a2 - a1);
// System.out.println("线段的斜率k=" + k);
double c1 = o.x;
double d1 = o.y;
System.out.print("则从点(" + c1 + "," + d1 + ")引该线段的垂直线,其垂直点为:");
double c2, d2;
//特殊情况1,a.x==b.x
if (a1 == a2) {
c2 = a1;
d2 = d1;
}
//特殊情况2,a.y==b.y
else if (b1 == b2) {
c2 = c1;
d2 = b1;
} else {
d2 = (b1 * (a2 - a1) * (a2 - a1) + d1 * (b2 - b1) * (b2 - b1) + (c1 - a1) * (a2 - a1) * (b2 - b1))
/ ((b2 - b1) * (b2 - b1) + (a2 - a1) * (a2 - a1));
c2 = (c1 * (a2 - a1) - (b2 - b1) * (d2 - d1)) / (a2 - a1);
}
System.out.println("(" + c2 + ", " + d2 + ")");
return new Point((int) c2, (int) d2);
}
- 已知交点,求交点是否在该线段上
/**
* 已知t在a与b确定的直线上
* 检测t点是否在a与b组成的线段上
*
* @param t 监测目标
* @param a 点a
* @param b 点b
* @return true如果t在点a和点b组成的线段上
*/
public static boolean isInSegment(Point t, Point a, Point b) {
return (t.x > a.x && t.x < b.x || t.x < a.x && t.x > b.x)
&& (t.y > a.y && t.y < b.y || t.y < a.y && t.y < b.y);
}
3.再求两点之间的距离
public static int getDistance(Point a, Point b) {
return (int) Math.sqrt((b.y - a.y) * (b.y - a.y) + (b.x - a.x) * (b.x - a.x));
}
4.判断一个多边形是否与小球碰撞,以三角形为例,代码中Block是阻碍物的抽象接口
public class Triangle implements Block {
private final Point a;
private final Point b;
private final Point c;
public Triangle(Point a, Point b, Point c) {
this.a = a;
this.b = b;
this.c = c;
}
@Override
public boolean isImpact(Ball ball) {
Point o = ball.getCenterPoint();
Point t = GeometryUtil.getVerticalPoint(o, a, b);
if (GeometryUtil.isInSegment(t, a, b)) {
if (GeometryUtil.getDistance(t, o) <= (int) ball.getRadio()) {
return true;
}
}
t = GeometryUtil.getVerticalPoint(o, a, c);
if (GeometryUtil.isInSegment(t, a, c)) {
if (GeometryUtil.getDistance(t, o) <= (int) ball.getRadio()) {
return true;
}
}
t = GeometryUtil.getVerticalPoint(o, b, c);
if (GeometryUtil.isInSegment(t, b, c)) {
if (GeometryUtil.getDistance(t, o) <= (int) ball.getRadio()) {
return true;
}
}
return false;
}
@Override
public void location(int left, int top) {
a.offset(left, top);
b.offset(left, top);
c.offset(left, top);
}
@Override
public void draw(Canvas canvas, Paint paint) {
canvas.drawLines(new float[]{a.x, a.y, b.x, b.y, a.x, a.y, c.x, c.y, b.x, b.y, c.x, c.y}, paint);
}
}
不对之处,请大家指正!