Java Gaming collision detection, (side collision) with rectangles - java

I have been writing a Game Engine and I have a problem with my actor class. I want to determine the collision with a rectangle (above) and a side of a rectangle. I had written two methods for them.
public boolean isLeftCollision(Actor actor) {
boolean bool = false;
Rectangle LeftBounds = new Rectangle(x, y, x-velocity, image.getHeight(null));
bool = LeftBounds.intersects(actor.getBounds());
return bool;
}
public boolean isRightCollision(Actor actor) {
boolean bool = false;
Rectangle RightBounds = new Rectangle(x+image.getWidth(null), y, image.getWidth(null)+velocity, image.getHeight(null));
bool = RightBounds.intersects(actor.getBounds());
return bool;
}
Here velocity is the movement for next step.
But they both give me error (ie., false judgements). How can I solve this in the actor class.

I admit I can hardly read your code and I'm sorry if my answer isn't helpful. My guess is that the velocity in the collision produces the errors. Depending on how often you check and what values velocity holds you might register collisions that haven't occured yet...
I'd do the collision detection in two steps.
Test for a collision
Determine if it's above or to one side.
here's some pseudocode:
Rectangle self_shape=this.getBounds();
Rectangle other_shape=actor.getBounds();
bool collision = self_shape.intersects(other_shape);
if(collision){
//create two new variables self_centerx and self_centery
//and two new variables other_centerx and other_centery
//let them point to the coordinates of the center of the
//corresponding rectangle
bool left=self_centerx - other_centerx<0
bool up=self_centery - other_centery<0
}
That way you can look where the other actor is positioned relative to you. If it's above or to one side.

Remember that the third parameter to Rectangle is the width, not the x of the other side. So, what you really want is probably like this:
public boolean isLeftCollision(Actor actor) {
return new Rectangle(x - velocity, y, velocity, image.getHeight(null))
.intersects(actor.getBounds());
}
public boolean isRightCollision(Actor actor) {
return new Rectangle(x + image.getWidth(null), y, velocity, image.getHeight(null))
.intersects(actor.getBounds());
}
(Assuming velocity is the (positive) distance to move left or right, and only the method for the direction you are moving will be called)

Related

My game collision system doesn't work on more than one tile

I'm working on a collision system for my game, however I can't get it to work, if I add more than one wall (which is the object I'm rendering) the collision system doesn't work and I can get through the block.
However if I leave only one wall the collision works correctly, or if at the end of the loop I add a break;
the collision works but only on the first wall of the map, the others don't get the collision.
Would anyone know how to solve this? I've been trying to solve it for 2 days and I couldn't.
public boolean checkCollisionWall(int xnext, int ynext){
int[] xpoints1 = {xnext+3,xnext+3,xnext+4,xnext+3,xnext+3,xnext+4,xnext+10,xnext+11,xnext+11,xnext+10,xnext+11,xnext+11};
int[] ypoints1 = {ynext+0,ynext+8,ynext+9,ynext+11,ynext+12,ynext+15,ynext+15,ynext+12,ynext+11,ynext+9,ynext+8,ynext+0};
int npoints1 = 12;
Polygon player = new Polygon(xpoints1,ypoints1,npoints1);
Area area = new Area(player);
for(int i = 0; i < Game.walls.size(); i++){
Wall atual = Game.walls.get(i);
int[] xpoints2 = {atual.getX(),atual.getX(),atual.getX()+16,atual.getX()+16};
int[] ypoints2 = {atual.getY(),atual.getY()+16,atual.getY()+16,atual.getY()};
int npoints2 = 4;
Polygon Wall = new Polygon(xpoints2,ypoints2,npoints2);
area.intersect(new Area(Wall));
if(area.isEmpty()){
return true;
}
}
return false;
}
I'm pretty sure the problem is this call:
area.intersect(new Area(Wall));
Here's the JavaDoc for that method:
public void intersect(Area rhs)
Sets the shape of this Area to the intersection of its current shape
and the shape of the specified Area. The resulting shape of this Area
will include only areas that were contained in both this Area and also
in the specified Area.
So area, which represents the shape of your player, is going to be modified by each test with a wall, which is why it's only working with one wall.
You could fix the issue by simply making the player Area the argument of the call, as in:
Area wallArea = new Area(Wall);
wallArea.intersect(area);
if(wallArea.isEmpty()){
return true;
}
By the way, this logic is reversed isn't it. Don't you want to check that the resulting area is not empty, i.e. there was an overlap between the player and the wall?
The other option, if each Wall is actually a rectangle, would be to use the this Area method instead:
public boolean intersects(double x,
double y,
double w,
double h)
Tests if the interior of the Shape intersects the interior of a
specified rectangular area. The rectangular area is considered to
intersect the Shape if any point is contained in both the interior of
the Shape and the specified rectangular area.
Something like this:
if(area.intersects(atual.getX(), atual.getY(), 16, 16)) {
return true;
}
As this avoids the creation of an Area object for each wall, and the intersection test is going to be much simpler, and faster.

Java: Iteration to find a coordinate point that hasn't been used?

I am using the following method to try to find a point (coordinate) that hasn't been previously used, and isn't within the bounds of items that have previously used and coordinates.
The way it works is I am rendering "bases" (RTS top-down game), and I am creating two random variable locations for x and y. I pass these, along with the bases texture, into the following method. The method loops through a list of rectangles that are the rectangles of each previously rendered base. If the point is within any of the rectangles, the method is called again using a different set of coordinates. It does this until it finds a set that isn't within a rectangle. It then adds a new rectangle to the list at these coordinates, and returns them so the game can render a new base.
However, the bases still overlap.
Here is the method:
private Point getCoords(int x, int y, Texture t){
for (int i=bases.size()-1; i> -1; i--) {
if (bases.get(i).contains(new Point(x,y))){
x = new Random().nextInt(map.getWidth() * map.getTileWidth());
y = new Random().nextInt(map.getHeight() * map.getTileHeight());
getCoords(x, y, t);
}
}
bases.add(new Rectangle(x,y,t.getImage().getWidth(), t.getImage().getHeight()));
return new Point(x, y);
}
And here is where it is being called:
switch(ran){
default:
int x = new Random().nextInt(map.getWidth() * map.getTileWidth());
int y = new Random().nextInt(map.getHeight() * map.getTileHeight());
Point p = getCoords(x, y, temp);
map.generateBase("air", p.x, p.y);
break;
}
Any ideas what is wrong here?
Thanks
There are several problems:
Your algorithm might be overwritting good coordinates (free ones) with wrong coordinates, you dont have any condition to exit the loop/recursion if you find a good place
You are checking for if rectangle contains the point, but later you are adding a rectanble, so it may not contain the point, but the rectangle created later may collide
try this
private Point getCoords(int x, int y, Texture t){
boolean found = false;
final int width = map.getTileWidth();
final int height = map.getTileHeight();
while(!found) {
x = new Random().nextInt(map.getWidth() * width);
y = new Random().nextInt(map.getHeight() * height);
for (int i=bases.size()-1; i> -1; i--) {
if (!bases.get(i).intersects(new Rectanble(x,y, width, height))){
found = true;
} else found = false;
}
}
bases.add(new Rectangle(x,y,t.getImage().getWidth(), t.getImage().getHeight()));
return new Point(x, y);
}
*** EDIT: Im not sure if I had to use TileWidth and TileHeight or image width and image height for width and height :D
int x = new Random().nextInt(map.getWidth() * map.getTileHeight());
Maybe a bad copy paste. It may be :
int x = new Random().nextInt(map.getWidth() * map.getTileWidth());
In both codes :-D
Okay so after some playing around, I found the issue is the rectangles that are saved are saved with a fixed location which means as the map moves, the rectangles don't. The fix is to loop through each bases and get the base's map position, rather than screen position, and check against this. Also, I found i was checking for a point in a rectangle, which may be outside the rectangle but leaves my bases overlapping still. So i now check for rectangle-rectangle collision instead

Break Out Collision Detection

I'm experimenting with a simple Break Out clone to get an understanding of Game States, Game Loops etc.
One of the things I'm having difficulty with is the collision detection.
I have a GameItem class that is extended by Ball, Brick and Paddle. In this class I have a getBounds() method to return the bounding Rectangle of the object, a collision(GameItem item) method to determine whether 2 GameItem objects have collided as well as methods for hitTop(), hitBottom(), hitLeft() and hitRight() to determine which side the object collided with the other.
These methods are as follows:
public Rectangle getBounds()
{
return new Rectangle(x, y, width, height);
}
public boolean collision(GameItem item)
{
return getBounds().intersects(item.getBounds());
}
public boolean hitTop(GameItem item)
{
return getY()+getHeight()>=item.getY();
}
public boolean hitLeft(GameItem item)
{
return getX()+getWidth()>=item.getX();
}
public boolean hitBottom(GameItem item)
{
return getY()<=item.getY()+item.getHeight();
}
public boolean hitRight(GameItem item)
{
return getX()<=item.getX()+item.getWidth();
}
The ball class has a method called checkBallCollissions(GameItem item) to determine when the ball has hit another object. This is as follows:
public boolean checkBallCollisions(GameItem item)
{
if(this.collision(item))
{
if(this.hitTop(item))
{
this.setVY(-1*this.getVY());
return true;
}
else if(this.hitBottom(item))
{
this.setVY(-1*this.getVY());
return true;
}
else if(this.hitLeft(item))
{
this.setVX(-1*this.getVX());
return true;
}
else if(this.hitRight(item))
{
this.setVX(-1*this.getVX());
return true;
}
return false;
}
return false;
}
Which would be called as follows:
ball.checkBallCollisions(paddle);
As you can see, if it hits one of the 4 edges, the x/y direction is reversed accordingly.
However, this doesn't work. Analysing the code it seems there's an obvious issue that I can't fathom a solution to.
The 3 images below show a collision of the ball with an object from above, below and the right.
With the code as it is, it first detects a collision based on the bounding Rectangles and the intersects() method. It then determines which side it hit. If it hits from above it works. However, when it hits form the bottom or right then hitTop() will return true since getY()+getHeight()>=item.getY() is true for both these cases (i.e the bottom edge of the ball has a greater y value than the top edge of the object it's colliding with).
What conditions am I missing to resolve this and have perfect collision detection?
for circles you use the circle's radius rather than bounds, as bounds is a rectangle.
i believe there are methods for doing that in java but i do not know them yet, so a little research should do it.
also, for advanced collision people tend to use vectors, again i never fiddled with them before so you will have to research that as well.
here is something to get you going:
Java area and intersection
The issue here is that as you mentioned
"hitTop() will return true since getY()+getHeight()>=item.getY() is true for both these cases (i.e the bottom edge of the ball has a greater y value than the top edge of the object it's colliding with)."
In order to fix this you also need a check to ensure that the y position of the circle is less than the y position of the gameobject + height of gameobject (possibly subtracting some offset from it as well).
Which should look something like this (not worrying about offset):
public boolean hitTop(GameItem item)
{
return getY() + getHeight() >= item.getY() &&
getY() < item.getY() + item.getHeight();
}
You will need to do this for all of the collisions.

Collision detection: Shapes overlap even after using intersects method

I am writing a code in which a circle moves randomly in a box and if it collides with any of the small rectangles inside it, it changes its heading direction/ bounces back. I am using the intersects method to find the collision between them. But the circle sometimes overlaps the rectangles rather than bouncing back on contact. I am bouncing back the ball by changing the orientation (180+current_orientation).
I am trying to solve this issue, but did not found any success yet. I read that intersects is finding the match by checking overlap of the bounding rectangles. But, how can I fix this issue. Is this problem due to the intersection or is there any issue with the way i am changing orientation. Any idea?
Code:
private void collisionAvoidanceRobot(int x, int y, int r, double robot_orientation2)
{
boolean collide1=false;
boolean collide2=false;
boolean collide3=false;
boolean collide4=false;
boolean collide5=false;
boolean collide6=false;
r+=5;
Shape collisionrobot=new Ellipse2D.Double(x,y,r,r);
collide1=collisionrobot.intersects(obst1);
if(collide1)
{
robot_orientation=180+robot_orientation;
}
collide2=collisionrobot.intersects(obst2);
if(collide2)
{
robot_orientation=180+robot_orientation;
}
collide3=collisionrobot.intersects(obst3);
if(collide3)
{
robot_orientation=180+robot_orientation;
}
collide4=collisionrobot.intersects(obst4);
if(collide4)
{
robot_orientation=180+robot_orientation;
}
collide5=collisionrobot.intersects(obst5);
if(collide5)
{
robot_orientation=180+robot_orientation;
}
collide6=collisionrobot.intersects(obst6);
if(collide6)
{
robot_orientation=180+robot_orientation;
}
}
public void setXPosition_robot(int x)
{
double distance=0;
distance = unit_moved + randomDouble(0, forwardNoise);
robot_x= (int) (x + Math.sin(Math.toRadians(robot_orientation))*distance);
//System.out.println("Robot_X:"+robot_x);
}
public void setYPosition_robot(int y)
{
double distance=0;
distance = unit_moved + randomDouble(0, forwardNoise);
robot_y=(int) (y+ Math.cos(Math.toRadians(robot_orientation))*distance);
//System.out.println("Robot_Y:"+robot_y);
}
private void createRobot(Graphics2D g)
{
ArrayList<Integer> robot_list= new ArrayList<Integer>();
robot_list=positionRobot(robot_x,robot_y);
robot_x=robot_list.get(0);
robot_y=robot_list.get(1);
setNoise(0.05,0.05,5.0);
//System.out.println("Robot:"+robot_x+"--"+robot_y+"--"+robot_orientation);
adjustRobotOrientation();
collisionAvoidanceRobot(robot_x,robot_y,robot_radius,robot_orientation);
drawRobot(g,robot_x,robot_y,robot_radius);
}
Screenshot:
Does the circle appear to wobble? since you are moving the circle a random distance each iteration, the distance that it moved into the square could be greater than the distance it gets on the next iteration to move in the opposite direction, this would cause the circle to "stick".
also, your collisionAvoidanceRobot could use a for loop instead of all those ifs

Scene2d - Rotated actor not translated as expected

I asked this at the libgdx forums but didn't get a response so I was hoping y'all could help me out:
I have Actors that represent game pieces. What I'm trying to do is make it so the player can click-and-drag the tile to move it around the screen and rotate it multiple times before submitting the placeTile command. From what I understand of DragAndDrop it doesn't seem to be designed with my use case in mind so I figured I'd instead attach a dragListener listener to each game piece (code below). It works well for dragging, except I can't figure out how to set the 'minimum distance before drag starts' to 0... but that's not my main question (though any insights would be appreciated )
Anyway, the big problem comes in when I rotate the actor, and then try to drag it: At 30 degrees rotation, drag acts almost like normal: at 60 degrees, very small movements of the mouse send the actor moving in a tight circle very quickly. Another 30 degrees, the tile actor exits the screen in 1-2 frames, moving in a wide arc. If the actor is rotated clockwise, it's movements are clockwise; same pattern for counter-clockwise.
It looks like the translation of the actor is taking rotation into account; I guess my question is, is it possible to rotate an Actor/Group without the rotation affecting future translations? Alternatively, is there a better way to drag an Actor around the screen based on touch/mouse input? I included some code below: I imagine I'm screwing up something basic, but I can't figure out what:
// during initial stage creation
tileActor.setOrigin(tileActor.getWidth() / 2, tileActor.getHeight() / 2);
tileActor.addListener(new DragListener() {
public void dragStart(InputEvent event, float x, float y,
int pointer) {
chosenTileActor = event.getTarget();
}
public void drag(InputEvent event, float x, float y, int pointer) {
Actor target = event.getTarget();
target.translate(x, y);
}
});
And for the listener that deals with rotation via scrolling mouse wheel:
multiplexer.addProcessor(new InputAdapter() {
#Override
public boolean scrolled(int amt) {
if (chosenTileActor == null)
return false;
else
chosenTileActor.rotate(amt * 30);
return true;
}
});
Any pointers? Am I even going the right direction by using DragListener?
Thanks for reading!
Instead of translating, just set the actor's position directly to the stage coordinates of your drag event:
tileActor.addListener(new DragListener() {
private float offsetX, offsetY;
#Override
public void dragStart(InputEvent event, float x, float y, int pointer) {
Actor target = event.getTarget();
this.offsetX = event.getStageX() - target.getX();
this.offsetY = event.getStageY() - target.getY();
}
#Override
public void drag(InputEvent event, float x, float y, int pointer) {
event.getTarget().setPosition(event.getStageX() - offsetX, event.getStageY() - offsetY);
}
});
I'm computing the offsets in dragStart so that the actor doesn't immediately jump to wherever I clicked when I started dragging (making the drags relative to my mouse). Tested this and it works with any rotation.

Categories

Resources