尝试这个:
将你的矩形分成N*M个小正方形,使得这些小正方形略宽于一个球的半径。最好让这些小正方形覆盖矩形的边缘,而不是完全适应其中。
创建一个BitSet数组。不要使用Bitset[M][N],只需使用new Bitset[M*N] - 一点乘法不会伤害你。
用一个数字来标识每个球。当你将一个球放在某个位置时,设置该正方形及其周围8个正方形的位(为了更容易地实现这一点,扩展你的正方形数组,使其超出矩形的边缘一格 - 这样你就不必剪裁了)。
遍历所有正方形。对于每个正方形中的每对球,将该对标记为潜在碰撞。为此,创建一个BitSet,并假设你有H个球和球A、B占据同一个正方形,则设置位A+BH和AH+B。
现在查找潜在碰撞已经很容易了,因为BitSet包括一个方法,它说“找到在此之后被设置的下一个位”。请记住,每个位都是双重计数的,所以当检测到位Q被设置时,请确保取消设置配对的另一个位
(Q%H)*H + (Q/H)
。
或者:您可以轻松地折叠碰撞数组。A和B之间的碰撞-假设A>B-可以通过设置位
A * (A-1) / 2 + B
来标记。这样做的好处是您不需要关心球的总数。
实际上:忘了吧。只需使用我编写的这个类作为练习:
import java.util.BitSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class PairSet extends BitSet implements
Iterable<PairSet.Pair> {
public static class Pair implements Comparable<Pair> {
public final int a;
public final int b;
private Pair(int a, int b) {
if (a < 0 || b < 0 || a == b) { throw new IllegalArgumentException(
"Pair(" + a + "," + b + ")"); }
if (a > b) {
this.a = a;
this.b = b;
} else {
this.a = b;
this.b = a;
}
}
public String toString() {
return "Pair(" + a + "," + b + ")";
}
public int hashCode() {
return a * (a - 1) / 2 + b;
}
public boolean equals(Object o) {
return o instanceof Pair
&& hashCode() == ((Pair) o).hashCode();
}
public int compareTo(Pair o) {
return hashCode() - o.hashCode();
}
}
PairSet() {}
PairSet(BitSet z) {
or(z);
}
PairSet(Iterable<Pair> z) {
for (Pair p : z)
set(p);
}
public void set(Pair p) {
set(p.a, p.b);
}
public void clear(Pair p) {
clear(p.a, p.b);
}
public void set(int a, int b) {
if (a < 0 || b < 0 || a == b) { throw new IllegalArgumentException(
"add(" + a + "," + b + ")"); }
if (a > b) {
set(a * (a - 1) / 2 + b);
} else {
set(b * (b - 1) / 2 + a);
}
}
public void clear(int a, int b) {
if (a < 0 || b < 0 || a == b) { throw new IllegalArgumentException(
"add(" + a + "," + b + ")"); }
if (a > b) {
clear(a * (a - 1) / 2 + b);
} else {
clear(b * (b - 1) / 2 + a);
}
}
public Iterator<Pair> iterator() {
return new Iterator<Pair>() {
int at = -1;
int triangle = 0;
int a = 0;
public boolean hasNext() {
return nextSetBit(at + 1) != -1;
}
public Pair next() {
int nextat = nextSetBit(at + 1);
if (nextat == -1) { throw new NoSuchElementException(); }
at = nextat;
while (triangle <= at) {
triangle += a++;
}
return new Pair(a - 1, at - (triangle - a) - 1);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
这将很好地跟踪您的潜在碰撞。伪代码如下:
SW = width of rectangle
SH = height of rectangle
R = radius of balls + 1
XS = number of squares across = SW/R + 4;
YS = number of squares hight = SH/R + 4;
int sx(Point2D.Float p)
:= (int)((p.x-R/2)/R) + 2;
int sy(Point2D.Float p)
:= (int)((p.y-R/2)/R) + 2;
Bitset[] buckets = new BitSet[XS*YS];
{for(int i: 0; i<buckets.length; i++) bukets[i] = new BitSet();}
BitSet bucket(int x, int y) {return bucket[y*XS + x]}
BitSet bucket(Point2D.Float p) {return bucket(sy(p),sx(p));}
void move(int ball, Point2D.Float from, Point2D.Float to) {
if bucket(from) == bucket(to) return;
int x,y;
x = sx(from); y=sy(from);
for(int xx==-1;xx<=1; xx++)
for(int yy==-1;yy<=1; yy++)
bucket(sx+xx, sy+yy).clear(ball);
x = sx(to); y=sy(to);
for(int xx==-1;xx<=1; xx++)
for(int yy==-1;yy<=1; yy++)
bucket(sx+xx, sy+yy).set(ball);
}
PointSet findCollisions() {
PointSet pp = new PointSet();
for(BitSet bb: buckets) {
int a;
int prev_a;
for(prev_a = -1; (a = bb.nextSetBit(prev_a+1))!=-1; prev_a=a) {
int b;
int prev_b;
for(prev_b = a; (b = bb.nextSetBit(prev_b+1))!=-1; prev_b=b) {
pp.add(a,b);
}
}
return pp;
}