Dectecting side collisions using rectangles in Java - java

This is my first week of Java, and I'm making a game that involves rectangles bouncing off of each other. The rx and ry are the coordinates of the rectangles, and the velRX and velRY are their x and y velocity. I'm trying to make it so that the rectangles bounce off of each other (reversed y velocity) but continue with the same x velocity if they collide on the top or bottom, and vice versa for the left and right sides. However, I don't know how to detect which side the rectangles collide on. Could I have some help?
relevant code
//Checking for collision between Nemesis and Cop
public boolean checkCollisionOther() {
Rectangle r1 = rect1.getBoundsNemesis();
Rectangle r2 = rect2.getBoundsCop();
if (r1.intersects(r2)){
collision = true;
rect1.velRY = -rect1.velRY;
rect1.velRX = -rect1.velRX;
rect2.velRY = -rect2.velRY;
rect2.velRX = -rect2.velRX;
}
else
collision = false;
return collision;
}

What you want to do is create bounding rectangles for each of the edges of your two game rectangles. Since there are 4 edges to a rectangle, you have 4 bounding rectangles for a rectangle.
You have 4 bounding rectangles for the cop and 4 bounding rectangles for the nemesis. This means you have to do 16 intersects tests in a double for loop.
When one of the intersects tests returns true, you can determine which edge of your cop rectangle and which edge of your nemesis rectangle had the collision.
Here's some code to illustrate how to create bounding rectangles. You can make them wider than 3 pixels if you want.
public List<BoundingRectangle> createBoundingRectangles(Rectangle r) {
List<BoundingRectangle> list = new ArrayList<BoundingRectangle>();
int brWidth = 3;
// Create left rectangle
Rectangle left = new Rectangle(r.x, r.y, brWidth, r.height);
list.add(new BoundingRectangle(left, "left"));
// Create top rectangle
Rectangle top = new Rectangle(r.x, r.y, r.width, brWidth);
list.add(new BoundingRectangle(top, "top"));
// Create right rectangle
Rectangle right = new Rectangle(r.x + r.width - brWidth, r.y, brWidth,
r.height);
list.add(new BoundingRectangle(right, "right"));
// Create bottom rectangle
Rectangle bottom = new Rectangle(r.x, r.y + r.height - brWidth,
r.width, brWidth);
list.add(new BoundingRectangle(bottom, "bottom"));
return list;
}
public class BoundingRectangle {
private Rectangle rectangle;
private String position;
public BoundingRectangle(Rectangle rectangle, String position) {
this.rectangle = rectangle;
this.position = position;
}
public Rectangle getRectangle() {
return rectangle;
}
public String getPosition() {
return position;
}
public boolean intersects(Rectangle r) {
return rectangle.intersects(r);
}
}

Related

Algorithm to verify if a shape is inside another - javafx

I have a rectangular and circle. I need to verify whether a rectangle is inside that circle.
I tried to Shape.intersects but intersects is checked the number 1.
Does anyone know this kind of algorithm in javafx?
Just to exemplifly, in the figure only rectangles 1, 2, 3, and 4 are inside the circle.
thanks for your help.
Solution
The basic idea behind this solution is that any polygon is contained within any convex (see comments) shape iff every point within the polygon is within the shape. The intersects() method that you're attempting to use returns true if at least one point of the polygon is within the shape. You've already figured out that it'll work, but it'll also offer false positives for any partially-intersected shapes. To fix it, we define our own intersection test which looks at all points.
This can be generalized to scan any given polygon for "total intersection" with any given shape:
public boolean totalIntersects(Polygon poly, Shape testShape) {
List<Point> points = flatDoublesToPoints(poly.getPoints());
boolean inside = true; // If this is false after testing all points, the poly has at least one point outside of the shape.
for(Point point : points) {
if(!testShape.intersects(point.x, point.y, 1, 1)) { // The 3rd and 4th parameters here are "width" and "height". 1 for a point.
inside = false;
}
}
return inside;
}
where flatDoublesToPoints() and Point are defined as:
private List<Point> flatDoublesToPoints(List<Double> flatDoubles) {
List<Point> points = new ArrayList<>();
for(int i = 0; i < flatDoubles.size(); i += 2) {
points.add(new Point(flatDoubles.get(i), flatDoubles.get(i + 1)));
}
return points;
}
class Point {
public double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
}
flatDoublesToPoints() is needed to split the "flat" {x1, y1, x2, y2, x3, y3...} polygon lists into a more easy-to-understand data structure. If you're doing tons of comparisons, it may be helpful to skip this step, however, and operate on the "flat list" directly for memory reasons.
Application
The following applies the other methods to a situation extremely similar to yours. (Not exact, because I didn't have your code.)
public class Main extends Application {
public static final int SIZE = 600;
#Override
public void start(Stage primaryStage) throws Exception {
Pane rootPane = new Pane();
List<Rectangle> rects = new ArrayList<>();
for (int j = 0; j < 2; j++) {
for(int i = 0; i < 5; i++) {
Rectangle r = new Rectangle(i * 100, j == 0 ? 0 : 300, 100, 200);
r.setFill(Color.BEIGE);
r.setStroke(Color.BLACK);
rects.add(r);
}
}
rootPane.getChildren().addAll(rects);
Circle circle = new Circle(350, 100, 200);
circle.setStroke(Color.BLACK);
circle.setFill(null);
rootPane.getChildren().add(circle);
List<Polygon> polys = new ArrayList<>();
for(Rectangle rect : rects) {
polys.add(rectangleToPolygon(rect));
}
List<Polygon> intersects = getTotalIntersections(polys, circle);
System.out.println(intersects);
primaryStage.setScene(new Scene(rootPane, SIZE, SIZE));
primaryStage.show();
}
public List<Polygon> getTotalIntersections(List<Polygon> polys, Shape testShape) {
List<Polygon> intersections = new ArrayList<>();
for(Polygon poly : polys) {
if(totalIntersects(poly, testShape)) {
intersections.add(poly);
}
}
return intersections;
}
public static Polygon rectangleToPolygon(Rectangle rect) {
double[] points = {rect.getX(), rect.getY(),
rect.getX() + rect.getWidth(), rect.getY(),
rect.getX() + rect.getWidth(), rect.getY() + rect.getHeight(),
rect.getX(), rect.getY() + rect.getHeight()};
return new Polygon(points);
}
public static void main(String[] args) {
Main.launch(args);
}
}
This code will print the following:
[Polygon[points=[200.0, 0.0, 300.0, 0.0, 300.0, 200.0, 200.0, 200.0], fill=0x000000ff], Polygon[points=[300.0, 0.0, 400.0, 0.0, 400.0, 200.0, 300.0, 200.0], fill=0x000000ff], Polygon[points=[400.0, 0.0, 500.0, 0.0, 500.0, 200.0, 400.0, 200.0], fill=0x000000ff]]
Which is your three polygons labeled 2, 3, and 4.
I don't think that JavaFX will have some special methods for this case.
To draw that circle you need coordinates (X_c, Y_c) of center and radius (R).
To draw rectangles you need to have coordinates ((X_1, Y_1), (X_2, Y_2) etc.) of angle points.
Then all you need is to check if all points of the rectangle is inside of the circle:
(X_1 - X_c)^2 + (Y_1 - Y_c)^2 < R^2
(X_2 - X_c)^2 + (Y_2 - Y_c)^2 < R^2
...
Try this :
import javafx.geometry.Point2D;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
/*
Check if a rectangle is contained with in a circle by checking
all rectangle corners.
For the rectangle to be contained in a circle, all its corners should be
in a distance smaller or equal to the circle's radius, from the circle's center.
Note:
Requires some more testing. I tested only a few test cases.
I am not familiar with javafx. This solution does not take into
calculation rectangle's arc or other attributes I may not be aware of.
*/
public class Test{
//apply
public static void main(String[] args){
Circle circle = new Circle(0 ,0, 100);
Rectangle rec = new Rectangle(0, 0, 50 , 50);
System.out.println("Is rectungle inside the circle ? "
+ isContained(circle,rec));
}
//check if rectangle is contained within a circle
private static boolean isContained(Circle circle,Rectangle rec) {
boolean isInside = true;
//get circle center & radius
Point2D center = new Point2D(circle.getCenterX(), circle.getCenterY());
double radius= circle.getRadius();
Point2D[] corners = getRectangleCorners(rec);
for(Point2D corner : corners) {
//if any corner falls outside the circle
//the rectangle is not contained in the circle
if(distanceBetween2Points(corner, center) > radius) {
return false;
}
}
return isInside;
}
//calculate distance between two points
//(updated a per fabian's suggestion)
private static double distanceBetween2Points
(Point2D corner, Point2D center) {
return corner.distance(center);
}
private static Point2D[] getRectangleCorners(Rectangle rec) {
Point2D[] corners = new Point2D[4];
corners[0] = new Point2D(rec.getX(), rec.getY());
corners[1] = new Point2D(rec.getX()+ rec.getWidth() , rec.getY());
corners[2] = new Point2D(rec.getX()+ rec.getWidth(), rec.getY()+ rec.getHeight());
corners[3] = new Point2D(rec.getX(), rec.getY()+ rec.getHeight());
return corners;
}
}
There is a working simple solution here : https://stackoverflow.com/a/8721483/1529139
Copy-paste here:
class Boundary {
private final Point[] points; // Points making up the boundary
...
/**
* Return true if the given point is contained inside the boundary.
* See: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
* #param test The point to check
* #return true if the point is inside the boundary, false otherwise
*
*/
public boolean contains(Point test) {
int i;
int j;
boolean result = false;
for (i = 0, j = points.length - 1; i < points.length; j = i++) {
if ((points[i].y > test.y) != (points[j].y > test.y) &&
(test.x < (points[j].x - points[i].x) * (test.y - points[i].y) / (points[j].y-points[i].y) + points[i].x)) {
result = !result;
}
}
return result;
}
}

simple Circle on circle Collision libgdx

made two circles one of radius 8(image 16x16)
and one of radius 20( image 40x40)
i am calling the circle over overlap method and the collsion is just off. It is colliding with a circle that is around the 0,0 point of where ever my image of the ball is. the bullet can go within the ball on the bottom and right sides.
public class MyGame extends ApplicationAdapter {
SpriteBatch batch;
Texture ballImage, bulletImage;
OrthographicCamera cam;
Circle ball;
Array <Circle> bullets;
long lastShot;
#Override
public void create ()
{
System.out.println("game created");
ballImage = new Texture(Gdx.files.internal("ball.png"));
bulletImage = new Texture(Gdx.files.internal("bullet.png"));
cam = new OrthographicCamera();
cam.setToOrtho(true,320,480);//true starts top right false starts top left
batch = new SpriteBatch();
ball = new Circle();
ball.radius=20;
ball.x=320/2-ball.radius; // half screen size - half image
ball.y=480/2-ball.radius;
bullets = new Array<Circle>();
spawnBullet();
/*
batch.draw(bulletImage,bullet.x,bullet.y);
bullet.x++;
bullet.y++; */
}
public void spawnBullet()
{
Circle bullet = new Circle();
bullet.radius=8;
bullet.x=0;
bullet.y=0;
bullets.add(bullet);
lastShot = TimeUtils.nanoTime();
}
#Override
public void render ()
{
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
cam.update();
batch.setProjectionMatrix(cam.combined);
batch.begin();
batch.draw(ballImage,ball.x,ball.y);
for(Circle bullet: bullets)
{
batch.draw(bulletImage, bullet.x, bullet.y);
}
batch.end();
if(Gdx.input.isTouched())
{
Vector3 pos = new Vector3();
pos.set(Gdx.input.getX(), Gdx.input.getY(),0);
cam.unproject(pos);
ball.y = pos.y - ball.radius;
ball.x = pos.x - ball.radius ;
}
//if(TimeUtils.nanoTime()-lastShot >1000000000) one second
//spawnBullet();
Iterator<Circle> i = bullets.iterator();
while(i.hasNext())
{
Circle bullet = i.next();
bullet.x++;
bullet.y++;
if(bullet.overlaps(ball))
{
System.out.println("overlap");
i.remove();
}
}
}
}
If your bullet and the ball are 2 circles, like you said you don't need an overlap method.
It is simple: 2 circles collide, if their distance is smaller then the sum of their radiuses.
To calculate the distance you need to make a squareroot. This is a pretty expensive calculation, so it would be better to use squared distance and squared sum of radiuses:
float xD = ball.x - bullet.x; // delta x
float yD = ball.y - bullet.y; // delta y
float sqDist = xD * xD + yD * yD; // square distance
boolean collision = sqDist <= (ball.radius+bullet.radius) * (ball.radius+bullet.radius);
Thats it.
Also in your cam.setToOrtho you wrote a cooment:
//true starts top right false starts top left
Thats wrong, it is top left or bottom left. By default it is bottom left, because this is the way a coordinate system works normaly. The top left is, because the monitor addresses pixels starting from top left = pixel 1.
EDIT: this should be the problem: The coordinates you give the batch.draw method are the left lower corner of the Texture by default, if you are using the "y = Down"-System it should be the top left corner (you have to try i am not sure).
The Circles position instead is its center.
To solve the problem you need to adjust the position like this (for "y = Up"-System):
batch.draw(bulletImage, bullet.x - bullet.radius, bullet.y - bullet.radius);
It is possible, that the same formula works also for the "y = Down"-System but i am not sure

Libgdx: how to draw rectangle around group elements of Scene2D

I want to know how to check collision between elements inside a group and other element of the games, in other word, I want to know how to draw a rectangle around elements of the group, cause so far every time I try, the rectangle is always in the wrong position, I tried using stageToLocalCoordinates but the result was always messed up (sometimes I get the rectangle in the correct position but when I move the group the rectangle seems to have a "mirror effect" {move in the opposite direction} )
Use ShapeRenderer..
Make a function in all of your elements of the group that is returning a Rectangle.
public Rectangle getBounds()
{
return new Rectangle(x,y,width,height);
}
Now in the class with your stage, before drawing your elements (actors) draw the rectangle.
shapeRenderer.setProjectionMatrix(stage.getCamera().combined);
shapeRenderer.begin(ShapeType.Filled);
shapeRenderer.setColor(Color.BLUE); // put any color you want
// for each actor of your stage do this
shapeRenderer.rect(actor.getBounds().x,actor.getBounds().y,actor.getBounds().width,actor.getBounds().height);
Edit:
To convert a rectangle to a polygon you can use this method made by me some time ago
public static float[] rectangleToVertices(float x, float y, float width,
float height) {
float[] result = new float[8];
result[0] = x;
result[1] = y;
result[2] = x + width;
result[3] = y;
result[4] = x + width;
result[5] = y + height;
result[6] = x;
result[7] = y + height;
return result;
}
And
Polygon poly=new Polygon(rectangleToVertices(.....));
Put your polygon in your gamescreen class.
In the render method set the polygon position..

Libgdx - Get Intersection Rectangle from Rectangle.overlap(Rectangle)

Is there any way to know the intersection Rectangle area between two Rectangles in libgdx like the Rectangle in c# http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.rectangle.intersect.aspx ?
I need to get the intersection rectangle area between that two rectangles but the overlap method in libgdx only return boolean value whether two rectangles are intersect or not.
I have read Intersector class but it provides nothing to do that.
Indeed, LibGDX does not have this functionality built in, so I would do something like this:
/** Determines whether the supplied rectangles intersect and, if they do,
* sets the supplied {#code intersection} rectangle to the area of overlap.
*
* #return whether the rectangles intersect
*/
static public boolean intersect(Rectangle rectangle1, Rectangle rectangle2, Rectangle intersection) {
if (rectangle1.overlaps(rectangle2)) {
intersection.x = Math.max(rectangle1.x, rectangle2.x);
intersection.width = Math.min(rectangle1.x + rectangle1.width, rectangle2.x + rectangle2.width) - intersection.x;
intersection.y = Math.max(rectangle1.y, rectangle2.y);
intersection.height = Math.min(rectangle1.y + rectangle1.height, rectangle2.y + rectangle2.height) - intersection.y;
return true;
}
return false;
}
You can use the Intersector class.
import com.badlogic.gdx.math.Intersector;
Intersector.intersectRectangles(rectangle1, rectangle2, intersection);
I'd like to add a slight variation to the answer of nEx Software. This one will work even if you want to store the resulting value in one of the source rectangles:
public static boolean intersect(Rectangle r1, Rectangle r2, Rectangle intersection) {
if (!r1.overlaps(r2)) {
return false;
}
float x = Math.max(r1.x, r2.x);
float y = Math.max(r1.y, r2.y);
float width = Math.min(r1.x + r1.width, r2.x + r2.width) - x;
float height = Math.min(r1.y + r1.height, r2.y + r2.height) - y;
intersection.set(x, y, width, height);
return true;
}
Here is an example usege:
Rectangle r1 = new Rectangle();
Rectangle r2 = new Rectangle();
// ...
intersect(r1, r2, r1);

Why is my game collision detection off by 500px?

I'm making something like r-type; I've made spaceship, missiles, aliens, and everything is working except collision detection. It seems to record collision between player and enemy in about 500 pixels before the actual alien comes and says that I lost.
Board.java:
public void checkCollisions() {
Rectangle r1 = al.getBounds();
ArrayList missiles = Craft.getMissiles();
for (int w = 0; w < missiles.size(); w++)
{
Missile m = (Missile) missiles.get(w);
Rectangle m1 = m.getRect();
if (r1.intersects (m1) && al.Alive()) {
al.isAlive = false;
m.visible = false;
}
}
Rectangle c = p.getBounds();
if (c.intersects(r1)) {
lost = true;
}
Craft.java:
public Rectangle getBounds() {
return new Rectangle(x, y, width, height);
}
Enemy.java:
public Rectangle getBounds() {
return new Rectangle(x, y, width, height);
}
As I said - missiles are working perfectly, but collision detection between craft and alien detects in x = 458, while it should in about 1100 - 1200.
It looks like you are creating the Rectangles objects with bad coordinates. Use System.out.println(String s) to print the coordinates the Rectangle is created with and check if they are correct.
I suspect the player's Rectangle has bad coordinates, because the missiles work fine, as you said.
Can you send me the full code of your game? I would like to try it...

Categories

Resources