RPGDXThe center of Indie-RPG gaming
Not logged in. [log in] [register]
 
 
Post new topic Reply to topic  
View previous topic - View next topic  
Author Message
akOOma
Wandering Minstrel


Joined: 20 Jun 2002
Posts: 113
Location: Germany

PostPosted: Sat Dec 20, 2003 12:27 pm    Post subject: What's wrong?? (NPC->Tile collision) [quote]

Here's my collision detection routine (collision between NPCs and nonwalkable tiles). It doesn't work. But I can't see the problem. So, could anyone show me at least the bug in that function??

Code:

short int _engine::npc_tile_collide(_npc npc1) {

   int x1, y1, x2, y2;

   for (int nx = -1; nx < 2; nx++)
      for (int ny = -1; ny < 2; ny++)
         if (!map_data[npc1.x_pos + nx][npc1.y_pos + ny]) {

            x1 = sprite_x_wide * npc1.x_pos + npc1.x_pix;
            y1 = sprite_y_wide * npc1.y_pos + npc1.y_pix;
            x2 = sprite_x_wide * (npc1.x_pos + nx);
            y2 = sprite_y_wide * (npc1.y_pos + ny);

            if (!((x1 > x2 + sprite_x_wide) || (x2 > x1 + npc_sprite_x_wide)))
               if (!((y1 > y2 + sprite_y_wide) || (y2 > y1 + npc_sprite_y_wide)))
                  return 1;

         }
      
   return 0;
   
}

_________________
Keep on codin'
-----------------

-----------------
Just another post to increase my rank...
Back to top  
Bjorn
Demon Hunter


Joined: 29 May 2002
Posts: 1425
Location: Germany

PostPosted: Sat Dec 20, 2003 1:00 pm    Post subject: [quote]

I'd say you might be looking for collisions in places without map data when checking this "!map_data[npc1.x_pos + nx][npc1.y_pos + ny]". Might it be that?
Back to top  
akOOma
Wandering Minstrel


Joined: 20 Jun 2002
Posts: 113
Location: Germany

PostPosted: Sat Dec 20, 2003 1:03 pm    Post subject: [quote]

Just for info:

if map_data[x][y] is true => walkable
if map_data[x][y] is false => not walkable

With this for loop I test all 8 fields arround the npc which he could collide

@Bjørn: No, that ain't the bug. Even when the npc is in the center of the map, this routine doesn't work.
_________________
Keep on codin'
-----------------

-----------------
Just another post to increase my rank...
Back to top  
Rooter
Copyright Infringer


Joined: 09 Feb 2003
Posts: 61

PostPosted: Sat Dec 20, 2003 6:40 pm    Post subject: [quote]

wait so is a 1 returned if their is a collision or if there isnt?

x1 = sprite_x_wide * npc1.x_pos + npc1.x_pix;
y1 = sprite_y_wide * npc1.y_pos + npc1.y_pix;
x2 = sprite_x_wide * (npc1.x_pos + nx);
y2 = sprite_y_wide * (npc1.y_pos + ny);

if (!((x1 > x2 + sprite_x_wide) || (x2 > x1 + npc_sprite_x_wide)))
if (!((y1 > y2 + sprite_y_wide) || (y2 > y1 + npc_sprite_y_wide)))
return 1;

what exactly is the point of this code? as far as i can tell you are testing the sprite against itself... why? that code just looks odd, heh.
Back to top  
Rainer Deyke
Demon Hunter


Joined: 05 Jun 2002
Posts: 672

PostPosted: Sat Dec 20, 2003 8:12 pm    Post subject: [quote]

Let me see if I understand this correctly.

npc1.x_pos is the position of the npc in tile coordinates.
npc1.x_pix is the position of the npc in pixel coordinates relative to npc1.x_pix.
sprite_x_wide is the width of a tile in pixels.
npc_sprite_x_wide is the width of an npc in pixels.
x1 is the calculated position of the top-left corner of the npc in pixel coordinates.
x2 is the calculated position of the top-left corner of the tile in pixel coordinates.

You just want to check if a collision occured.
npc1.x_pix is in the range from -sprite_x_wide to +sprite_x_wide.

Assuming all of this is true:

1. You should use return type 'bool', not 'short'. Minor style issue.
2. You should check if npc1.x_pos + nx and npc1.y_pos + ny are in range before checking !map_data[npc1.x_pos + nx][npc1.y_pos + ny]. Safety issue - not doing so may result in an access violation if the npc is at the edge of a map.
3. You should use >= instead of > in the checks at the bottom of the code. Otherwise an npcs will be unable to squeeze between two tiles if the width of the npc is the same as the tile size.

I can't find any other problems with this code (other than the overall design, which I decline to comment on). Could you be more specific about how this code fails? Could the bug be somewhere else?
Back to top  
Happy
JonA's American snack pack


Joined: 03 Aug 2002
Posts: 200

PostPosted: Sun Dec 21, 2003 12:42 am    Post subject: [quote]

See, you still use tile coordinates because you're still using a map [array]. Why should you change the way you do map collisions?

Code:

/*

    Example is for moving downwards. (along the y axis, assuming 0,0 is
    the top left corner of the screen)
   
    This would probably be put in before your npc movement code.
    Orginally this was for bounding boxes, which would take the bounding box
    into account rather than the actual npc height.
*/

//Check if the bottom of the npc will go into the next tile when rendering.
if( ( (npc.y_offset + npc.movement_speed) + npc.height ) > tile.height )
{
     //It will, so we have to check if the next tile is walkable.
     if(map_data[npc.x_tile][npc.y_tile+1] == WALKABLE_TILE)
     {
         //No collision.         
     }
     else
     {
        //Collision
     }
}


If there was no collision, you'd have to, of course, increment the npc's tile position/pixel offset accordingly.

I'm working off of memory here, so there's probably something I'm forgetting.

But to answer your question, the bug looks like those for loops you got there, I have no idea what the point of those are.
Back to top  
Rainer Deyke
Demon Hunter


Joined: 05 Jun 2002
Posts: 672

PostPosted: Sun Dec 21, 2003 2:45 am    Post subject: [quote]

Happy wrote:

Code:

if( ( (npc.y_offset + npc.movement_speed) + npc.height ) > tile.height )
{
     //It will, so we have to check if the next tile is walkable.
     if(map_data[npc.x_tile][npc.y_tile+1] == WALKABLE_TILE)
     {
         //No collision.         
     }
}

I'm working off of memory here, so there's probably something I'm forgetting.


Yup! Since this is presumably a pixel*pixel (rather than tile*pixel) engine, you may need to check multiple tiles in case the npc is on the boundary between two tiles.

Code:

if( ( (npc.y_offset + npc.movement_speed) + npc.height ) > tile.height )
{
  for (int ix = npc.x_offset / tile.width; ix <= (npc.x_offset + npc.width - 1) / tile.width) {
     if(map_data[npc.x_tile + ix][npc.y_tile+1] != WALKABLE_TILE)
     {
       // At least one collision.         
     }
  }
}


Personally I would avoid the separate x_tile/x_offset coordinates and just store the x coordinate in pixels. That would avoid a lot of potential bugs.
Back to top  
Happy
JonA's American snack pack


Joined: 03 Aug 2002
Posts: 200

PostPosted: Sun Dec 21, 2003 8:07 am    Post subject: [quote]

Rainer Deyke wrote:
Yup! Since this is presumably a pixel*pixel (rather than tile*pixel) engine, you may need to check multiple tiles in case the npc is on the boundary between two tiles.

Code:

if( ( (npc.y_offset + npc.movement_speed) + npc.height ) > tile.height )
{
  for (int ix = npc.x_offset / tile.width; ix <= (npc.x_offset + npc.width - 1) / tile.width) {
     if(map_data[npc.x_tile + ix][npc.y_tile+1] != WALKABLE_TILE)
     {
       // At least one collision.         
     }
  }
}


Personally I would avoid the separate x_tile/x_offset coordinates and just store the x coordinate in pixels. That would avoid a lot of potential bugs.


Erm, sorry. I probably should have clarified, the x and y offset variables in the example code are the pixel offset of the current tile position. That is, when rendering it'd be something like:

Code:
render_sprite(npc.sprite, (npc.x_tile*tile.width) + npc.x_offset, (npc.y_tile*tile.height) + npc.y_offset);


In RNM, I only checked collision on the direction the sprite was moving, assuming that if there was a collision on any of the other directions it would have found it already. Which is rather accurate, and, if you're doing diagonal movement, checking both directions would suffice. Having any kind of loop or multiplication/division in a bounding-box/tile based collision detection routine is just bad design, as there's no need for it.
Back to top  
Bjorn
Demon Hunter


Joined: 29 May 2002
Posts: 1425
Location: Germany

PostPosted: Sun Dec 21, 2003 9:54 am    Post subject: [quote]

In our engine, we also calculate the tile where the player would be going and then check if he can go to that tile. That's a tile*tile engine though.

In the original code, a bounding box is checked against a 3x3 tile block around the npc (or at least I think that's what it means to do). It's not a big loop and I wouldn't call it bad design so quickly. It does assume that the bounding box will be smaller than 3x3 tiles, I think.

A hibrid solution would be to take the direction of the npc and calculate the new part of its bounding box. This would be a thin line at the side, or two on diagonal movement. Then, for each tile within that area, check if it's an obstacle. The efficiency would be lineary dependable on the size of the bounding box.
Back to top  
DrunkenCoder
Demon Hunter


Joined: 29 May 2002
Posts: 559

PostPosted: Sun Dec 21, 2003 11:23 am    Post subject: [quote]

My guess is that the logic if flawed. Im guessing that what you want todo is to check if the sprite overlaps the nonwalkable tile.

That should look something like (insted of the nested ifs):
Code:

return abs(x1 - x2) < sprite_x_wide && abs(y1 - y2) < sprite_y_wide)


Like the code is now Im actually guessing that the nested if's only hold true when the NPC is standing in the middle of a tile (and in wich case the tile is clearly walkable so it will never be tested)
_________________
If there's life after death there is no death, if there's no death we never live. | ENTP
Back to top  
akOOma
Wandering Minstrel


Joined: 20 Jun 2002
Posts: 113
Location: Germany

PostPosted: Sun Dec 21, 2003 11:35 am    Post subject: [quote]

Code:

short int _engine::npc_tile_collide(_npc npc1) {

   int x1, y1, x2, y2;

   x1 = sprite_x_wide * npc1.x_pos + npc1.x_pix;
   y1 = sprite_y_wide * npc1.y_pos + npc1.y_pix;

   for (int nx = -1; nx < 2; nx++)
      for (int ny = -1; ny < 2; ny++)
         if (!map_data[npc1.x_pos + nx][npc1.y_pos + ny]) {

            x2 = sprite_x_wide * (npc1.x_pos + nx);
            y2 = sprite_y_wide * (npc1.y_pos + ny);

            if (abs(x1 - x2) < sprite_x_wide && abs(y1 - y2) < sprite_y_wide)
               return 1;

         }
     
   return 0;
   
}


But now there's another problem. When you run into a not walkable tile from below or from the right, it's ok. If you run from above or from the left into a not walkable tile you'll see the problem.

Here's the compiled exe with sprites and so on: http://cpanel28.gzo.com/~akooma/upload/cRPG.zip

Has anyone got an idea, what's now the problem??
_________________
Keep on codin'
-----------------

-----------------
Just another post to increase my rank...
Back to top  
Post new topic Reply to topic Page 1 of 1 All times are GMT
 



Display posts from previous:   
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum