I'm trying to implement a class to check if two game objects intersect. Can anyone give me a better solution / more elegant to this problem?
Basically I want to addCollision and know if one object collidesWith another. A double entry matrix seemed a good idea.
private class CollisionMatrix {
private boolean[][] matrix;
private HashMap<Tag, Integer> matrixIndexes = new HashMap<Tag, Integer>();
public CollisionMatrix() {
int i = 0;
for (Tag tag : Tag.values())
matrixIndexes.put(tag, i++);
matrix = new boolean[i][i];
}
private void addCollision(Tag tag1, Tag tag2) {
int p1 = matrixIndexes.get(tag1);
int p2 = matrixIndexes.get(tag2);
matrix[p1][p2] = true;
matrix[p2][p1] = true;
}
private boolean collidesWith(Tag tag1, Tag tag2) {
int p1 = matrixIndexes.get(tag1);
int p2 = matrixIndexes.get(tag2);
return matrix[p1][p2] || matrix[p2][p1];
}
}
This is not a complete answer, but it should set you on a path to get a more complete solution.
The simplest (not efficient) way to do this is to have a list of the objects that can collide with each other and then for every frame in time, got through every object in the list and check if the object collides (Shares the same space or bounding volume) with another one in the list.
pseudo code:
L: list of objects that can potentially collide.
t: time
for each frame in t {
for each object obj in L {
P: list of objects without obj
for each object otherObj in P {
does obj collide with otherObj
}
}
}
While this technically works, it's not a good solution as it will be very slow as soon as you start having many objects, and it doesn't take that many to make it slow.
To make this possible in real time, you would need to add some acceleration techniques.
One of these acceleration techniques is using "Bounding volume hierarchy" or BVH. https://en.wikipedia.org/wiki/Bounding_volume_hierarchy
In a nutshell, BVH is technique or algorithm to enable quick lookups of which objects are likely to collide.
It typically uses some type of tree structure to keep track of the positions and volumes occupied by the said objects. Tree structures provide faster lookup times than just linearly iterating a list multiple times.
Each level of the tree provides a hierarchy of bounding volumes (space the object is likely to occupy). Top levels of the tree provide a bigger volume for the particular object (a more rough, less granular or less fitting to the object's shape), but easier to discard if the object in question is not in that same space (you would know with little calculations that the object would never collide with anything in that same bounding volume). The deeper in the tree you go, the more granular or more fitting to the objects shape the bounding volumes get, until you get the objects which collide.
Hope this helps :)
Related
so lately i've been learning about abstract classes and interfaces, and i saw a neat yt video about falling sand games (think Noita) so i wanted to take a crack at it as some particles share a characteristic that can be abstracted, and behavior that's not specific to only particles that can be interfaced. only going to be implementing 2, maybe 3 types of "particle" but it seems im having trouble with gravity.
i've spent a few hours trying to figure out a good way of doing it and this is what i got so far out of my attempts (which include: gravity somehow working backwards, collision being ignored, and working, but sunk a few pixels into the object)
// the drop method is called every second, and force == 1
// drop is a method from the Gravity interface, Sand implements gravity and overrides drop.
#Override
public void drop(Particle self) {
for (Particle p : Controller.particle) {
if (p.equals(self)) {
continue;
}
if (self.rectangle.getBoundsInParent().intersects(p.rectangle.getBoundsInParent())) {
Behavior.repulseUpward(self, force);
}
}
Behavior.gravityDrop(self, force);
}
public class Behavior {
static void gravityDrop(Particle p, double force) {
// in the future ill replace bottomLine with what's inside it but for now its more
// readable to me.
double bottomLine = (Controller.height - p.rectangle.getHeight());
if (p.rectangle.getLayoutY() < bottomLine) {
p.setPosition(p.rectangle.getLayoutX(), p.rectangle.getLayoutY() + force);
}
}
static void repulseUpward(Particle p, double force) {
p.setPosition(p.rectangle.getLayoutX(), p.rectangle.getLayoutY() - force);
}
}
(if i've forgotton any code that you think would be relevant ill add it)
this gives me the behavior i was hoping for! exceeeeeept if say two shapes are 20 pix wide, and are spaced 20 pix apart, it will still detect a collision and cause themselves to suspend in air. Is there any fix to get around this situation or do i have to scrap the drop method again?
Picture of the problem here
I am developing an algorithm to extract text and images from PDF files in the reading order. I use iText java for this purpose and basically my algorithm works as follows.
Coordinates of every text chunk in the page is extracted using iText.
Rectangle object is created using the extracted coordinates. After this step we have whole bunch of rectangle objects representing actual text chunks in the page.
Group the rectangles into larger text blocks which will be corresponding to the actual columns in the pdf page.
Order the text blocks by Y then X
Apply the locationTextExtractionStrategy for text blocks one by one.
This approach gives my around 80% or slightly more results for the PDF files with medium to complex layouts. I know that it will be almost impossible to gain 100% accuracy because PDF files does not store information in the reading order.
What I want to do is to increase my accuracy here but the problem is iText stops me from doing that. I have identified a problem in iText. It sometimes extract false locations of text chunks which makes my algorithm incorrect. Following images are a good example for that.
You can see that in the actual PDF page there is a clear gap between columns. But the resulting rectangles contains some faulty rectangles in between that gap which prevents me from identifying the correct columns.
Following is the code that I use to extract locations of text chunks.
package com.InteliText.Extract;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.LineSegment;
import com.itextpdf.text.pdf.parser.SimpleTextExtractionStrategy;
import com.itextpdf.text.pdf.parser.TextExtractionStrategy;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
import com.itextpdf.text.pdf.parser.Vector;
/*
* THIS CLASS ACT AS THE TEXT EXTRACTOR FOR THE PREPROCESSOR
*/
public class PreProcessorStrategy extends SimpleTextExtractionStrategy{
private StringBuilder result = new StringBuilder();
private ArrayList<Double> fontSizes = new ArrayList<Double>();
private ArrayList<Double> lineSpaces = new ArrayList<Double>();
private ArrayList<TextSegment> textSegments = new ArrayList<TextSegment>();
Vector previousBaseLine = null;
#Override
public void beginTextBlock() {
// TODO Auto-generated method stub
}
#Override
public void endTextBlock() {
// TODO Auto-generated method stub
}
#Override
public void renderImage(ImageRenderInfo arg0) {
// TODO Auto-generated method stub
}
#Override
public void renderText(TextRenderInfo renderInfo) {
//This code assumes that if the baseline changes then we're on a newline
Vector curBaseline = renderInfo.getBaseline().getStartPoint();
Vector topRight = renderInfo.getAscentLine().getEndPoint();
//System.out.println(renderInfo.getText()+"\t"+curBaseline.get(0)+"\t"+topRight.get(0));
if(curBaseline.get(1) < 800 && curBaseline.get(1) > 50 ) {
// Chunk of text as a rectangle
Rectangle rect = new Rectangle(curBaseline.get(0), curBaseline.get(1), topRight.get(0), topRight.get(1));
double curFontSize = rect.getHeight();
fontSizes.add(curFontSize);
String text = renderInfo.getText();
boolean isBullet = text.contains("•");
if(!(text.equals(" ") || text.equals(" ") || text.equals(" ")) && !isBullet) {
double endX = topRight.get(0);
if(text.endsWith(" "))
endX -= 8;
textSegments.add(new TextSegment(curBaseline.get(0),endX,curBaseline.get(1),topRight.get(1),renderInfo.getText(),curFontSize));
}
result.append(renderInfo.getText());
}
previousBaseLine = topRight;
}
#Override
public String getResultantText() {
// TODO Auto-generated method stub
return result.toString();
}
public ArrayList<TextSegment> getResultantTextSegments() {
return this.textSegments;
}
I use the resulting textSegments ArrayList to create rectangle objects by looking at the coordinates stored in those textSegments. I suspects that this is might be a bug in iText.
As you can see currently I'm shrinking the text chunks a little bit if the content of that text chunk ends with a white space. But this is a temporary fix and I don't want to do that because it shrink the correct text chunks too.
So is there a work around for this one? Or if it is a problem in my code please help me to fix that..
I am assuming here that if you knew where the columns were you could assign each rectangle to the correct column. It looks to me that if you drew a line down the left edge of the right hand column you could assign almost all of the rectangles correctly based on whether their centre was to the right or left of that edge. So the problem is to find the parameters that describe the data best (in particular the left hand edge of the rightmost column) in the presence of outliers.
The absolutely correct way is probably to fit some sort of statistical model, but I think there are a couple of easier hacks that might work.
1) All of the overlapping rectangles in your image seem to be very small. Perhaps you can simply remove rectangles below a given size, work out where the columns should be, and then assign each small rectangle according to whether its centre is to the left or right of the left hand edge of the right hand column.
2) There is a general strategy for fitting data contaminated by outliers you can derive from https://en.wikipedia.org/wiki/RANSAC.
2a) Start by fitting the model to only a small amount of the data. You will be repeating 2a and 2b multiple times, and picking the best result. You are hoping that the initial points chosen for one of these cases are completely free of outliers. Note that if there are N outliers and you divide the data into N+1 chunks, at least one of these chunks must be completely free of outliers.
2b) Once you have an initial fit, look at all the data and work out which points are outliers and ignore them temporarily (i.e. put aside the k worst fitting points). Then fit the model again using the remaining points. In many cases you can prove that if you repeat this step indefinitely it will eventually converge to something, because changing the points identified as the k worst fits improves the fit, as does re-fitting the model, so each iteration improves the fit until you there is no change, at which point you declare that the process has converged.
In my game there's raining meteorites of differnet sizes. I've tried to make it so that if you touch a bigger meteorite, it will be removed (from an array), and a smaller meteorite object will take it's place on the same place as the bigger was removed.
The problem I have is that if you touch a bigger meteorite, it will get removed and go through all other meteorite types until it gets to the smallest one and completely dissapears... all in the same touch. I obviously don't want that. Not sure how to solve this problem either.
Here's the code that handles input:
private void handleInput() {
Iterator<FallingItem> iter = items.meteorites.iterator();
while (iter.hasNext()) {
FallingItem item = iter.next();
if (Gdx.input.justTouched()) {
gameCam.unproject(touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0));
if (item.getClass() == SmallMeteorite.class && item.getBounds().contains(touchPoint.x, touchPoint.y)) {
meteoriteDestroyed.play(0.5f); //play sound
iter.remove(); //removes item from array when it's no longer needed
item.dispose(); //dispose meteorite texture to clear up memory
score += 20; //add to score
} else if (item.getClass() == MediumMeteorite.class && item.getBounds().contains(touchPoint.x, touchPoint.y)) {
meteoriteDestroyed.play(0.5f);
iter.remove();
item.dispose();
score += 10;
items.meteorites.add(new SmallMeteorite(item.getBounds().getX(), item.getBounds().getY()));
} else if (item.getClass() == LargeMeteorite.class && item.getBounds().contains(touchPoint.x, touchPoint.y)) {
meteoriteDestroyed.play(0.5f);
iter.remove();
item.dispose();
score += 10;
items.meteorites.add(new MediumMeteorite(item.getBounds().getX(), item.getBounds().getY()));
}
}
}
}
You're adding the items to the array so they still get iterated on. Instead, you need to mark them somehow so they can be changed after the iteration. There are a few ways to do this.
One way would be to keep a separate List. Clear it before doing this loop, and then add items that need to be replaced with smaller ones to the separate list. When the loop is done on the main list, then you can iterate the separate list to add the smaller versions to the main list.
But LibGDX has an even easier way. Instead of using a List to store your meteorites, use LibGDX's SnapshotArray class. It lets you iterate on a copy of the list, so your changes to the array don't take effect until you're done with iterating:
FallingItem[] array = snapshotArray.begin();
for (int i=0; i<snapshotArray.size; i++){ //important to use snapshotArray.size, not array.length
FallingItem item = array[i];
//...
//if (something) {snapshotArray.removeIndex(i, true); snapshotArray.add(new Something());}
}
array.end();
But before you even try that, think about whether you actually need separate classes for SmallMeteorite, MediumMeteorite, etc. There is definitely such as thing as over-encapsulation. If these objects all behave the same but simply have different parameters, they should probably be a single class whose parameters are modified to represent the different sizes. Then, instead of removing and replacing them in the array, you would only need to change their parameters.
Side note: it's wasteful that you check justTouched and do the unproject inside the loop instead of outside it. A lot of redundant repetition there.
I'm trying to create random items in a libgdx project.I'm relatively new to Java,but here is the code I've come up with for the method.
I've been at this for a week now,and figured I'd ask here for an answer.
I've been trying to come up with something that works first.So please do forgive the shabby code.
The number parameter of the method is the number of items that will be created.
The item just needs to have a random x positon,which is generated within the constraints of the width of the container.
The game is as bottom up scroller,with different platforms being generated.
private Item[] generateRandomItems(int number){
Money[] items=new Money[number];
for(int i=0;i<number;i++){
Random r=new Random();
int x =r.nextInt(120)+3;//136 is the width of the container to which the item is to be generated
Money tempitem=generateMoney(x);//generateMoney() just returns a new instance of the Money class with the created x passed in as a param.
if(i!=0) {
for (int j=0;j<i;j++) {
boolean failed=true;
while (failed) {
//getItem() returns the bounding rectangle/circle f the item
if (!Intersector.overlaps(tempitem.getItem(), items[j].getItem())) {
failed = false;
items[i] = tempitem;
}else{
Random random= new Random();
int newX=random.nextInt(120)+3;
tempitem=generateMoney(newX);
}
}
}
}else{
items[i]=tempitem;
}
}
return items;
}
I don't know if this is a correct way to do it or not,but the created Items do collide sometimes.I've been trying to find what's wrong with the code for sometime now.Any suggestions to improve the code are also appreciated.
Edit::I Know that the code is unnecessarily complicated.This is my first attempt at procedural generation.So please do forgive me.
Instead of generating a new random position if there is a collision, you should move it deliberately left or right until there is no collision. To illustrate the problem reusing random generation after each collision, if you have 10 slots and 9 slots are already filled, it could take a long time to find that open slot using random generation as you would be almost certain to hit the same object numerous times. However, if you keep track of where you’ve checked and deliberately move to a new location each time, then the worst case scenario is you’d hit each object one time before finding the empty slot.
You can check how much of an overlap there is and move the object by that amount to clear the object, then check to make sure it didn’t collide with another object next to it, if it did then keep moving it over until there is a free spot. If you hit the edge of the screen, move to the opposite side and keep moving until you find a free spot.
Also, as good coding practice you should avoid hard coding numbers (like 120+3) into method calls. Since you use the same value in multiple places, if you decide to change the width of your container to 500, then you have to change it in all those places...and if you forget to change one you’re opening yourself up for a nightmare of bug hunting. Instead you can either set an integer such as containerWidth=120 or set the container width directly using container.setWidth(120) and then use container.getWidth() each time you call your random method to get the width of the container the random value is being constrained too. Either way will work fine, whichever is better for your workflow. And I know you said this was quick and sloppy code just to get it going, so you may already be aware of this.
Thanks for the answers.I now know that checking each generated item for collision without saving the previously generated item is bad.
But I got the previous code working after some help,and wanted to share it with anyone who would need it in the future.
I moved the checking part into a new method,and added a new flag to see if the item was generated correctly,after checking for collision from all the items before it.
private Item[] generateRandomItems(int number){
Money[] items=new Money[number];
for(int i=0;i<number;i++){
Random r=new Random();
int x =r.nextInt(120)+3;
Money tempitem=generateMoney(x);
if(i>0) {
boolean generated=false;
while (!generated) {
boolean f = checkIfItemOverlapsWithPrevious(items, tempitem);
if (!f) {
items[i] = tempitem;
generated = true;
} else {
Random random = new Random();
int newX = random.nextInt(120) + 3;
System.out.println("Collided");
tempitem = generateMoney(newX);
}
}
}else{
items[i]=tempitem;
}
}
return items;
}
private boolean checkIfItemOverlapsWithPrevious(Money[] items, Money tempitem) {
for(Money item :items){
if(item!=null) {
if (Intersector.overlaps(tempitem.getItem(), item.getItem())) {
return true;
}
}
}
return false;
}
So I'm just messing around learning to create a Space invaders type game. I can get the bad guys to move, Great!!. Hero moves, Great!! Bullets move, Great!! However I try to remove my bullets once they leave the screen as to not eat up all resources and it force closes on me once it gets rid of the bullet. It goes off the screen. Hits the int of -2 and then we use the remove() and boom. Force Close.
Here is my code. I'm wondering if they access the size() at the same time and just cause a force close because of it.
//I removed everything that doesn't pertane to the bullets.
public class GameScreen{
Bullet bullet = world.bullet;
public GameScreen(Game game) {
super(game);
world = new World();
}
//Draws our bullets.
int bulletLength = bullet.bullets.size();
for(int i = 0; i < bulletLength; i++) {
Placement part = bullet.bullets.get(i);
x = part.x * 32 + 11;
y = part.y * 32;
g.drawPixmap(Assets.bullet, x, y);
}
Class that holds my bullets.
public class Bullet {
public List<Placement> bullets = new ArrayList<Placement>();
public Bullet() {
}
public void shoot(int x, int y){
bullets.add(new Placement(x,y));
}
public void advance(){
int len = bullets.size(); // gets all bullets.
for(int i = 0; i < len; i++) {
bullets.get(i).y --;
if (bullets.get(i).y <= -2){//removes them once they are off the screen.
bullets.remove(i);
}
}
}
This is what I use to keep track of placement.
package com.learning.planecomander;
public class Placement {
public int x, y;
public Placement(int x, int y) {
this.x = x;
this.y = y;
}
}
When going through your list to remove bullets, you can remove bullets from a list but that affects the list immediately instead of after your loop is done. Since you are traversing to the length of the list at the start, you are going off the end of the list since you've removed elements. An example is probably more helpful than that description.
Let's say you have a list with three bullets (which I'll call a, b, c to make the example easier). On a pass through the list, a and c are fine but b needs to be removed.
i = 0;
bullets[0] = a;
bullets[1] = b;
bullets[2] = c;
First loop goes fine, second loop starts like this
i = 1;
bullets[0] = a;
bullets[1] = b;
bullets[2] = c;
We remove b, but the loop keeps going
i = 2;
bullets[0] = a;
bullets[1] = c;
OH CRAP ARRAYINDEXOUTOFBOUNDS! PROGRAM CRASHES!
The way to solve this is to use a temp list to store the bullets that need to be removed, and then once your update loop is finished, make a call to bullets.removeAll(temp)
Doing two passes is a good answer. It makes the loops simpler and easy to understand. If you'd like to do it in one pass though, iterate through the list in reverse order, and when you remove a bullet you can go to the next one and not worry about blasting past the end of the ArrayList.
Alternatively, you can keep your bullets in a linked list, and run through the list with an Iterator, which you can also use to remove items from the list with. Removing from the beginning middle or end of an linked list is always a constant time operation. Whereas removing from the beginning of an ArrayList can be more expensive. If you need random access to the elements in the list, then they can be inefficient. Keep in mind though, if you're only dealing with a handful of objects, then it doesn't really matter.
For bonus points, you might want to put all of your objects in a list, and then have your central loop process them all and have your game objects respond polymorphically to calls like dead?, think, move, draw or whatever you think is appropriate.