2
\$\begingroup\$

I've been trying to program a game for the past while and I can't ever figure out one thing: collision detection. I can get up+down and left+right to work, but when I put them together, they interfere with each other.

I seriously don't really know how to do it, and I don't want to have to use Box2D or any other premade collision detection system. All I need is something simple and will work in 4 directions.

Here's the code I have now from my Player class:

for (int i = 0; i < blocks.size(); i++) {
            Block b = blocks.get(i);

            if (isCollidingDown(b)) {
                b.doTopCollision(this);
            }

            else if (isCollidingUp(b)) {
                b.doBottomCollision(this);
            }

            else if (isCollidingLeft(b)) {
                b.doRightCollision(this);
            }

            else if (isCollidingRight(b)) {
                b.doLeftCollision(this);
            }

        }

Here's the actual detection code. I know it's long, but just roll with it:

public boolean isCollidingDown(Block b) {
        if ((Float.compare(this.yPos,b.yPos+b.height) <= 0) && (Float.compare(this.yPos,b.yPos)>=0) && (Float.compare(this.yPos+this.height,b.yPos+b.height)>=0)) {

            if (((Float.compare(this.xPos, b.xPos)>=0) && (Float.compare(this.xPos, b.xPos+b.width)<= 0)) || 
                    ((Float.compare(this.xPos+this.width,b.xPos)>=0) && (Float.compare(this.xPos+this.width,b.xPos+b.width)<=0))) {


                    return true;

            }
        }
        return false;
    }

    public boolean isCollidingUp(Block b) {
        if ((Float.compare(this.yPos+this.height,b.yPos) >=0) && (Float.compare(this.yPos+this.height,b.yPos+b.height)<=0) && (Float.compare(this.yPos,b.yPos)<=0)) {

            if (((Float.compare(this.xPos, b.xPos)>=0) && (Float.compare(this.xPos, b.xPos+b.width)<= 0)) || 
                    ((Float.compare(this.xPos+this.width,b.xPos)>=0) && (Float.compare(this.xPos+this.width,b.xPos+b.width)<=0))) {

                return true;
            }
        }
        return false;
    }

    public boolean isCollidingLeft(Block b) {
        if ((Float.compare(this.xPos,b.xPos+b.width) <=0) && (Float.compare(this.xPos,b.xPos)>=0)) {

            if (((Float.compare(this.yPos, b.yPos)>=0) && (Float.compare(this.yPos, b.yPos+b.height)<=0)) || 
                    ((Float.compare(this.yPos+this.height,b.yPos)>=0) && (Float.compare(this.yPos+this.height,b.yPos+b.height)<=0))) {

                return true;
            }
        }
        return false;
    }

    public boolean isCollidingRight(Block b) {
        if ((Float.compare(this.xPos+this.width,b.xPos) >=0) && (Float.compare(this.xPos+this.width,b.xPos+b.width)<=0)) {

            if (((Float.compare(this.yPos, b.yPos)>=0) && (Float.compare(this.yPos, b.yPos+b.height)<= 0)) ||
                    ((Float.compare(this.yPos+this.height,b.yPos)>=0) && (Float.compare(this.yPos+this.height,b.yPos+b.height)<=0))) {

                return true;
            }
        }
        return false;
    }

Note that I had to do this Float.compare() thing because floats aren't accurate when comparing. Also, I'm not using Java2D, I'm using a framework called libgdx. That shouldn't matter though.

What can I do? Thanks!

EDIT: Guys, that's not the point. I know my code is very unprofessional, but the point is that the collisions don't work the way they're supposed to. I just need a 4-way collision system with boxes. That's all.

\$\endgroup\$
4
  • \$\begingroup\$ why are you using floating point math? \$\endgroup\$
    – MeBigFatGuy
    Commented Sep 25, 2011 at 0:18
  • \$\begingroup\$ To be more accurate? Why would I use ints? \$\endgroup\$
    – TheGag96
    Commented Sep 25, 2011 at 2:26
  • 1
    \$\begingroup\$ You should be using doubles, which is even more accurate, and is probably just as quick as float, at least if you're on a 64-bit system... \$\endgroup\$ Commented Sep 25, 2011 at 20:50
  • 1
    \$\begingroup\$ @Paxinum There's really no need to use doubles, floats are sufficiently accurate as long as the number stays relatively small. I would also recommend that you rename your functions as they don't make sense : 'if (isCollidingDown(b)) b.doTopCollision(this);' is just confusing. \$\endgroup\$ Commented Oct 6, 2011 at 8:30

4 Answers 4

1
\$\begingroup\$

I'm not entirely clear on what you're trying to do here. It looks like you only want one of the doXCollision functions to be called, but a lot of collisions are going to intersect two sides of your object at once.

Assuming the Player class inherits from the Block class, I would add four functions for clarity:

public float Left()   { return xPos;}
public float Right()  { return xPos + width;}
public float Bottom() { return yPos;}
public float Top()    { return yPos + height;}

then you can rewrite your collision function:

for (int blockIndex = 0; blockIndex < blocks.size(); ++blockIndex) 
{
    Block b = blocks.get(blockIndex);

    if (b.Left() > this.Right()) break;
    if (b.Right() < this.Left()) break;
    if (b.Bottom() > this.Top()) break;
    if (b.Top() < this.Bottom()) break;

    //Collision

    if (b.Bottom() < this.Top() && b.Top() > this.Top()) isCollidingUp();
    if (b.Top() > this.Bottom() && b.Bottom() < this.Bottom()) isCollidingDown();
    if (b.Left() < this.Right() && b.Right() > this.Right()) isCollidingRight();
    if (b.Right() > this.Left() && b.Left() < this.Left()) isCollidingLeft();

    //One object is inside the other - add your solution here
}

Apologies for any synax problems in the Java, it's not really a language I'm familiar with. I think I also missed the case where this is totally inside box, but you should be able to work it out.

\$\endgroup\$
2
  • \$\begingroup\$ why have you not subtracted the widths and heights from left and bottom? and why would you need to check if one object is inside another because surely that would be impossible if your checking all of the sides? \$\endgroup\$
    – SD1990
    Commented Oct 5, 2011 at 13:42
  • \$\begingroup\$ From the original code, you can see that the positions are for one corner of the object (I assumed bottom left, but it doesn't really matter). If you want to store the position as the centre of the object, you should be using half width and half height to make your life easier. One object can be inside another if they move fast enough. Collision detection is done once per timestep, and a small fast object could easily get inside a larger one without being in a position where it intersects the edge. In fact, small fast objects are a right pain in this sort of collision detection. \$\endgroup\$
    – SimonW
    Commented Oct 5, 2011 at 16:13
0
\$\begingroup\$

What sort of collision detection are you after? just a simple box v. box collision detection (AABB)? If so it is just a simple case of checking to see if the extremities of one box are within the extremities of another and vica versa.

This is just a case of seeing if values are less than or greater than?

Of you can do circle v circle collision detection, where the shapes are approximated by circles, in this case the distance between the centre of each of the circles are calculated which is a simple test (calculating the distance between two points) and if this distance is less than both radius's of the circles added together then they are in collision.

Another good one to use which is slightly more complex is to conduct an on left test, google should have some good suggestions for this, this is good as it can work with complex shapes.

\$\endgroup\$
0
\$\begingroup\$

A very quick look at your code suggests to me a very simple cause of your error. You have "else" between all the conditions. That means if you are colliding up or down, the left or right condition will never be checked. I think you want to remove the "else" before the isCollidingLeft test.

\$\endgroup\$
0
\$\begingroup\$

The key is not to complicate matters and it seems like you did.

 for (int i = 0; i< blocks.length; i++)
    {
        Block b = blocks.get(i);


            return false;

        //bottom y
        if((this.ypos + this.height/2) > (b.ypos - b.height/2))
            b.doBottomCollision(this);


        //top y
        if((this.ypos - this.height/2) < (b.ypos + b.height/2))
            b.doTopCollision(this);


        //right x
        if((this.xpos + this.width/2) > (b.xpos - b.width/2))
            b.doRightCollision(this);

        //left x
        if((this.xpos - this.width/2) < (b.xpos + b.width/2))
            b.doLeftCollision(this);

    }

Im basing all of this on the normal coordinate system. (top left 0,0)

Another note would be to try and use pointers.

A better way or initailizing the Block would be something like:

Outside the loop.

Block *block = new Block();

Block**blocks = block*[];

blocks[0] = new Block();
blocks[1] = new Block();
etc...

you can loop through if you want.

then in your loop you can do ...

blocks[i];

instead of :

blocks.get(i);

Although not sure you can fully do this in Java... On an ending note with the code above you shouldnt have to write an code for "being inside another object" considering its impossible if they are squares, although again you havent specified what your actually hitting , or you might have done but i dont do java for gaming.

Edit according to stackoverflow ".length" is better than ".size()", especially in arrays

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .