【问题标题】:JavaFX: Massive collision detection optimization (Quad Tree)JavaFX:大规模碰撞检测优化(四叉树)
【发布时间】:2013-05-30 22:58:04
【问题描述】:

我正在为我的游戏进行大规模碰撞检测(超过 1000 个精灵对于我的游戏来说是巨大的),我正在寻找一种方法来实现这一点,然后我找到了四叉树:

http://en.wikipedia.org/wiki/Quadtree

这是一种减少应该检查碰撞的对象数量的方法,方法是将它们划分为更有可能发生碰撞的对象组。 我在这里找到了一个 java 版本的四叉树:

http://gamedev.tutsplus.com/tutorials/implementation/quick-tip-use-quadtrees-to-detect-likely-collisions-in-2d-space/

然后我更改它并将其用于我的 javafx 游戏。但是对于大量的对象性能并不是很好,所以我对其进行了一些优化。

【问题讨论】:

    标签: javafx-2 collision-detection game-engine quadtree


    【解决方案1】:

    我对每棵树都使用了AnimationTimer 来检查碰撞,这大大提高了性能。我认为 Animation Timer 使用 GPU 来处理,因为当我运行我的代码时 CPU 使用率不会很高(3% 到 5% - 1640 个精灵)。但是如果我使用 Thread 而不是 AnimationTimer 它会使用更多的 CPU(大约 40% 到 50% - 1640 个精灵)。

    import java.util.ArrayList;
    import java.util.List;
    import javafx.animation.AnimationTimer;
    import javafx.scene.layout.Region;
    import javafx.scene.layout.RegionBuilder;
    import javafx.scene.paint.Color;
    import viwofx.sprit.Sprite;
    import viwofx.ui.GameScene;
    
    public class QuadTree
    {
    
      private int MAX_OBJECTS = 10;
      private int MAX_LEVELS = 5;
      private int level;
      private ArrayList<Sprite> sprites;
      private ArrayList<Sprite> unAllocatedSprites;
      private Region bounds;
      private QuadTree[] nodes;
      private QuadTree parent;
      private AnimationTimer detection;
      private boolean detecting = false;
    
      private QuadTree getqt()
      {
        return this;
      }
    
      public QuadTree(QuadTree p, int pLevel, Region pBounds)
      {
        this.parent = p;
        level = pLevel;
        sprites = new ArrayList<>(0);
        unAllocatedSprites = new ArrayList<>(0);
        bounds = pBounds;
        nodes = new QuadTree[4];
    
        detection = new AnimationTimer()
        {
          @Override
          public void handle(long l)
          {
            // This for happens when this node has child nodes and there is some object which can not fit whitin the bounds of child nodes
            // these object being checked till they can fit inside the bounds of child nodes then they will be added to correspinding child node,
            // or object is out of bounds then it will be pushed to the parent node
            for (int i = 0; i < unAllocatedSprites.size(); i++)
            {
              if (!isInside(unAllocatedSprites.get(i)))
              {
    
                pushToParent(unAllocatedSprites.get(i));
                continue;
              }
              int index = getIndex(unAllocatedSprites.get(i));
              if (index != -1)
              {
                nodes[index].add(unAllocatedSprites.remove(i));
              }
            }
            for (int i = 0; i < sprites.size(); i++)
            {
              Sprite ts = sprites.get(i);
              if (isInside(ts))
              {
                int ii = 0;
                for (ii = 0; ii < sprites.size(); ii++)
                {
                  Sprite ts2 = sprites.get(ii);
                  if (ts != ts2)
                  {
                    Your collision detection logic
                  }
                }
                if (parent != null)
                {
                  for (ii = 0; ii < parent.getUnAllocatedSprites().size(); ii++)
                  {
                    Sprite ts2 = parent.getUnAllocatedSprites().get(ii);
                    if (ts != ts2 && isInside(ts2))
                    {
                      Your collision detection logic
                    }
                  }
                }
              }
              else
              {
                pushToParent(ts);
              }
            }
          }
        };
      }
    
      public int getLevel()
      {
        return level;
      }
    
      public ArrayList<Sprite> getUnAllocatedSprites()
      {
        return unAllocatedSprites;
      }
    
      // Split the node into 4 subnodes
      private void split()
      {
        double subWidth = (bounds.getPrefWidth() / 2);
        double subHeight = (bounds.getPrefHeight() / 2);
        double x = bounds.getLayoutX();
        double y = bounds.getLayoutY();
    
        nodes[0] = new QuadTree(this, level + 1, RegionBuilder.create().layoutX(x).layoutY(y).prefWidth(subWidth).prefHeight(subHeight).build());
        nodes[1] = new QuadTree(this, level + 1, RegionBuilder.create().layoutX(x + subWidth).layoutY(y).prefWidth(subWidth).prefHeight(subHeight).build());
        nodes[2] = new QuadTree(this, level + 1, RegionBuilder.create().layoutX(x).layoutY(y + subHeight).prefWidth(subWidth).prefHeight(subHeight).build());
        nodes[3] = new QuadTree(this, level + 1, RegionBuilder.create().layoutX(x + subWidth).layoutY(y + subHeight).prefWidth(subWidth).prefHeight(subHeight).build());
      }
    
      private int getIndex(Sprite s)
      {
        int index = -1;
    
        double verticalMidpoint = bounds.getLayoutX() + (bounds.getPrefWidth() / 2);
        double horizontalMidpoint = bounds.getLayoutY() + (bounds.getPrefHeight() / 2);
        double spriteMaxX = (s.getNode().getTranslateX() + s.getWidth());
        double spriteMaxY = (s.getNode().getTranslateY() + s.getHeight());
    
        // Object can completely fit within the top quadrants
        boolean topQuadrant = (spriteMaxY < horizontalMidpoint);
        // Object can completely fit within the bottom quadrants
        boolean bottomQuadrant = (s.getNode().getTranslateY() >= horizontalMidpoint);
    
        // Object can completely fit within the left quadrants
        if (s.getNode().getTranslateX() >= bounds.getLayoutX() && spriteMaxX < verticalMidpoint)
        {
          if (topQuadrant)
          {
            index = 0;
          }
          else if (bottomQuadrant)
          {
            index = 2;
          }
        }
        // Object can completely fit within the right quadrants
        else if (s.getNode().getTranslateX() >= verticalMidpoint && (s.getNode().getTranslateX() + s.getWidth()) < (bounds.getLayoutX() + bounds.getPrefWidth()))
        {
          if (topQuadrant)
          {
            index = 1;
          }
          else if (bottomQuadrant)
          {
            index = 3;
          }
        }
    
        return index;
      }
    
      public boolean isInside(Sprite s)
      {
        double maxX = bounds.getLayoutX() + bounds.getPrefWidth();
        double maxY = bounds.getLayoutY() + bounds.getPrefHeight();
    
        // Object can completely fit within the left quadrants
        if (s.getNode().getTranslateX() >= bounds.getLayoutX() && (s.getNode().getTranslateX() + s.getWidth()) < maxX && s.getNode().getTranslateY() >= bounds.getLayoutY() && (s.getNode().getTranslateY() + s.getHeight()) < maxY)
        {
          return true;
        }
        if (parent != null && parent.getUnAllocatedSprites().contains(s))
        {
          return true;
        }
        return false;
    
      }
    
      public void pushToParent(Sprite s)
      {
        sprites.remove(s);
        unAllocatedSprites.remove(s);
        if (parent == null)
        {
    
          //System.out.println("parent");
          if (!unAllocatedSprites.contains(s))
          {
            unAllocatedSprites.add(s);
          }
          return;
        }
    
        parent.add(s);
        if (sprites.size() < 1 && unAllocatedSprites.size() < 1)
        {
          stopDetection();
        }
      }
    
      public void add(viwofx.sprit.Sprite sprite)
      {
        // if sprite is not fit in the bounds of node, it will be pushed to the parent node.
        // this is a optimization for when child node push a object to this node and object still is not fit in the bounds this node, 
        // so it will be pushed to the parent node till object can be fited whitin the node bounds
        // this if prevent of out of bounds object to being added to unAllocatedSprites and then being pushed to parent
        if (!isInside(sprite))
        {
          pushToParent(sprite);
          return;
        }
        // if tree has been splited already add sprite to corrosponding child
        if (nodes[0] != null)
        {
          int index = getIndex(sprite);
          if (index != -1)
          {
            nodes[index].add(sprite);
            return;
          }
          else
          {
            unAllocatedSprites.add(sprite);
            return;
          }
        }
    
        sprites.add(sprite);
        if (!detecting)
        {
          startDetection();
        }
    
        if (sprites.size() > MAX_OBJECTS && level < MAX_LEVELS)
        {
          if (nodes[0] == null)
          {
            split();
          }
          int i = 0;
          while (i < sprites.size())
          {
            int index = getIndex(sprites.get(i));
            if (index != -1)
            {
              nodes[index].add(sprites.remove(i));
            }
            else
            {
              unAllocatedSprites.add(sprites.remove(i));
            }
          }
        }
      }
    
      public List<Sprite> retrieve(List<Sprite> returnObjects, Sprite pRect)
      {
        int index = getIndex(pRect);
        if (index != -1 && nodes[0] != null)
        {
          nodes[index].retrieve(returnObjects, pRect);
        }
        returnObjects.addAll(sprites);
        return returnObjects;
      }
    
      public void startDetection()
      {
        detecting = true;
        detection.start();        
      }
    
      public void stopDetection()
      {
        //detecting = false;
        //detection.stop();
      }
    }
    

    希望对你有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-26
      • 2015-03-25
      • 1970-01-01
      • 2013-10-30
      • 1970-01-01
      • 1970-01-01
      • 2012-04-03
      • 2016-02-24
      相关资源
      最近更新 更多