Page 1 of 2

Opinions on floor collision

Posted: Thu Dec 18, 2008 4:07 pm
by tokumaru
Hey everyone. I'm having some trouble coming up with a simple set of rules to implement decent floor collision. The problem is mainly about slopes, and I'll present it with a series of sketches.

Code: Select all

  \
    \
      \          +---+
        \        |   |
          \      |   |
            \    |   |
              \  |   |
                \o---+
                  \
                    \
                      \
                        \
This is how the regular hitbox of the player would collide with a slope. This is not the desired behavior, as the art for the character would appear to not be standing on the floor (not without special angled sprites, at least).

In order to fix the problem above, we can shift the collision data to the left by half the character's width when a slope is detected (slopes can be detected when one of the edges of the hitbox is on the ground but the other is not) in order to center it and make it look more stable:

Code: Select all

  \
    \
      \       +---+
        \     |   |
          \   |   |
            \ |   |
       +-> \  |   |
       |     \+-o-+
       |       \  \
   displaced        \
 collision map        \
                        \
Now, although the hitbox is colliding with the displaced version of the ground at the exact same point as before, because it is displaced it looks more correctly placed than before. This is great because every object, regardless of it's width, will seem to have it's central point colliding with the ground, as opposed to the quick fix of using art that differs from the collision data in hopes to compensate the bad positioning (but this would only work well for objects of a certain width).

Now, the fix proposed above should work well for slopes, but there are a couple of problems caused by it. Look at this:

Code: Select all

     \
       \                   +---+
         \                 |   |
           \               |   |
             \             |   |
               \           |   |
                 \---------o-+-+
                             |
                             |
                             |
                             |
In this case, we do not want the character to fall yet, because it'd have half of it' body stuck into the wall while doing this. So we must find a way to tell slopes and pits apart, so that the collision data is not displaced on pits. This can probably be detected by looking at the sudden height decrease, so it's not such a big deal. Also, pits must be detected anyway to make the character start falling.

Now here's one thing I have no idea how to deal with:

Code: Select all

  \
    \       +---+
      \     |   |
        \   |   |
          \ |   |
            |   |
            +-o-+
              |
              |
              |
              |
              |
              |
If the slope ends directly in a pit, there seems to be no way to make sure the character has the whole hitbox out of the wall before falling. I've been looking at some games, and Mega Man X on the SNES seems to handle this last case pretty well. On the Armadillo stage, there are some quite good slopes to try this stuff on. When you reach the end of the slope, instead of falling down you keep moving in a straight horizontal line until the whole hitbox is completely free to fall.

This means that Mega Man X behaves exactly as I'd like the characters in my project to. To only problem is that I can't seem to find a decent rule to fix this last problem that will be compatible with the other rules. Does anyone have any ideas? Can anyone think of a different set of rules to make this whole thing work?

This is not for my NES project, but for an ActionScript 3 game I'm making for college. The game doesn't really need to behave like this, but I'll have to deal with this kind of thing anyway later for my personal projects, so I decided I'd use this game as a testing ground. If anyone has anything to contribute, I really appreciate it.

Posted: Thu Dec 18, 2008 5:35 pm
by tepples
Try reading the map's opacity and slope at the bottom center, bottom left, and bottom right of the sprite cel.

Posted: Thu Dec 18, 2008 5:41 pm
by tokumaru
tepples wrote:Try reading the map's opacity and slope at the bottom center, bottom left, and bottom right of the sprite cel.
And then...?

Posted: Thu Dec 18, 2008 6:31 pm
by Disch

Code: Select all

bool IsPlayerOnFloor()
{
  if( IsOnFloor( center_point ) )
    return true;

  if( IsOnFloor( left_corner ) )
    return !IsOnSlopedFloor( left_corner ) )

  if( IsOnFloor( right_corner ) )
    return !IsOnSlopedFloor( right_corner ) )

  return false;
}
I believe that's what tepples was getting at.

EDIT -

Hrm.. I just realized that wouldn't work with your last case there.

Posted: Thu Dec 18, 2008 7:54 pm
by Celius
I was actually considering making ledges a tile type. Look a this:

Code: Select all

\
 \
  \
   \gggggL
        v
        v
        v
\ is a slant, g is a completely solid metatile the player walks on top of, v is a wall, and L is a ledge tile type. Here's how "L" is handled. It acts just as "g" acts, a solid tile the player can walk on top of until the player's left bounding box border crosses the left edge of the L tile type. So it will allow the player to walk all the way off the edge until the left bounding box border crosses a certain point, at which point the player will fall into the pit. Now this idea could be modified, but I'm just throwing it out there.

Posted: Thu Dec 18, 2008 9:17 pm
by Dwedit
So is this the right time to link to Gregmann's blog post about programming MC kids?

Posted: Thu Dec 18, 2008 10:01 pm
by tokumaru
Dwedit wrote:So is this the right time to link to Gregmann's blog post about programming MC kids?
That game isn't such a good example... the collision system is pretty hacky, and the author himself said there are still some problems with terrain collision (go up a hill and watch the player fall one pixel down to the ground at the top).

Anyway, this game seems to heavily rely on block types to make floor collision work properly. This is similar to what Celius suggested and it just might be the answer. Of course I'd like to come up with a system where the shape of the terrain would be enough for decent collision detection, but I guess I may be just dreaming here.

Posted: Thu Dec 18, 2008 10:56 pm
by Celius
It's possible to create collision detection that's shape specific, especially if you're working with 16 pixel wide metatiles (though I don't know if this is what you are working with). For example, take this tile:

Code: Select all

1              1
1              1
11            11
11            11
11            11
111          111
111          111
1111        1111
1111        1111
11111      11111
111111    111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
This is 16x16 pixels, where each 1 is a solid pixel. All you need to do is define the heights of each column of pixels. In this case, you'd define a 16 byte array somewhere in ROM that goes:

.db 0, 2, 5, 7, 9, 10, 11, 11, 11, 11, 10, 9, 7, 5, 2, 0

Those define the relative Y coordinates in the tile of the tops of the columns of pixels. For these types of tiles though, you'd have to check for collision for every pixel along the bounding box edge. This is a major disadvantage because it's really slow.

Posted: Thu Dec 18, 2008 11:16 pm
by tokumaru
I already use that kind of height table, yes. At first I thought I'd just have the central point of the sprite stick to the top of whatever pixel column it was on. However, when standing at the edge of a cliff, one of the corners should prevent you from falling. The problem is that if the central point defined the vertical position of the sprite, once it's out of the floor you can't simply have the corner point take over that role or there would be a "jump" when the position is adjusted to it.

It's really hard coming up with a decent collision system for irregular floors with slopes...! It's no surprise there were so many platformers that used only blocks or at most simple slopes.

Posted: Thu Dec 18, 2008 11:20 pm
by Sivak
Oy. Sorry I'm not contributing, but seeing all this makes me cringe. Hehe.

It was hard enough for me to get 90 and 0 degree platforms to work correctly. Luckily those will be all the platform types I'll use. I wouldn't want to begin thinking about slants. Especially that parabola Celius showed.

Posted: Fri Dec 19, 2008 12:31 am
by Celius
Collision detection on this level is a bit ridiculous, especially if you're dealing with entities wider than the metatile. Though it's very important for some things. I'm probably going to need this kind of table in my game for a completely circular solid platform (like the gears in the clock tower levels in Castlevania 3). This won't be so hard to deal with especially since the platforms are about 3-4 metatiles wide, so the player won't have to deal with mutliple collisions inside a tile, like the one I demonstrated above.

I've actually decided to stick with only a few types of solid metatiles: block, 1/1 (slope) slants, 1/2 slants. Though there are slants in all 4 directions, and I believe Castlevania Symphony of the Night on PS uses about that many tile types. However, I think it has 2/1 slants also, and I may incorporate this sort of thing in my game though I don't think it's that necessary.

Posted: Fri Dec 19, 2008 4:18 am
by Dwedit
There is yet one more way to possibly handle slopes: Inset them by 8 pixels, and use box collision as normal. Then you don't need to concern yourself with the bottom-center of the sprite, just the whole box. Probably not such a hot idea though.

Posted: Fri Dec 19, 2008 9:02 am
by Celius
Generally I think you should check for collision in the exact same way every time: if a collision point on the bounding box is within a certain tile, it has "collided" with that tile, meaning it is inside the tile. For different tile types, you handle collision detection (when the player doesn't just enter a tile, but physically collides with the ground) differently. For example, the player may stop being effected by gravity on a 1/1 slope when the player's midpoint on the bounding box RelativeY >= RelativeX. So in the case of 1/1 slopes, collision detection only applies to the midpoint of the object (so if the midpoint hasn't entered the 2x2 tile, there is no collision detection), but that's all up to the code that determines how something collides for that particular tile type. If you handle tile types separately with different collision conditions, I think you should be fine.

I would really stick with ledge tile types which don't appear on the screen, but like I explained they stick off of the ledge so the player can walk to the very end without falling off beforehand. You can also create 1/1 slope ledge tile type that extends off the ledge like so:

Code: Select all

\
 \
  \
   \
   |\
   |
   |
   |
The code for this tile type would still tell the midpoint to collide with a 1/-1 slant, but only under the condition that the left edge of the bounding box hasn't past the left edge of the ledge tile. That's how you might handle that type of scenario.

Posted: Fri Dec 19, 2008 1:31 pm
by Bregalad
Oh doing slopes really sounds complicated, and while many games didn't do it quite a few did it. When it comes to the NES, Gimmick and SMB3 instantly comes in mind, but it's true not that many NES games have slopes.

You'd want collision to fit into 2 bits or so. '00' means solid, '01' means transparent, '10' for left slope and '01' for right slope or something like that.

And what you said in the very fist post seems like a good solution, but instead of shifting collision data to the left, you should just design metatiles so that their collision slope doesn't match the graphic slope but is 8 pixels ahead. For example a slope will look like that graphically :

Code: Select all

_
 \
  \
   \
    \
     \
      \
       \
        \
         |
         |
         |
         |
         |
But inside the computer for collision it would look like that :

Code: Select all

\
 \
  \
   \
    \
     \
      \
       \_
         |
         |
         |
         |
         |
Using special metatile for slope start and slope end seems ackward and to be a bad idea overall. It's very user unfriendly too.

Oh and all of this makes me feel like I should add slopes to my game engine, but the simple 1-bit collision map barely fitted my metatile definition sheme.

Posted: Fri Dec 19, 2008 2:09 pm
by tokumaru
Bregalad wrote:instead of shifting collision data to the left, you should just design metatiles so that their collision slope doesn't match the graphic slope
Did you catch the part where I said this will only work for a specific sprite width? This is indeed the simplest solution, and might work well for the player, but once I have a huge enemy, like, 3 times wider than the player, walk on the same slope, things will look weird. Also, consider tiny items that might end up completely below the floor line.

Dynamically shifting the collision data (this isn't hard, really, just look a few pixels back instead of where the sprite actually is) on a per sprite basis will result in all of them having their middle points appear to be colliding with the floor. Or simply using the middle point at all times, just considering ledges special cases where the other 2 points (left and right) are considered too.

Both options should work, and the only real problem I see is the last case I presented. Celius' suggestion of extending the slope past the wall might work, but that's still specific to a certain sprite width. Different sprites will need different slope extensions to fall at the correct time.