gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > Hardness maps and background collisions.

#4874 - cooky - Sun Apr 13, 2003 7:55 pm

I want to know if people still use hardness map on the GBA. (If anyone on this forum knows what they are)

If you don't they are an exact copy of a background map with areas of it coloured in a certain colour.
Testing for that colour (in respects to the main character) will tell you whether you are colliding with that area of the background or not.

If any one has some code that does hardness map and an explination of how that code works would be handy.

#4879 - pollier - Sun Apr 13, 2003 11:53 pm

I doubt many people are using it on the GBA; most of the time it's overkill and, unless you've got a really straightforward sidescroller, isn't very technically feasible. I remember a post somewhere about using affline registers to get a linear pixel buffer in tile modes--that would probably be helpful... info, anyone?
_________________
(Works for me!)

#4889 - sgeos - Mon Apr 14, 2003 5:05 am

This might be useful:

unsigned long bit_squasher(unsigned long val)
{
val = (val & 0x33333333) | ((val & 0xCCCCCCCC) >> 2);
return (val & 0x11111111) | ((val & 0x22222222) >> 1);
}

(Untested!) It can be used used to & pixel data together. Any non-zero pixel values will be turned into 1's. For example:
bit_squash(0x01233210) == 0x01111110

What is the best way, or even just a good way to test if a sprite has collided with a bg?

-Brendan

#4891 - sgeos - Mon Apr 14, 2003 5:36 am

Better yet, setting non-zero pixels to 0xF would work better. This way only one thing needs to be converted.

unsigned long bit_expander(unsigned long val)
{
val |= ((val & 0x33333333) << 2) | ((val & 0xCCCCCCCC) >> 2);
return val | ((val & 0x55555555) << 1) | ((val & 0xAAAAAAAA) >> 1);
}

Again, untested.

-Brendan

#4896 - excessus - Mon Apr 14, 2003 8:20 am

I like to call that "collision map" :) ok, basically how I use that is that I draw a 256 color image that has different colors to represent different walls that things hit. Image is then converted to array using gfx2gba. Usage is rather simply, as you move your sprite(or whatever) on screen/map that is visible you also move the "hot points", ie the points where you should check collision, on the collision map. Hot points are placed inside (maybe center) of the sprite and/or on the outlines of the sprite. Then you just do an "if-check" for each hot point and compare them to the collision map array. The collision map and the real map you are using can/should be same size for convinience. Also this kind of map can be used for creating areas of map where you trigger off stuff rather than just collide into things. Of course this is bit too comprehensive collision detection for side scroller shooter. But some games do certainly need quite precise collision detection (pinball comes into mind as i'm trying to code such :).

below few feeble examples trying to explain my mumblings:
actual sprite:

###
###
###

numbers representing hot points and their location:

#1#
203
#4#

actual positions :
sprite position : sprite_x = 10, sprite_y = 12 (upper left corner of the sprite)

hot points:
0 : x = sprite_x + 1, y = sprite_y+1
1: x = sprite_x +1 , y = sprite_y
...etc

checking:
if ( collision_map_array[hot_point_position[0]] == 0x00) // floor color
do_nothing();
else if ( collision_map_array[hot_point_position[0]] == 0x01) // wall color
do_things();

and so forth...

phew hope this made any sense :)
_________________
Current binary for my pinball game demo:
www.lut.fi/~rusakko/gba/flibu.gba

#4903 - cooky - Mon Apr 14, 2003 12:51 pm

Yeh it made perfect sense just how I was going to do it. Thanks.

I'm glad to hear people still use this. Thanks alot for the code.
:)
_________________
Rolling a six is unlikely but how do you know if you have never picked up the dice.
www.ceorron.co.uk

#5943 - blaow2 - Tue May 13, 2003 5:00 pm

Quote:
Usage is rather simply, as you move your sprite(or whatever) on screen/map that is visible you also move the "hot points", ie the points where you should check collision, on the collision map


lets say our collision map is in a 1d array, whats the best way to move the collision map as the real map moves so the hotspots allways stay where they should be(places where we want to check for collisions on the real map). thanks

#5944 - Quirky - Tue May 13, 2003 5:47 pm

You shouldn't need to - you should be keeping track of the "world x y" as well as the actual hardware BG x,y when you scroll. Then you can just use the world x y values to find the background hotspot tile in ROM, as you would to find the next (visible) tile values in rom.

#5945 - sgeos - Tue May 13, 2003 5:57 pm

blaow2 wrote:
lets say our collision map is in a 1d array, whats the best way to move the collision map as the real map moves so the hotspots allways stay where they should be(places where we want to check for collisions on the real map). thanks


You'll have to arrange it into rows, and keep track of how many rows you have, and the width of each row. Do the same with your real map. Here is a diagram:

Code:
  ........
  .....11.
  .22..11.
  .22.....
  ........
  ........

Here the real map has six rows, and each one has a width of eight. The 1d array has two rows each with a width of two.

1 and 2 are what your 1d array contains at times 1 and 2. The dots are your real map. To simplify things don't let the player leave the real map. If you do let them travel off it, you'll have to preform boundry checking and decide what gets loaded if a spot is off the map.

Rereading the thread, I agree that keeping track of the "world x y" should be enough though. Reloading your 1d array will be half as much trouble as testing against the map in the ROM. Testing against the 1d array will be the other half...

-Brendan

#5967 - excessus - Wed May 14, 2003 10:31 am

blaow2 wrote:
Quote:
Usage is rather simply, as you move your sprite(or whatever) on screen/map that is visible you also move the "hot points", ie the points where you should check collision, on the collision map


lets say our collision map is in a 1d array, whats the best way to move the collision map as the real map moves so the hotspots allways stay where they should be(places where we want to check for collisions on the real map). thanks



well lets say you have point in x axis 200 and y 100 in screen that is 240x160 pixels, in 1d array that would be found at position 240 * 100 + 200 = array[24200] ( map_width * position_y + position_x ). Was this what you meant ?
_________________
Current binary for my pinball game demo:
www.lut.fi/~rusakko/gba/flibu.gba

#5998 - tubooboo - Wed May 14, 2003 9:48 pm

another approach to deal with collision is to store an attribute table for each tile on your tilemap that contains information on how this tile affects collision. This method was used in many SNES titles.

Emanuel
_________________
HAM author
http://www.ngine.de

#6084 - blaow2 - Fri May 16, 2003 5:29 pm

Quote:
( map_width * position_y + position_x ). Was this what you meant ?

yes thats exactly what i needed that formula so i know where in the array to check for collision.. thanks everyone for all the tips...

#6205 - blaow2 - Mon May 19, 2003 8:46 pm

Quote:
( map_width * position_y + position_x ). Was this what you meant ?
i tried the above and it seems to be partially working because my sprite does seem to collide, however it does it in empty space off the target(place where it should collide) i supose this is because
im doing this: (map_width*background_y+background_x) to browse for collisions in my 1d collision array, since not only my background moves but also my sprite(my sprite can move without moving the background(jumping) and also moving it(walking)), then to find the real hotspots(places where it should collide) in the 1d collision array(holds collision positions of the real background) it should be taken into acount both the background and sprite x y positions to calculate the position to look for collisions in the 1d collision array. the problem is i dont know how..

maybe someone could help to clarify this for me.. im using a 1024 * 1024 pixel background (128*128) tiles. my sprite is 16 pixels by 16 pixel.
thanks again

#6206 - niltsair - Mon May 19, 2003 9:40 pm

Let's explain how an array works.

A 2d Array like this :
Code:
u8 i,j;
u8 Array2D[3][5];
for(i = 0; i < 3; i++)
    for(j = 0; j < 5; j++)
        Array2d[i][j] = i*10 + j;


This code would yield those value :
00,01,02,03,04,05,10,11,12,13,14,15,20,21,22,23,24,25

If we separate it by line :
Line 0 (00,01,02,03,04,05)
Line 1 (10,11,12,13,14,15)
Line 2 (20,21,22,23,24,25)

As you can see, if you want find an element by acessing it as a 1d array, you would need to do this :
(Line*NbItemPerLine) + Col

So, to reach Line 1, item 4 you do :
( 1 * 3) + 4

Now, for your problem
i'm using a 1024 * 1024 pixel background
That means you have an array of Array[1024][1024]
(if you do collision onf pixel level and not tile level, else it's slightly more complicated)

So to find your sprite X/Y position it's :
PosY = MapMoveY + SpriteMoveY
PosX = MapMoveX + SpriteMoveX

So it'll be :
(MapMoveY + SpriteMoveY)*1024 + MapMoveX + SpriteMoveX

Basicly. But you also have to takes something else into account. The SpriteX/SpriteY origin is Top/Left. This means that the collisoin woudl only occurs once hte sprite is totally over the thing it is suppose to collide on. To correct this, SpriteWidth when moving right and the sprite's Height when moving Down, In this case 16 and 16 :

When moving Right :
(MapMoveY + SpriteMoveY)*1024 + MapMoveX + SpriteMoveX + 16

When moving Down :
(MapMoveY + SpriteMoveY + 16)*1024 + MapMoveX + SpriteMoveX

Or you could use this simple solution, always evaluate the middle of the sprite.
(MapMoveY + SpriteMoveY + 8)*1024 + MapMoveX + SpriteMoveX + 8

Here, hope it'll help you out. Like is said, this solution only apply if you do collision detection on a pixel level, but i think it is more common to do it on a tile level.

#6274 - excessus - Wed May 21, 2003 9:35 am

blaow2 wrote:
Quote:
( map_width * position_y + position_x ). Was this what you meant ?
i tried the above and it seems to be partially working because my sprite does seem to collide, however it does it in empty space off the target(place where it should collide) i supose this is because
im doing this: (map_width*background_y+background_x) to browse for collisions in my 1d collision array, since not only my background moves but also my sprite(my sprite can move without moving the background(jumping) and also moving it(walking)), then to find the real hotspots(places where it should collide) in the 1d collision array(holds collision positions of the real background) it should be taken into acount both the background and sprite x y positions to calculate the position to look for collisions in the 1d collision array. the problem is i dont know how..

maybe someone could help to clarify this for me.. im using a 1024 * 1024 pixel background (128*128) tiles. my sprite is 16 pixels by 16 pixel.
thanks again


I suggest you draw a simplified little cartoon on paper how your pixels (bg and sprites) move on map when you scroll and so on. Atleast it clarified for me the math behind it :)
_________________
Current binary for my pinball game demo:
www.lut.fi/~rusakko/gba/flibu.gba

#6450 - blaow2 - Mon May 26, 2003 5:54 am

thanks for the help, it had really made clear a lot of things. however i still got the same problem, i tried a second test on a smaller background, this time 512*512 pixels (64*64)8 pixel tiles and 16*16 sprites. and im still getting offtarget collisions. well i'll post some of the code i got.. maybe someone could help me and point out wich of the parts looks fishy....

I copy my map data as follows:
map=my map data(u8) 1d array;
CHAR_SHIFT is set to 1
Code:

u16*mapData;
u16* temp;
mapData = (u16*)ScreenBaseBlock(28);
temp = (u16*)map;
    for(int x = 0; x < 64; x++){
      for (int y=0; y<64; y++){
         mapData[y*64+x] = temp[y*64+x];
                                    }
                                    }

Then i fill my collision array //basicly if color is black then collision is set to false for that position (colliding whit everything else but black tiles)
collisionMap its an 64*64 boolean 1d array
[code]
for(int x = 0; x < 64*64; x++){
if((map[x])==0x00)
collisionMap[x]= false;
else
collisionMap[x]= true;
}
each time the sprite/bg moves the position of the background and sprite prior to the movement are stored for later use in the collision method.
which looks something like this:
by, bx=background x and y

[code]
if(collisionMap[((by + sprite.y)/8)*64 + ((bx + sprite.x)/8)])
set background and sprite posistion to what it was before collision

if(collisionMap[((by + sprite.y)/8)*64 + (((bx + sprite.x)+ 15)/8)])
set background and sprite posistion to what it was before collision

if((collisionMap[(((by + sprite.y)+ 15)/8)*64 + ((bx + sprite.x)/8)]))
set background and sprite posistion to what it was before collision

if((collisionMap[(((by + sprite.y)+ 15)/8)*64 + (((bx + sprite.x)+15)/8)]))
set background and sprite posistion to what it was before collision
[/code]

thats basicly it, thanks

#6463 - niltsair - Mon May 26, 2003 2:24 pm

When you fill your collision array, you have to know how Map layout works.

You would think that in a 64x64 Map layout, to reach col say 45, is an easy thing, but it's a little crooked. See, the Gba still works with 32x32 Maps and not 1 big map. So here's the layout for it :

Map0 | Map1
Map2 | Map3

Before being able to reach Map1 column(32-64) you need to have totally parsed Map0 data. Before being able to reach Map2, you need to have parsed Map0 and Map1. Each of those maps contains 32x32 elements. So, your loop that fill your collision array, it should look like this :
Code:

int x,y;

//Map0
for( y = 0; y < 32; y++)
    for( x = 0; x < 32; x++)
        collisionMap[y*64+x] =  (map[(y*32)+x]==0x00);

//Map1
for( y = 0; y < 32; y++)
    for( x = 0; x < 32; x++)
        collisionMap[y*64+x+32] =  (map[(32*32)+(y*32)+x]==0x00);

//Map2
for( y = 0; y < 32; y++)
    for( x = 0; x < 32; x++)
        collisionMap[(y+32)*64+x] =  (map[2*(32*32)+*y*32)+x]==0x00);

//Map3
for( y = 0; y < 32; y++)
    for( x = 0; x < 32; x++)
        collisionMap[(y+32)*64+x+32] =  (map[3*(32*32)+(y*32)+x]==0x00);
 


This code is far from being optimized and hasn't been tested. But should give you an idea or 2. I also think you should check directly your collision with your map, instead of wasting Cpu's time to calculate a collision table, if it's isn't pre-built.

Sprite isn't colliding with color 0, it's colliding with Tile 0, small nuance.

#6476 - blaow2 - Mon May 26, 2003 10:30 pm

thanks, makes sence now, i forgot to mention that i use a rotating background for this, i dont supose this matters since i dont rotate or scale anything, does it?