Javafx 2 JavaFX:大规模碰撞检测优化(四叉树)
我正在为我的游戏进行大规模碰撞检测(1000多个精灵对我的游戏来说是巨大的),我正在寻找实现这一点的方法,然后我找到了四叉树: 好吧,这是一种减少应该检查碰撞的物体数量的方法,将它们划分为有更多机会碰撞的物体组。 我在这里找到了一个java版本的四叉树:Javafx 2 JavaFX:大规模碰撞检测优化(四叉树),javafx-2,collision-detection,game-engine,quadtree,Javafx 2,Collision Detection,Game Engine,Quadtree,我正在为我的游戏进行大规模碰撞检测(1000多个精灵对我的游戏来说是巨大的),我正在寻找实现这一点的方法,然后我找到了四叉树: 好吧,这是一种减少应该检查碰撞的物体数量的方法,将它们划分为有更多机会碰撞的物体组。 我在这里找到了一个java版本的四叉树: 然后我更改它并将其用于我的javafx游戏。但是对于大量的对象,性能并不是很好,所以我对它进行了一些优化。我对每棵树使用了AnimationTimer来检查碰撞,这大大提高了性能。我认为动画计时器使用GPU进行处理,因为当我运行代码时,CP
然后我更改它并将其用于我的javafx游戏。但是对于大量的对象,性能并不是很好,所以我对它进行了一些优化。我对每棵树使用了
AnimationTimer
来检查碰撞,这大大提高了性能。我认为动画计时器使用GPU进行处理,因为当我运行代码时,CPU使用率不会很高(3%到5%-1640个精灵)。但是如果我使用Thread
而不是AnimationTimer
它会使用更多的CPU(大约40%到50%-1640个精灵)
import java.util.ArrayList;
导入java.util.List;
导入javafx.animation.AnimationTimer;
导入javafx.scene.layout.Region;
导入javafx.scene.layout.RegionBuilder;
导入javafx.scene.paint.Color;
导入viwofx.sprit.Sprite;
导入viwofx.ui.GameScene;
公共类四叉树
{
私有int MAX_OBJECTS=10;
私人int最大_级别=5;
私有整数级;
私人阿雷利斯特精灵;
未分配的私有阵列列表;
私有区域边界;
私有四叉树[]节点;
私有四叉树父树;
私有动画定时器检测;
私有布尔检测=假;
私有四叉树getqt()
{
归还这个;
}
公共四叉树(四叉树p,整数级,区域pBounds)
{
这个。父=p;
水平=水平;
精灵=新阵列列表(0);
unAllocatedSprites=新的ArrayList(0);
边界=pBounds;
节点=新的四叉树[4];
检测=新建动画计时器()
{
@凌驾
公共无效句柄(长l)
{
//如果此节点具有子节点,并且存在无法适应子节点边界的对象,则会发生此情况
//正在检查这些对象,直到它们能够适应子节点的边界,然后将它们添加到相应的绑定子节点,
//或者对象超出边界,则会将其推送到父节点
对于(int i=0;i=水平中点);
//对象可以完全适合左象限
如果(s.getNode().getTranslateX()>=bounds.getLayoutX()&&spriteMaxX=verticalMidpoint&&(s.getNode().getTranslateX()+s.getWidth())<(bounds.getLayoutX()+bounds.getPrefWidth())
{
if(顶象限)
{
指数=1;
}
else if(底部象限)
{
指数=3;
}
}
收益指数;
}
公共布尔isInside(精灵s)
{
double maxX=bounds.getLayoutX()+bounds.getPrefWidth();
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();
}
}