1
\$\begingroup\$

I know this is both a strange question and a stupid question. But this is an issue I have dealt with in every single game I've made, and I have yet but to find a solution. I can't be the only one who's run into this problem. My google-fu must suck though, because I can't find any information on how to fix this.

Here's the situation:

  • A dynamic object is moving in a certain direction.
  • This dynamic object then hits a static object.
  • This dynamic object now changes the direction, and heads the other way until it hits an object, and then it changes the direction once again. Kind of like paramecia movement, but not exactly. Seems simple enough.

Now here's the issue. I detect the collision, but I can simply inverse the velocity. Using OOP, I have my own class for this object, which performs all movement and actions. So, instead, I have a boolean in this class. A simple boolean that asks whether I'm moving left. If I am, my velocity is negative. I may also need to flip the sprite. If, however, the boolean is false, my velocity is positive. So, all is fine and dandy.

Now, here is where the common issue comes in. I detect collision, and inverse the boolean(object.movesLeft != object.movesLeft). This is performed EVERY time it detects a collision. The issue now comes in. I inverse the boolean and start to move it in the opposite direction; however, it is not quick enough, and before it gets moving, it still detects the collision, reversing the boolean once again, turning in back into its original value.

I have yet to find a good solution to this. I have found workarounds, but often times I still get this issue.

Currently, I am using Box2D for collision detection, so my flexibility for solutions are constrained. Box2D provides a method for when specific collisions are happening, and when the specific collisions end. I tried only changing the boolean when a collision ends, but there was a pretty big issue. It would only run when a collision ended, obviously. It didn't have to be the moving object that I'm trying to inverse, it could be any dynamic object. The obvious issue is that I need some kind of collision to end before the object will inverse the direction.

So anyways, this is the issue I have faced in even simple games such as pong, and really, really need to find a way to solve it. I can't be the only one of us who have run into this issue.

If anybody understands the issue I'm having, or has experienced it themselves, and has a solution, help would be greatly appreciated. Thanks in advance.

\$\endgroup\$
  • \$\begingroup\$ Alright. I typed out an answer explaining a workaround I used, but I can't post it for another 4 hours or so. :) \$\endgroup\$ – user2082169 Apr 21 '14 at 21:52
0
\$\begingroup\$

Alright, so I used another one of my workarounds. Hopefully this one is more stable than others I've used in the past. I'm using Box2D, so I have a custom class implementing a ContactListener so I can apply my own effects on collisions. I'm going to refer to the dynamic object I want to change the direction as a DObject. Alright, so first, I define a boolean for when I first detect the collision.

private boolean firstHitCollision = true;

It will initialized as true.

So, next, I handle when the collision is detected:

@Override
    public void beginContact(Contact contact) {
        Fixture fixtureA = contact.getFixtureA();
        Fixture fixtureB = contact.getFixtureB();

        if (fixtureB.getBody().getUserData().getClass() == DObject.class)
            handleDObjectCollisions(fixtureA, fixtureB);
    }

Now it will run this handleDObjectCollisions method while there is a collision with my DObject.

private void handleDObjectCollisions(Fixture fixtureA, Fixture fixtureB) {
        float[] sides = getSidesArray(fixtureA, fixtureB);

        if (collidesWithLeft(sides) | collidesWithRight(sides)) {
            DObject dObject = (DObject) fixtureB.getBody().getUserData();
            if(firstHitCollision) {
                dObject.moveLeft = !dObject.moveLeft;
                firstHitCollision = false;
            }
        }
    }

Above is the handleDObjectCollisions method. I check if my DObject collides with the left or right sides of another object. If its true, then I check make my DObject that is currently collided with another object. Next, I check if firstHitCollision is set to true. If it is, then I inverse the direction boolean and set firstHitCollision to false. So now the next time I run this, firstHitCollision will be set to false and the boolean won't be inverted again. So that all works fine, but now firstHitCollision will be false for the rest of the game, and the DObject won't be inverted again. Well, to solve this issue, I override the endContact method. As seen below, I check if the object that has ended collision is my DObject object. If it is, I need to set firstHitCollision back to true so the next time it collides with the side of an object, it can inverse the direction.

@Override
    public void endContact(Contact contact) {
        Fixture fixtureA = contact.getFixtureA();
        Fixture fixtureB = contact.getFixtureB();
        if(fixtureB.getBody().getUserData().getClass() == DObject.class)
            firstHitCollision = true;
    }

And there we go. No more sticking to walls, no more rapid inversions of the boolean. However, I feel like this is a workaround. Now I'm wasting more memory with these extra if statements, and it is susceptible to bugs. But, I digress. That's how I will be handling this issue in this project.

Edit: If anybody cares, here's the getSidesArray method, along with the methods to check if it collides with a specific side. The getSidesArray method only gets a rectangle.

// 0-3:FixtureA, 4-7:FixtureB
// Top, Left, Bottom, Right
private float[] getSidesArray(Fixture fixtureA, Fixture fixtureB) {
        float[] sidesArray = new float[8];

        // Collided-with object
        sidesArray[0] = fixtureA.getBody().getPosition().y
                + Constants.BLOCK_SIZE / Constants.METER_FACTOR;
        sidesArray[1] = fixtureA.getBody().getPosition().x;
        sidesArray[2] = fixtureA.getBody().getPosition().y;
        sidesArray[3] = fixtureA.getBody().getPosition().x
                + Constants.BLOCK_SIZE / Constants.METER_FACTOR;

        // Character object
        sidesArray[4] = fixtureB.getBody().getPosition().y
                + Constants.CHARACTER_HEIGHT / Constants.METER_FACTOR;
        sidesArray[5] = fixtureB.getBody().getPosition().x;
        sidesArray[6] = fixtureB.getBody().getPosition().y;
        sidesArray[7] = fixtureB.getBody().getPosition().x
                + Constants.CHARACTER_WIDTH / Constants.METER_FACTOR;

        return sidesArray;
    }

    private boolean collidesWithLeft(float[] sides) {
        if (sides[1] >= sides[7])
            return true;
        return false;
    }

    private boolean collidesWithRight(float[] sides) {
        if (sides[3] <= sides[5])
            return true;
        return false;
    }

    private boolean collidesWithBottom(float[] sides) {
        if (sides[2] >= sides[4])
            return true;
        return false;
    }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.