我遇到了使用Java2D绘制可能数十万个矩形的问题。
起初我的所作所为是这样的:
private void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
//Draw Graphics here
g.dispose();
bs.show();
}
我在测试后意识到,当有500个以上的矩形时,这个效率非常低,所以我认为可能将图形绘制到BufferedImage然后显示BufferedImage会更有效率,所以我想出了这个。
private BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
private void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Graphics g = image.createGraphics();
//Draw Graphics here
g.dispose();
Graphics g2 = bs.getDrawGraphics();
g2.drawImage(image, 0, 0, width, height, null);
g2.dispose();
bs.show();
}
然而,这仍然和第一种方式一样效率低下,我很难想出更好的方法来做到这一点,而且我宁愿远离像OpenGL这样的东西。
在500-1000个矩形之间,它变得非常慢,而且任何高于该程序的程序都会很快冻结。
所以似乎碰撞检测似乎是主要问题,而不是Java2D渲染。这就是我处理检测的方式,它检查两个矩形是否在任何一侧接触。如果某人有更好的方法,我会很感激,因为我看到这是多么低效。
public boolean collides(Entity e) {
Point2D upperLeftIn = new Point2D.Double(bounds.getX() + 1, bounds.getY());
Point2D upperRightIn = new Point2D.Double(bounds.getX() + 8, bounds.getY());
Point2D lowerLeftIn = new Point2D.Double(bounds.getX() + 1, bounds.getY() + 9);
Point2D lowerRightIn = new Point2D.Double(bounds.getX() + 8, bounds.getY() + 9);
Point2D upperLeftDown = new Point2D.Double(bounds.getX(), bounds.getY() + 1);
Point2D lowerLeftUp = new Point2D.Double(bounds.getX(), bounds.getY() + 8);
Point2D upperRightDown = new Point2D.Double(bounds.getX() + bounds.getWidth(), bounds.getY() + 1);
Point2D lowerRightUp = new Point2D.Double(bounds.getX() + bounds.getWidth(), bounds.getY() + 8);
Line2D top = new Line2D.Double(upperLeftIn, upperRightIn);
Line2D bottom = new Line2D.Double(lowerLeftIn, lowerRightIn);
Line2D left = new Line2D.Double(upperLeftDown, lowerLeftUp);
Line2D right = new Line2D.Double(upperRightDown, lowerRightUp);
if (e.bounds.intersectsLine(top)) {
return true;
}
if (e.bounds.intersectsLine(bottom)) {
return true;
}
if (e.bounds.intersectsLine(left)) {
return true;
}
if (e.bounds.intersectsLine(right)) {
return true;
}
return false;
}
答案 0 :(得分:4)
虽然我不确定如何处理由于编辑问题的内容/目标完全改变的事实...
总结一下:正如评论中指出的那样(你似乎已经预料到),甚至数千个矩形的渲染也不应该是Java2D中的一个问题。在内部, 使用DirectX或OpenGL的硬件支持,你真的必须将大量的抗锯齿文本和复杂的,纹理的或渐变绘制的形状溢出到屏幕上,以便真正减慢速度。< / p>
话虽如此,碰撞检测确实不是这里的瓶颈。
据推测,您发布的方法是Entity
类。大概,e.bounds
是Rectangle2D
。在这种情况下,您只需使用
public boolean collides(Entity e) {
return this.bounds.intersects(e.bounds);
}
在那里创建Line2D
对象并不清楚你想要实现什么,你应该用语言来解释。 (也许在实际界限周围有某种“门槛”?)。但是你应该记住,intersectsLine
方法的计算成本可能很高,至少与你实际想要做的测试相比。您应该尝试将其归结为间隔检查。
但是,即使你对collides
方法进行了这种(微? - )优化,问题也可能更为普遍:从你到目前为止所写的内容来看,你必须假设你是测试每个实体与其他实体的冲突,并在类似
for (int i=0; i<allEntities.size(); i++)
{
for (int j=i+1; j<allEntities.size(); j++)
{
Entity ei = allEntities.get(i);
Entity ej = allEntities.get(j);
if (ei.collides(ej)) handleCollision();
}
}
如果是这种情况,实现的优化不会对您有所帮助,因为问题出在渐近复杂性:交叉测试的数量(即,对collides
- 方法的调用次数随着对象的数量增加二次。对于500个对象,您必须对该方法执行~125.000次调用。这已经很多了,很难做到每秒60次。但是对于5000个对象,你不需要1.250.000个呼叫,但是12.500.000 - 这是100倍,当然,60 FPS是不可能的。
对于这么多对象的(成对)碰撞检测有复杂的数据结构。不幸的是,这里的“复杂”通常意味着“实施起来非常复杂”。 Bounding Volume Hierarchies可能是一种有助于通过合理努力加速碰撞检测的方法。但是如果你有“很多”和“小”的对象在“小”空间, Spatial Hashing 可能是更合适的解决方案。您可以使用此关键字快速查找教程/博客条目和示例代码,一个示例位于Spatial hashing implementation for fast 2D collisions,但还有其他几个。