我的碰撞检测在 Java 小程序中生成低 fps

发布于 2025-01-01 04:29:12 字数 630 浏览 5 评论 0原文

这是我第一次尝试碰撞算法。我尝试检查带有边界的对象的矩形大小。现在,在这个应用程序中,我制作了运行子弹并检查是否在无延时 while 循环中发生碰撞。问题是,当我生成大约 30-40 颗子弹时,FPS 变得如此之低。如果有人能教我一种编写碰撞检测的可靠方法,我会很高兴。

顺便说一句,我使用了一个java Vector集合(可能迭代不够快?或者我的代码太乱)

public void checkBoundary(int width, int height) //width and height of the applet
{
    for(int i = 0; i < vec.size(); i++)
    {
        if(vec.get(i).x + vec.get(i).width <= 0 ||
            vec.get(i).y + vec.get(i).height <= 0 ||
            vec.get(i).x >= width ||
            vec.get(i).y >= height)
            vec.remove(i);
    }
}

这个Vector存储了一个Bullet的对象,以(x,y)为左下角,并且(宽度,高度)。

This is my first time experimenting on collision algorithm. I tried checking the rect size of an object with the boundary. Now, in this application, I made running bullets and check if the collision in a no time-delay while loop. The problem is, as I spawn around 30-40 bullets, the fps gets so low. I would be glad if someone could teach me a robust way to write collision detection.

By the way, I used a java Vector collection (Maybe the iteration is not fast enough? or my code is being too messy)

public void checkBoundary(int width, int height) //width and height of the applet
{
    for(int i = 0; i < vec.size(); i++)
    {
        if(vec.get(i).x + vec.get(i).width <= 0 ||
            vec.get(i).y + vec.get(i).height <= 0 ||
            vec.get(i).x >= width ||
            vec.get(i).y >= height)
            vec.remove(i);
    }
}

This Vector store an object of Bullet with (x,y) as bottom-left corner, and (width,height).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

吹梦到西洲 2025-01-08 04:29:12

首先,您的算法不正确,因为当您使用 vec.remove(i); 删除时,i+1 元素变为 i 元素,因此你跳过一个元素。

性能问题来自这样一个事实:在最坏的情况下,每个元素都会删除成本 O(n),因为每个后续元素都需要向左移动。试试这个:

public void checkBoundary(int width, int height) //width and height of the applet
{
  LinkedList<T> outofbounds = new LinkedList<T>();
  for(int i = 0; i < vec.size(); i++)
  {
    if(vec.get(i).x + vec.get(i).width <= 0 ||
        vec.get(i).y + vec.get(i).height <= 0 ||
        vec.get(i).x >= width ||
        vec.get(i).y >= height)
        outofbounds.add(vec.at(i)); 
  }
  vec.removeAll(outofbounds);

}

编辑:

正如 Frozen Spider 指出的那样,removeAll 很昂贵。它的复杂度为 O(outofbounds.size()*vec.size()),即 O(n^2)。当稍微改变逻辑时,您可以导出一个保证在 O(vec.size()) 中工作的算法。

public void checkBoundary(int width, int height) //width and height of the applet
{
  LinkedList<T> newvec = new LinkedList<T>(); 
  for(int i = 0; i < vec.size(); i++)
  {
    if(vec.get(i).x + vec.get(i).width <= 0 ||
        vec.get(i).y + vec.get(i).height <= 0 ||
        vec.get(i).x >= width ||
        vec.get(i).y >= height)
        continue;

        newvec.add(vec.at(i)); 
  }
  vec.clear();
  // or vec = newvec if there are no others reference sharing the same object as vec
  vec.addAll(newvec); 

}

First your algorithm is incorrect because when you remove using vec.remove(i);, the i+1 element become the i element, so you skip one element.

The performance issue come from the fact that on worst case each remove cost O(n), as each subsequent element need to be shifted left. try this:

public void checkBoundary(int width, int height) //width and height of the applet
{
  LinkedList<T> outofbounds = new LinkedList<T>();
  for(int i = 0; i < vec.size(); i++)
  {
    if(vec.get(i).x + vec.get(i).width <= 0 ||
        vec.get(i).y + vec.get(i).height <= 0 ||
        vec.get(i).x >= width ||
        vec.get(i).y >= height)
        outofbounds.add(vec.at(i)); 
  }
  vec.removeAll(outofbounds);

}

Edit:

As Frozen Spider pointed out, removeAll is expensive. It has a complexity of O(outofbounds.size()*vec.size()), which is O(n^2). When slightly changing the logic you can derive an algorithm which is guaranteed to work in O(vec.size()).

public void checkBoundary(int width, int height) //width and height of the applet
{
  LinkedList<T> newvec = new LinkedList<T>(); 
  for(int i = 0; i < vec.size(); i++)
  {
    if(vec.get(i).x + vec.get(i).width <= 0 ||
        vec.get(i).y + vec.get(i).height <= 0 ||
        vec.get(i).x >= width ||
        vec.get(i).y >= height)
        continue;

        newvec.add(vec.at(i)); 
  }
  vec.clear();
  // or vec = newvec if there are no others reference sharing the same object as vec
  vec.addAll(newvec); 

}
篱下浅笙歌 2025-01-08 04:29:12

remove() 是一项非常昂贵的操作,创建新列表、将所需元素复制到其中并用新列表替换原始列表要快得多。

我还建议您使用 ArrayList 而不是 Vector。如果需要同步,请将 ArrayList 包装在 Collections.synchronizedList() 中。

试试这个,几乎可以立即工作 - <16 毫秒(0.016 秒)超过 100k 元素:

public static void checkBoundary(int width, int height) // width and height of the applet
{
    int size = vec.size();
    List <YourObjectType> newVec = new ArrayList <YourObjectType>(size);
    for (int i = 0; i < size; i++) {
        YourObjectType element = vec.get(i);
        if (element.x + element.width > 0 && 
            element.y + element.height > 0 &&
            element.x < width && 
            element.y < height) {
                newVec.add(element);
        }
    }
    vec = newVec;
}

remove() is a very costly operation, much faster will be to create new list, copy desired elements into it and replace original list with new one.

I also recommend you to use ArrayList instead of Vector. If you need synchronization, wrap ArrayList in Collections.synchronizedList().

Try this, works almost instantly - <16 ms (0.016 sec) over 100k elements:

public static void checkBoundary(int width, int height) // width and height of the applet
{
    int size = vec.size();
    List <YourObjectType> newVec = new ArrayList <YourObjectType>(size);
    for (int i = 0; i < size; i++) {
        YourObjectType element = vec.get(i);
        if (element.x + element.width > 0 && 
            element.y + element.height > 0 &&
            element.x < width && 
            element.y < height) {
                newVec.add(element);
        }
    }
    vec = newVec;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文