I am creating a game using Java Swing and Graphics in which I need to sense when the player comes into contact with some obstacles. I am trying to compare when their hitboxes overlap but it seems for some obstacles the exact boxes I see on the screen don't compare exactly to coordinates being compared between player and object. They are close but not exact. Therefore, if the player is slightly to the right or left of the object he will sometimes act as though he touched it even though the hitboxes do not visually overlap. The printed out coordinates show they overlap even though this is not what is shown on screen. I can fix this by adding a "wobble" to the inequalities I am comparing, a relatively small, hard coded value which I have to figure out and is not that precise. Is this an issue with Graphics just not being that exact? Is there a way to fix this?
public boolean isTouching(Player p)
{
if (p.getX()<this.getXHitBox()+this.getWHitBox() && p.getX()+p.getWidth()>=this.getXHitBox() && p.getY()+p.getHeight()>this.getYHitBox() && p.getY()<=this.getYHitBox()+this.getHHitBox())
{
//some stuff
return true;
}
return false;
}
XhitBox is the x value of the hitbox (x + xOffset)
YHitBox is the y value (y+yOffset)
HHitBox is the height value
WHitBox is the width value
hitbox is set for obstacles as such:
hitbox=new Rectangle2D.Float( (float) (x + xOffset), (float) (y + yOffset), w, h);
and drawn like this:
public void drawHitbox(Graphics g)
{
g.setColor(Color.RED);
Graphics2D g2d = (Graphics2D) g;
g2d.draw(hitbox);
}
I expected the sensing to only occur when the hitbox visually overlapped but the hitboxes are just slightly off (wobble= approx. 15) from reality. Why is that?
I tried printing out the coordinates and they are different from what is shown on screen! Even if the player is still slightly more to the right than the obstacle, according to the coordinates he is a little more to the left and touching the obstacle.
ie Obstacle will be to the left with x coordinates 88 (for x + width)
Player will be slightly to the right of the obstacle with coordinates 77. What gives?
This is my solution to this question:
Given a circle represented as (radius, x_center, y_center) and an
axis-aligned rectangle represented as (x1, y1, x2, y2), where (x1, y1)
are the coordinates of the bottom-left corner, and (x2, y2) are the
coordinates of the top-right corner of the rectangle.
Return True if the circle and rectangle are overlapped otherwise
return False.
In other words, check if there are any point (xi, yi) such that
belongs to the circle and the rectangle at the same time.
class Solution {
public boolean checkOverlap(int radius, int x_center, int y_center, int x1, int y1, int x2, int y2) {
for(int i=x1; i<=x2; ){
for(int j=y1; j<=y2; ){
System.out.println((Math.pow(i-x_center, 2) +" "+ Math.pow(j-y_center, 2))+" "+Math.pow(radius, 2));
System.out.println(i+" "+j);
if((Math.pow(i-x_center, 2)+Math.pow(j-y_center, 2))<=Math.pow(radius, 2)){
return true;
}
j += 1;
}
i += 1;
}
return false;
}
}
I am pretty confident that the logic is correct. Ranging from the bottom left corner of the rectangle to the top right point, for every point, I am checking if it lies inside the circle.
If I increase the increment step to anything beyond '1', I see that the code fails for the test cases where the rectangle and circle just 'touch' each other. But having it this way leads to exceeding the time limit in some cases. How do I optimize the code for this logic?
Thanks in advance.
This problem can be simplified. I've found a solution of time complexity O(1) and memory complexity O(1). Instead of checking for every single pixel from the rectangle, you can even only take into consideration the bounds themselves.
As I see it, there are 3 cases:
Circle is fully inside rectangle.
Rectangle is fully inside circle
Circle and rectangle outlines intersect in at least one point. This is the tougher one.
I'll call center of the circle coordinates x0 and y0.
You can simply check the bounding box for the circle, as in, which is the northern most point of the circle(x0,y0-radius), which is the southern-most point of the circle(x0,y0+radius), eastern-most(x0-radius,y0) and western-most(x0+radius,y0). If they all fall within the rectangle, then problem solved.
If a rectangle is fully within a circle, that definitely means that its corners are at a smaller distance from the center of the circle than the radius. Simply check this distance for each corner.
Now comes the hard part.
So, as you have figured out, being in a circle(or intersecting one) means that some point must be at a smaller or equal distance from the center than the radius.
However, we can do an optimization for the rectangle checking part, too. Intersection with a rectangle likely means intersection with at least one of the segments that make the outline of the rectangle. So, in the case of an intersection between a rectangle and a circle, you need to check if there is any segment that would be at a smaller or equal distance from the center of the circle than the radius.
Edit: Also, the reason that your code fails on tests where they barely touch is likely due to floating-point errors. Do not use == (or in this case <=, which is similar) for checking if two floating point(or even a floating-point and integer) values are the same. Math.pow() in Java returns double. Just use normal multiplication for squaring.
In fact, you might want to stay as far from floating-point as possible, unless you can't figure out how to get rid of them and the problem says "0.001 error is acceptable" or something around the lines. They are both slow and prone to errors.
Edit 2: Also, I've written the code to help you aid in understanding this explanation. I've tested it on the site, it works for every test with runtime 1ms and memory usage 37.7Mb.
class Solution {
public boolean checkOverlap(int radius, int x_center, int y_center, int x1, int y1, int x2, int y2) {
//case 1: circle is fully inside rectangle
//check the bounding box of the circle against the rectangle
if(x_center-radius>=x1&&y_center-radius>=y1
&&x_center+radius<=x2&&y_center+radius<=y2
)
return true;
//case 2: checking closest corner against circle bounds
int minX=(Math.abs(x_center-x1)>Math.abs(x_center-x2))?(x_center-x2):(x_center-x1);
int minY=(Math.abs(y_center-y1)>Math.abs(y_center-y2))?(y_center-y2):(y_center-y1);
if(minX*minX+minY*minY<=radius*radius)
return true;
//case 3: checking distances to segments against circle bounds
//Checking distance from a segment to a point is alike checking
//the distance from the line the segment is part of to a point,
//except you have to check if the closest point from the segment
//is actually on the segment. If it isn't, the distance from a
//segment to a point is the minimum distance from one of its
//corners to the point.
if(x1<=x_center&&x_center<=x2)
if(minY*minY<=radius*radius)
return true;
if(y1<=y_center&&y_center<=y2)
if(minX*minX<=radius*radius)
return true;
return false;
}
}
This code could be possibly shortened. However, time complexity-wise, it can't get better than O(1).
I'm working on finding maximum overlap rectangle.
I've tried using following lines of code but it returns with that rectangle is overlaping to other or not
public boolean isOverlapping(Rect r1, Rect r2) {
if (r1.top < r2.top || r1.left > r2.left) {
return false;
}
if (r1.width() < r2.width() || r1.height() > r2.height()) {
return false;
}
return true;
}
I expect output that rectangle 3 is most overlaping to given rectangle. Not list or number of rectangles that overlaping to given rectangle.
A bit of pseudo code to get you going:
for each rect in Rectangle list
overlap = compuateOverlap(rect, givenRect)
In other words: it is relatively easy to actually compute the overlap area for two rectangles. Just do that, and compare the results, and isolate the maximum.
In case you need more guidance how to compute that overlap, see this answer for some inspiration.
Or here, there you find even the exact formula to use to compute the overlap area of two rectangles!
I'm trying to determine whether two rectangles border each other. If they share an edge or part of an edge, then I want to include them, if they only share a vertice then I don't.
I've tried using android android.graphics.Rect, I was hoping that the intersect method would return true giving me a rectangle, with 0 width but the points of the intersecting edge. I'm using andEngine and also tried the collideswith method of org.andengine.entity.primitive.Rectangle however that returns true, even if the rectangle only share one corner vertice.
Is there a nice way of doing this? The only other way I can think of is to try and create a collection of all the edges then see if they're equal or are in someway partly equal.
Here's an image to demonstrate what I want. If I click on rect 1 then I want to return rects 2,3 and 4, but not 5.
"Map":
It sounds like you need a new class to do this. I would take the coordinates of each corner of the rectangles. Then, when you are selecting a rectangle, you can get those adjacent to it by finding them one side at a time. Starting with the top for an example, you check which other rectangles have corners at the same height. From that list, you check to see which ones exist on at least one point between the two top corners. So, if top left is 0,3 and top right is 4,3 then you would look for the list of corners at y=3. From that list you find all corners where 0<=x<=4 and anything that fits will be adjacent. You then do the same thing for each additional side. It should be an easy class to make, but I am not going to write any code as I do not know anything about how you stored your data or how you would reference this in your code. If you need help with that, write a comment.
Write a function to find which rectangles share edges with rectangles within all considered rectangles.
Then, map these rectangles which share edges to one another. An Adjacency List is just a way of representing a graph in code.
Sometimes code is easier to understand, so here's code. I have not tested this, but it should get you most the way there.
Also, I'm not sure what you're end goal is here but here's a question I answered that deals with rectangular compression.
List<Rectangle> allRectangles;
public boolean shareAnEdge(Rectangle r1, Rectangle r2){
int y1 = r1.y + r1.height;
int y2 = r2.y+r2.height;
int x1 = r1.x+r1.width;
int x2 = r2.x+r2.width;
boolean topShared = (y1 == r2.y && r2.x == r1.x);
boolean bottomShared = (y2 == r2.y && r2.x==r1.x);
boolean rightShared = (x1 == r2.x && r2.y==r1.y);
boolean leftShared = (x2 == r1.x && r2.y==r1.y);
if (topShared || bottomShared || rightShared || leftShared) {
return true;
}
return false;
}
public List<Rectangle> findSharedEdgesFor(Rectangle input){
List<Rectangle> output = new List<Rectangle>();
for(Rectangle r : allRectangles){
if(r!=input && shareAnEdge(r, input)){
output.add(r);
}
}
}
public AdjacencyList createGraph(List<Rectangle> rectangles){
AdjacencyList graph = new AdjacencyList();
for(Rectangle r : rectangles){
List<Rectangle> sharedEdges = findSharedEdgesFor(r);
for(Rectangle shared : sharedEdges){
graph.createEdgeBetween(r, shared);
}
}
}
How would I check if an object is inside the bounds of an isometric chunk? for example I have a player and I want to check if its inside the bounds of this isometric chunk.
I draw the isometric chunk's tiles using OpenGL Quads.
My first try was checking in a square pattern kind of thing:
e = object;
this = isometric chunk;
if (e.getLocation().getX() < this.getLocation().getX()+World.CHUNK_WIDTH*
World.TILE_WIDTH && e.getLocation().getX() >
this.getLocation().getX()) {
if (e.getLocation().getY() > this.getLocation().getY() &&
e.getLocation().getY() < this.getLocation().getY()+
World.CHUNK_HEIGHT*World.TILE_HEIGHT) {
return true;
}
}
return false;
What happens here is that it checks in a SQUARE around the chunk so not the real isometric bounds. Image example: (THE RED IS WHERE THE PROGRAM CHECKS THE BOUNDS)
What I have now:
Desired check:
Ultimately I want to do the same for each tile in the chunk.
EXTRA INFO:
Till now what I had in my game is you could only move tile by tile but now I want them to move freely but I still need them to have a tile location so no matter where they are on the tile their tile location will be that certain tile. then when they are inside a different tile's bounding box then their tile location becomes the new tile. Same thing goes with chunks. the player does have an area but the area does not matter in this case. and as long as the X and Y are inside the bounding box then it should return true. they don't have to be completely on the tile.