Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/unix/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Optimization 如何优化我的基本物理模拟器?_Optimization_Physics_Collision Detection - Fatal编程技术网

Optimization 如何优化我的基本物理模拟器?

Optimization 如何优化我的基本物理模拟器?,optimization,physics,collision-detection,Optimization,Physics,Collision Detection,我写了一个简单的物理建模器,可以让我在屏幕上弹起球。您可以单击并拖动以启动球,也可以一次生成数百个球,并观看它们彼此交互 这是一个有趣的小项目,如果可以的话,我想做得更进一步。我知道他们说早熟优化是万恶之源,但我开始遇到实际的性能障碍,我想知道是否有在游戏/模拟器开发方面有经验的人可以帮助我 问题: 现在,如果你添加了太多的球,我的程序就会阻塞(在我的机器上似乎不能处理超过800个球)。如果这样做,模拟将不再真实,并且所有球在底部彼此重叠 问题在于碰撞检测。在最简单的情况下,碰撞检测是一个O

我写了一个简单的物理建模器,可以让我在屏幕上弹起球。您可以单击并拖动以启动球,也可以一次生成数百个球,并观看它们彼此交互


这是一个有趣的小项目,如果可以的话,我想做得更进一步。我知道他们说早熟优化是万恶之源,但我开始遇到实际的性能障碍,我想知道是否有在游戏/模拟器开发方面有经验的人可以帮助我

问题:

现在,如果你添加了太多的球,我的程序就会阻塞(在我的机器上似乎不能处理超过800个球)。如果这样做,模拟将不再真实,并且所有球在底部彼此重叠

问题在于碰撞检测。在最简单的情况下,碰撞检测是一个O(N^2)问题。每个球检查另一个球。这很快就会导致性能下降(即使是在100个球之后,你也会在每个循环中进行10公里的碰撞检查)

如果你看这里,你可以看到我添加了几百个球的截图。模拟器跟不上,它们开始相互重叠


目前,我通过寻找重叠的球来检测碰撞。如果我发现两个球重叠,我会按它们的最小平移距离(MTD)将它们分开,或者将它们推开。然后我用一个简单的物理方程来调整它们的脉冲向量,然后它们在碰撞后向不同的方向移动

它工作得很好,除非球太多,最小平移距离变得明显。它们开始大量重叠,并在底部不断相互推挤。我越是增加“重力”,情况就越糟。它们上的压力增加,彼此压缩/重叠的量增加

再一次,我没有问题,直到我击中了相当数量的N个球

当前优化方法

碰撞检测-
-(又名排序和扫描)

我在球上使用插入排序,每个循环沿x轴进行。由于插入排序的性质,我可以利用模拟器的特性。帧到帧,球的位置变化很小,所以排序没有太多的工作要做。这会将摊销运行时间线性排序为O(N)或线性排序,而不是其平均运行时间O(N^2)

由于球已排序,因此在检查碰撞之前,我在第二个循环中进行了几次预检查。现在,我只需要检查彼此相邻的球(因为它们是沿着x轴排序的),并且每当我检查一个球与另一个球(其xmin大于当前球的xmax)时,我就会跳出第二个循环。因此它跳过了数千张支票

当我实现这一点时,它带来了巨大的速度提升。但是,我仍然希望能够处理600-800多个球。我读过一些关于物理引擎的书,这些引擎可以轻松地同时处理10k物体之间的碰撞检测,所以我想我可以通过一些工作达到1-2k

运行探查器后,结果显示碰撞检测占用了我大约55%的时间,而渲染占用了大约45%的时间。所以,这是我最昂贵的两个成本


问题:

你能想出更好的算法或技术让我的模拟器能够处理更多的球吗?


相关代码:

整个项目:

svn校验

或者,单击以在浏览器中手动浏览文件

感兴趣的部分:

  • -w/清扫和修剪
  • -如果仍然没有通过扫描和修剪删除,则需要昂贵的真实碰撞检查和解决方案
  • -仅渲染就占用了我大约45%的时间

跟踪附近的球-

正如插入排序是最佳的,因为每帧的变化最小,您可以使用相同的属性来跟踪球的“邻居”

每个球最多只能与6个其他球交互。您可能可以每隔10帧左右运行一个算法,计算出每个球有哪些邻居,然后在接下来的10帧中使用该信息来计算实际碰撞

您还可以跟踪每个球的矢量,绘制虚拟线,查看在接下来的3-5帧中哪些线交叉,并仅计算可能发生的碰撞(即使由于时间的原因很少发生)

正如您按x轴排序一样,您可以在主窗口内的细分中“分组”球。当球位于至少一个球直径的细分区域内时,它只需查看同一细分区域中的球。如果它更接近一个或两个边界,则需要查看一个或两个其他细分,但计算量应该更少

此外,这些细分可以动态定位,因此平均细分只有100个球-不需要具有相同大小的细分和不同数量的球

根据细分的拥挤程度(每平方单位的球数),您可以尝试不同的算法-填充稀疏的长方体只需要矢量计算来预测碰撞,而密集的细分可能只需要某种优化的六边形计算。对于许多帧,稀疏框可能不需要碰撞检测(因为重叠不会像在密集框中那样被注意到)

可以确定给定长方体的能量密度-具有低能球的非常稀疏长方体比具有高能的稀疏长方体需要更少的碰撞计算


-Adam

我认为是时候衡量性能了,以验证瓶颈到底在哪里。您不需要提前进行测量,因为存在明显的算法问题。现在仍然有改进a的空间

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 // +1 is a fudge factor.

XS = number of squares across = SW/R + 4; // the +4 adds some slop
YS = number of squares hight = SH/R + 4; // the +4 adds some slop

int sx(Point2D.Float p) // the square into which you put a ball at x
   // never returns a number < 1
 := (int)((p.x-R/2)/R) + 2;

int sy(Point2D.Float p) // the square into which you put a ball at y
   // never returns a number < 1
 := (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;
}