4
\$\begingroup\$

I have been trying to get my collision code working for hours now but with every resource I have tried my character still jitters. First I tried using the platformer sample but my character still jittered up and down really quickly, even when standing still. Then I took to these forums and found this post and I used his code and eventually found out that in order for his to work you have to:

  1. Move the character's position along the x-axis only.
  2. Check if the character collided with something along the x-axis

Then do the same thing for the y axis. In other words, the character could only move one direction at a time and with my game there are a lot of different forces, velocities, accelerations, et cetera. all pulling on the target at once and this kind of solution didn't really work.

I heavily simplified the code in the sample and ended up with this:

Rectangle playerBounds = GetPlayerBounds(motion.Position.X, motion.Position.Y, characterWidth, characterHeight);

// Get the player's bounding rectangle and find neighboring tiles.
Rectangle bounds = playerBounds;
int leftTile = (int)Math.Floor((float)bounds.Left / TileSize);
int rightTile = (int)Math.Ceiling(((float)bounds.Right / TileSize)) - 1;
int topTile = (int)Math.Floor((float)bounds.Top / TileSize);
int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / TileSize)) - 1;

for (int y = topTile; y <= bottomTile; ++y) // For each potentially colliding tile,
{
    for (int x = leftTile; x <= rightTile; ++x)
    {
        Tile collision = stage.CustomLayer.GetTile(x, y);

        if (collision.Value == Tile.CollisionTile)
        {
            Vector2 depth = Extensions.GetIntersectionDepth(bounds, collision.Bounds); // Determine collision depth (with direction) and magnitude.

            if (depth != Vector2.Zero)
            {
                if (Math.Abs(depth.Y) < Math.Abs(depth.X)) // Resolve the collision along the shallow axis.
                {
                    motion.Position = new Vector2(motion.Position.X, motion.Position.Y + depth.Y); // Resolve the collision along the Y axis.
                }
                else // Ignore platforms.
                {
                    motion.Position = new Vector2(motion.Position.X + depth.X, motion.Position.Y); // Resolve the collision along the X axis.
                }

                bounds = playerBounds; // Perform further collisions with the new bounds.
            }
        }
    }
}

The code produces the jittering effect as well. I have debugged it for hours and I think I found the cause but I do not know how to fix it.

For example, if the character's Y position is 500 and is found to be colliding with a tile with a depth of 5 pixels, he will be pushed to Y position 495. At this time, though, the loop will continue based off of old topTile and bottomTile values and he may still be found to be colliding with those tiles, and will be pushed up to Y position of 490. Next time this function is called though when the character has moved back down to Y position of 495 due to gravity, he won't be colliding because of different initial topTile and bottomTile values and will continue moving downwards.

I believe this is what causes the jittering effect but I do not understand why it is happening to me. As far as I know, I am using the same algorithm as everyone else and their code doesn't produce any jittering. All my tiles are of equal size (32x32) and I am using the same GetIntersectionDepth method found in the platformer sample.

Thanks to anyone who can help me solve this problem.

\$\endgroup\$
5
  • \$\begingroup\$ Switching bounds = playerBounds; to bounds = GetPlayerBounds(motion.Position.X, motion.Position.Y, characterWidth, characterHeight); solved the problem. Thank you. \$\endgroup\$ Mar 13, 2012 at 19:55
  • \$\begingroup\$ That's good. I'll copy the comments to the answer section and delete them. \$\endgroup\$ Mar 13, 2012 at 19:57
  • \$\begingroup\$ Also, I am still interested in your solution. I see the disadvantages of the one I have posted now. However, I have performance concerns with your solution. Wouldn't the checking collision method have to be called twice for all objects in the game? Seems like that could be an issue. \$\endgroup\$ Mar 13, 2012 at 19:57
  • \$\begingroup\$ Indeed it's slower, but unfortunately I have yet to find another way to handle tile based collisions that doesn't start giving problems in some cases. I'd be happy to learn about a more proper solution! But it has to work in every direction and against every wall - think oldschool Zelda type of collision, not platformer. \$\endgroup\$ Mar 13, 2012 at 20:05
  • \$\begingroup\$ Hmm, alright. Well I definitely prefer a proper working solution that's somewhat slower to one that is buggy and faster! I'm going to go and try your solution once more. I doubt collision testing is really going to be the bottleneck regardless. Thanks for all your help. \$\endgroup\$ Mar 13, 2012 at 20:11

1 Answer 1

1
\$\begingroup\$

My solution did not imply that the character can only move in one axis at a time. It can still move in any direction, but you apply forces/accelerations/velocities separately on each axis. But I agree it might not be trivial to add these changes if you already have another system in place. But keep the idea in mind in case you encounter the kinds of problems mentioned in the other post.

As for the jittering problem, I remember I had it too and the solution for me was by doing Math.Round on the player position after applying velocity (I did it before handling collisions, but I'm not sure if order matters). You can see that on my example, line 66, and if I remember correctly it was also present in the original sample in the Update method or something. From your code snippet I can't see if you're already doing it or not, but I recommend it to prevent jittering at certain speeds.

Other than that suggestion, there's something wrong with your code. In particular, since Rectangle is a value type, not a reference type, when you do bounds=playerBounds after moving the player, you're not really pointing to the new bounds of the player. It's still the old value. You need to call GetPlayerBounds() again to recalculate the new bounds for the player.

\$\endgroup\$
4
  • \$\begingroup\$ Just wanted to leave another comment letting you know everything works perfectly now using your system. Thanks again so much. \$\endgroup\$ Mar 13, 2012 at 20:37
  • \$\begingroup\$ @FrankScott Great! By the way, before changing to separate axis handling, can you describe exactly what problems were you having? In my case the character would get stuck hugging the walls. Did that happen to you too? \$\endgroup\$ Mar 13, 2012 at 20:54
  • \$\begingroup\$ With the platformer sample when jumping below a platform you could hug the bottom and move to the right, but not the left. Not sure if this is a big deal or not now considering gravity works but a flaw is a flaw. I don't have much collidable terrain currently on the map so I didn't test much. \$\endgroup\$ Mar 13, 2012 at 21:03
  • \$\begingroup\$ @FrankScott Okay then, that's also the problem I was having, which becomes much more evident when there's no gravity at all, such as a top down zelda type game. Getting stuck when trying to move against a wall is not forgivable. \$\endgroup\$ Mar 13, 2012 at 21:06

You must log in to answer this question.

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