Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Smooth scrolling collision

Credits go to Edgar Reynaldo, OnlineCop, and weapon_S for helping out!
This thread is locked; no one can reply to it. rss feed Print
Smooth scrolling collision
Lance5057
Member #12,864
May 2011

Ok I'm making a top down style game that uses smooth scrolling that goes 2 pixels per frame. The issue is that the collision doesn't work well. If I put the character on a single tile surrounded by walls he can still wiggle around inside or if he goes around an edge it'll often cut the corner and wind up in the wall. I can't figure out how to fix this no matter what I try.

Movement code

#SelectExpand
1 //+++Key_UP++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 2 if(key[KEY_UP]) 3 { 4 if(levelCurrent[levelx+10][levely+10]==1) 5 { 6 if(y<0){ 7 levely--; 8 y=32; 9 } 10 y-=2; 11 } 12 } 13 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 14 15 //+++Key_DOWN++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 16 if(key[KEY_DOWN]) 17 { 18 if(levelCurrent[levelx+10][levely+11]==1) 19 { 20 if(y>32){ 21 levely++; 22 y=0; 23 } 24 y+=2; 25 } 26 27 } 28 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 29 30 //+++Key_LEFT++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 31 if(key[KEY_LEFT]) 32 { 33 if(levelCurrent[levelx+10][levely+10]==1) 34 { 35 if(x<0){ 36 levelx--; 37 x=32; 38 } 39 x-=2; 40 } 41 } 42 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 43 44 //+++Key_RIGHT+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 45 if(key[KEY_RIGHT]) 46 { 47 if(levelCurrent[levelx+11][levely+10]==1) 48 { 49 if(x>32){ 50 levelx++; 51 x=0; 52 } 53 x+=2; 54 } 55 } 56 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

Drawing code

         //draw levelCurrent chunk
         al_set_target_bitmap(buffer);
         for(b=-1;b<21;b++){
           for(c=-1;c<21;c++){
             if(b*32-y>=0&&
               b*32-y<=640&&
               c*32-x>=0&&
               c*32-x<=640&&
               levelCurrent[c+levelx][b+levely]==1){
               al_draw_bitmap(wall,(c*32)-x,(b*32)-y+32,0);
             }
           }
         }

Any help would be greatly appreciated. Thanks

OnlineCop
Member #7,919
October 2006
avatar

I'm having a hard time understanding what your current movement code is doing. You have your levelCurrent array with both levelx and levely, but there doesn't appear to be any indication of what it does.

You can usually do one of two things:
1) Move your character. See if the character now collides with something; push the character back to the point where it no longer collides
2) Check before the character attempts to move whether something is in the way; go UP TO it, but don't allow it to go INTO it

I've always stored things like walls, other characters, etc. as individual objects, and then just iterate over those objects to see when things are trying to collide.

struct Object
{
  unsigned int x, y;
  unsigned int width, height;
  // ...
};

Create your objects (your walls, your characters, etc.) off of something like this. Then, when you try to move onto it, you test it like this:

for (i = 0; i < number_of_objects_on_screen; ++i)
{
  if (is_collision(my_player, the_object[i], direction_my_player_is_moving) == true)
    // Handle what should happen when it collides, like preventing it from moving
}

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Create a rectangle class. Give your player and your walls a rectangle object. When you do collision, check whether the future player rectangle overlaps the wall rectangles for each wall that your player could be near.

weapon_S
Member #7,859
October 2006
avatar

Separate actions/input from movement/physics; and separate level coordinates from screen coordinates. Make functions/structs/classes that do that for you.

Lance5057
Member #12,864
May 2011

Huh I never actually thought of doing the collision checking separate from the movement. Thanks for your help.

Audric
Member #907
January 2001

No, checking collision as a prerequisite to movement is fine. Many people do the collision check after the movement has taken place, and then they see they can't determine how to move the character back into non-collision place.

About your code, there's certainly an issue with the mix of [levelx+10][levely+10], [levelx+11][levely+10]. What are 10 and 11 ?
In any case, it would be simpler to have x the complete coordinate in pixels, ie: x + levelx*32
This way, the tests look like:

#SelectExpand
1// character_width and character_height are the size of the player in pixels 2if(key[KEY_UP]) 3{ 4 // Check the pixel that is 2px above character: is it on a walkable tile 5 if(levelCurrent[x/32][(y-2)/32]==1) 6 { 7 y-=2; 8 } 9} 10if(key[KEY_DOWN]) 11{ 12 // Check the pixel that is 2px below character: is it on a walkable tile 13 if(levelCurrent[x/32][(y+(character_height-1)+2)/32]==1) 14 { 15 y+=2; 16 } 17} 18if(key[KEY_LEFT]) 19{ 20 if(levelCurrent[(x-2)/32][y/32]==1) 21 { 22 x-=2; 23 } 24} 25if(key[KEY_RIGHT]) 26{ 27 if(levelCurrent[(x+(character_width-1)+2)/32][y/32]==1) 28 { 29 x+=2; 30 } 31}

edit Wait, I noticed my version is too simplified for full x and y freedom of movement. As long as the character is smaller than one tile, you can use checks:

if(key[KEY_UP]) 
{
   // Check the 2 pixels that are above the character's top angles
   if(levelCurrent[x/32][(y-2)/32]==1 && levelCurrent[(x+(character_width-1))/32][(y-2)/32]==1)
    y-=2;
}
if(key[KEY_DOWN])
{
   // Check the 2 pixels that are below the character's bottom angles
   if(levelCurrent[x/32][(y+(character_height-1)+2)/32]==1 && levelCurrent[(x+(character_width-1))/32][(y+(character_height-1)+2)/32]==1)
    y+=2;
}

...
</code>

Lance5057
Member #12,864
May 2011

I'm just gonna post here again since I'm having issues with scrolling still.
Ok so I tried your collision code just on moving up to make sure it would work and it does but only after I've moved completely off the array map. The code doesn't seem to notice that any of the tiles are walkable until you move far enough out.

2nd issue is now whenever I move once I'm fully on a tile I'll jump to the next tile and then I can move normally till I pass another tile. It's kinda hard to explain but it basically looks like I'm jumping a tile everytime the array shifts.

I'll post the new code cause I've changed some things:

Movement Code:

#SelectExpand
1//+++Key_UP++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 2 if(key[KEY_UP]) 3 { 4 if(levelCurrent[x/32][(y-2)/32].walkable==1 && levelCurrent[(x+(32-1))/32][(y-2)/32].walkable==1) 5 { 6 y--; 7 } 8 } 9 10 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 11 12 //+++Key_DOWN++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 13 if(key[KEY_DOWN]) 14 { 15 y++; 16 } 17 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 18 19 //+++Key_LEFT++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 20 if(key[KEY_LEFT]) 21 { 22 x--; 23 } 24 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 25 26 //+++Key_RIGHT+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 27 if(key[KEY_RIGHT]) 28 { 29 x++; 30 } 31 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

Drawing Code:

         //draw levelCurrent chunk
         for(b=0;b<20;b++){
           for(c=0;c<20;c++){
             if(b+(y/32)<21&&
               b+(y/32)>-1&&
               c+(x/32)<21&&
               c+(x/32)>-1&&
               levelCurrent[c+(x/32)][b+(y/32)].walkable==1){
               al_draw_bitmap(wall,(c*32)-x,(b*32)-y+32,0);
             }
           }
         }

weapon_S
Member #7,859
October 2006
avatar

I don't see where your player gets drawn. (Or is it fixed in the center or something?) Am I right to assume the dimensions of your character are also 32x32?
Walkable = wall?

Quote:

(x+(32-1))/32

What does this do... in the code for y directional collision?
Where are all your other collision checks?
Doesn't the rest of your code touch any of the variables presented here?
Are you familiar with the "%" (modulus) operator?
I'd like to see these variables in there: player_tile_pos_x, player_tile_pos_y, player_size_x, player_size_y, tile_size_x, tile_size_y.(Not necessarily named this way.)

Lance5057
Member #12,864
May 2011

Character gets drawn at the center at 320,352. Yes all my sprites so far are 32x32.
Looking at the (x+(32-1))/32 Im not entirely sure what thats use is. It was given to me above and I just used it.
I havent added any other collision checks yet because I wanted to make sure one worked first.
No the x and y variables are only used here.
Yea I just didn't think it would be useful here.

Here's everything I've got so far:

(The size limit wouldn't let me post it all so I added an attachement)

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Lance5057 said:

Looking at the (x+(32-1))/32 Im not entirely sure what thats use is. It was given to me above and I just used it.

x + (32 - 1) is the right edge of your sprite. After dividing it by 32, it is the grid location of the right edge of your sprite.

Files.zip : main.cpp lines 207-213 said:

              if(key[KEY_UP]) 
              {
                if(levelCurrent[x/32][(y-2)/32].walkable==1 && levelCurrent[(x+(32-1))/32][(y-2)/32].walkable==1)
                {
                  y-=2;
                }
              }

This code tests whether the future position of the top left and top right corners of your sprite is walkable.

You need to do the same thing for the other three directions. When moving down, test the bottom left and bottom right corners of your sprite against their grid locations. When moving left, test the top left and bottom left corners of the sprite. When moving right, test the top right and bottom right corners of the sprite.

Lance5057
Member #12,864
May 2011

Ok I found the error in the collision. I forgot to add in the characters x and y so it was him colliding not the top left corner of the screen.

I'm still having the issue where the map jumps whole tiles on occasion though.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Go to: