0
\$\begingroup\$

I'm try to clone a popular 2D platformer as a project. For both the player character and enemies, whenever I constantly check to see if they are walking on the ground or floating platforms, they "vibrate" or "Bounce" instead of walking smoothly, as gravity pushes them downward, and the platform pushes them upward. I have tried searching for this problem, but can't seem to find anything relevant to my problems (which probably means I am searching the wrong thing because I'm sure everyone runs into this problem at some point.)

Essentially, the player and enemies are always effected by gravity, but the collision acts against it and pushes them upward. I tried having a boolean that was set once they appeared to be on solid ground, but this made them float artificially once they walked off of floating platforms.

Here is the code for enemy collisons:

public void EnemyBlockResponder(IEnemy enemy, IBlock block, Rectangle intersection)
{
    enemy.YLocation -= intersection.Height;
}

I'm currently assuming that a side collision with a block will never happen for debugging.

Here is the Update method for the enemy:

public void Update()
{
    if (currentDirection == Direction.LEFT)
    {
        xLocation--;
    }
    else if (currentDirection == Direction.RIGHT)
    {
        xLocation++;
    }
    if (NoFloor())
    {
        yLocation += 3;
    }

    goombaSprite.Update();
}

Where NoFloor is a method that checks all the blocks on camera to see if the enemy is colliding with any of them.

Where is this code causing the sprite to continually bounce? I appreciate any help and criticisms. Thanks.

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

Your bouncing is caused by calculus imprecision, mixed with the time integration component.

This kind of things happen with involvement of these components:

  • contacts detection
  • response impulses
  • integration

Point 1 we don't know who does it in your code. I guess some framework feature, the result of the contact information is given to you in the intersection object so it seems. The Height component has an imprecision you need to aknowledge, and therefore you can't trust it to be a good corrector.

Point 2, you do it on this line enemy.YLocation -= intersection.Height;
this is an exact simplified impulse. You correct the position without going through a force integration step (impulse method) but it is the same thing.
This point too is imperfect, and you can't perfect it, don't strive for it, collision handling in games is not an exact science. But you can make it fault tolerant by introducing softness a bit everywhere. In the form of deltas, epsilons, gradients, spring forces, multi-iteration penetration solving, fixed step simulation, auto freeze, etc etc... lots of little bits of technique there and there that makes your whole collision handling smooth.

Your "gravity" integration yLocation += 3, I guess "freefall" simulation, is also sensitive to the fact that you need to rely on a NoFloor() method that is probably too strict (too correct numerically) which is yet another factor in your bouncing problem.

The final point is the integration, in the code snippets you give us, there is notion of delta time watsoever. You skip framerate issues, or physics simulation rate issues altogether. Maybe this is for snippet purity sake on stackexchange, maybe not. Anyway it matters, and introducing it will have you think about precision even more. Because here it almost seems like you're doing integer calculus, but I think the vector you manipulate must come from xna and therefore be floats ? double precision usually is not needed in absolute, BUT can help in these kind of domains, to increase smoothness.

Determinism, also helps in smoothness, when you fix your simulation timestep. see this link.

To talk a bit more about integration, when you will have introduced your "dt" (stuff that you multiply every unit to integrate into the next unit. Acceleration integrates into speed, and speed integrates into position). You use "dt" to multiply. If you do a simple multiply based on current values, this is called "Euler integration". If you use half of the current value, and half of the previous frame's value, this is called "Verlet integration".

Using Verlet will ALSO buy you smoothness and stability.

But really in your particular case, you need to focus on your "NoFloor()" resolution, this is the main culprit.
You have a feedback loop, because of numerical imprecision, you can't determine that for sure your character is "on the floor" after you corrected a penetration with your -= intersection.Height subtraction. So what you need to do, is cut some lack to your "NoFloor" function so that it says a little bit more often that the character is actually on the floor.

Next step, is to try to correct penetration by progressive and smooth forces integrated over time rather than a super hard direction position correction. Use dampers and spring forces which are evaluated in term of the "distance to ground". And let a few frames fix the position, it will get rid of your bouncing totally. Just make your spring "hardness" (usually noted k, or newton constant, in physics) very high to simulate the fact that you bump against a hard ground, and not some soft trampoline. But With any physics simulation, making so that every collision is solved in terms of soft interactions is always much more stable than truely hard interactions.

As a final point, I made a platformer demo that implements some of the ideas above, and the ground walking is perfectly smooth. You can check the code here.

\$\endgroup\$
2
  • \$\begingroup\$ I appreciate the response. So it seems you're saying that since I am using the intersected area between two sprites to adjust instead of velocty*time to adjust that my character is getting over adjusted, thus why the bounciness occurs? \$\endgroup\$
    – brostone51
    Commented Mar 25, 2015 at 13:22
  • \$\begingroup\$ I think the bouncing occurs because when you adjust, you hit too high, then you have NoFloor is true, then freefall, then intersection, then adjust... so the problem comes from both the readjustment precision, and the NoFloor condition check (too strict). The answer is long because by adopting a set of measures you will remove bouncing, but start by trying to give a tolerance to your NoFloor condition. This way it will create a hystersis zone where you be "on the floor" and yet not "collided". \$\endgroup\$
    – v.oddou
    Commented Mar 26, 2015 at 0:16

You must log in to answer this question.

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