Forum: pixelperfekt collision

Forum huvudsida -> Programmering -> pixelperfekt collision

Sidor: 1

Till botten

Dread 20:59 - 5:e Januari 2009 | Post #1
Medlem
Inlägg: 135


Skicka PM
försöker få igån pixelperfektkollision, men går inte så himla bra. Jag åter uppfann hjulet skulle man kunna säga, för metoden jag trodde jag kommit på kallades visst för bitmask detektering. HGE saknar inbyggd kollisionshantering tyvärr. Har lärt mig massor hittills och tror jag kommer lära mig mer om jag bara får rätt på det.

Hur som helst, jag kom inte på en bra metod för att få ut positionerna så jag kan jämföra rätt bools. Så jag beslöt mig för att använda en färdig kod av Ulf Ekström,
Här en bit ner under 2d Collision detection lib. Tog detta bibliotekt för att det var litet och nätt, tyvärr hittar jag inte så mycket info om detSmiley
"tutorialen" på hemsidan förklarar i allmäna ordalag hur detekteringen går till (grundprincipen har jag förstått), men inte hur man använder biblioteket.
Tanken är ju att man ska sätta en position till true eller false, beroende på om det är fast eller inte, och det är nog det jag har problem med just nu,

  1. #define BITW unsigned long int
  2. #define BITW_LEN 32
  3. #define BITW_MASK 31
  4. #define BITN(n) ((BITW)1 << (n))
  5.  
  6. /* Sets the bit at (x,y) */
  7. static INLINE void bitmask_setbit(bitmask *m,int x,int y)
  8. {
  9. m->bits[x/BITW_LEN*m->h + y] |= BITN(x & BITW_MASK);
  10. }

(INLINE är definierat så att det funkar för alla kompilatorer, högre upp i koden)

Jag fattar inte alls hur den funkarSmiley
Bestämmer jag nånstans i den funktionen om (x,y) är true eller false, 1 eller 0.

så som jag lagt upp min kod, så gör jag en bool array när jag laddar in spriten, och sätter true respektive falsk då. för att inte gå in i texturen under spelets gång (vilket jag misstänker är väldigt prestanda krävande). Jag trodde det "bara" skulle vara att konvertera över det direkt till masken istället, men så verkar inte vara fallet tyvärr.

,,r något oklart eller jag formulerat mig vagt, säg till så förklarar (försöker iaf) jag bättre

,,r extremt tacksam för svar/hjälp, vill gå vidare med spelet asap.

EDIT:
Ulf Ekström verkar ha slutat utveckla biblioteket, men jag tror det funkar bra, har läst om en del spel som utnyttjat det, funderar på att mejla någon av dem som använt sig utav det, vem vet man kanske får svar...

-------------------------
c++

Senast redigerad 21:02 - 5:e Januari 2009


Slash 21:37 - 5:e Januari 2009 | Post #2
Medlem
Inlägg: 141


Skicka PM
Bitarna är antagligen packade i int:ar. Det vill säga m->bits är av typen unsigned int (eller unsigned long om den råkar vara 32 bits på din platfrom)? I alla fall:
  1. m->bits[x/BITW_LEN*m->h + y] |= BITN(x & BITW_MASK);


Sätter alltså en viss bit på en viss plats i bitmasken. Arrayen består av en massa bitar:
  1. index 0: 00000000 00000000 00000000 00000000
  2. index 1: 00000000 00000000 00000000 00000000
  3. index 2: 00000000 00000000 00000000 00000000
  4. ....
  5. index w*h-1:00000000 00000000 00000000 00000000

I tabellform såhär då:
  1. x >
  2. 0 1 2 3 4 5
  3. y 0: 00 06 12 18 24 30
  4. \\/ 1: 01 07 13 19 25 31
  5. 2: 02 08 14 20 26 32
  6. 3: 03 09 15 21 27 33
  7. 4: 04 10 16 22 28 34
  8. 5: 05 11 17 23 29 35


Det x/BITW_LEN*m->h + y gör är att beräkna rätt index i denna array. Det vill säga x/BITW_LEN ger rätt index, eftersom BITW_LEN är 32 (storleken på ett element i arrayen) så ger x/BITW_LEN vilket x-position bit nummer x ligger på. Tar man då x/BITW_LEN*m->h, alltså multiplicerat med höjden så för man rätt element. Då vet vi alltså vilken kolumn det är. Sedan adderas y för att få rätt index för raden också. Ett exempel:

Säg att du vill indexera x=67 och y=4 i arrayen ovan. Sätt in i formeln: x/BITW_LEN*m->h + 4 = 67/32*6 + 4 = 16. Då bör alltså index vara 16. Slår man upp i tabellen ovan x=67/32=2, y = 4 så ser vi index 16.

Index 16 innehåller då alltså 32 stycken bitar: 00000000 00000000 00000000 00000000. Det |= BITN(x & BITW_MASK) gör är att sätta rätt bit till 1 i denna array. Alltså m->bits[16] |= BITN(67 & BITW_MASK) är lika som m_bits[16] |= BITN(3) som i sin tur är lika som m_bits[16] |= 1 << 3. Resultat blir alltså:
00000000 00000000 00000000 00000100.

Det x & BITW_MASK gör är alltså detsamma som x MOD 32 fast snabbare. Detta fungerar bara för jämna potenser av 2, som 32 råkar vara.

Hoppas det var nåt åt det hållet du ville ha svar på, annars har jag skrivit en massa blaj i onödanSmiley

-------------------------
Ingen sigantur!



Dread 22:06 - 5:e Januari 2009 | Post #3
Medlem
Inlägg: 135


Skicka PM
  1. typedef struct bitmask
  2. {
  3. int w,h;
  4. BITW *bits;
  5. } bitmask;


glömde posta att bistmask structen ser ut så här.

Och nej var inte i onödan nu begripper jag lite bättre hur koden fungerar iaf.

Så om jag förstått det rätt, så ska jag bara ta setbit på de positioner som ska vara true, dvs kollision? Annars låter jag bli att köra funktionen?



-------------------------
c++



Slash 01:01 - 6:e Januari 2009 | Post #4
Medlem
Inlägg: 141


Skicka PM
Utan att ha undersökt saken vidare så låter det som att det är precis vad du ska göra.

Alltså typ:
  1. for (int y = 0; y < image->height; ++y)
  2. {
  3. for (int x = 0; x < image->width; ++x)
  4. {
  5. if (imageCanCollideAt(image, x, y)) // Hur du nu får reda på vilka pixlar som kolliderar
  6. {
  7. bitmask_setbit(my_bitmask, x, y);
  8. }
  9. }
  10. }


-------------------------
Ingen sigantur!



Dread 18:38 - 6:e Januari 2009 | Post #5
Medlem
Inlägg: 135


Skicka PM
Funkar tyvärr inte som tänkt, har ingen anning vad som gått fel, den rapporterar krock någorlunda korrekt om man går med sprite:en uppåt eller neråt mot föremålet man ska krocka med, men kommer man från höger eller vänster så skiter det sig. blir små hack på sina ställen om man kommer uppifrån/nerifrån, och vid de yttre kanterna så åker den rakt igenom det den ska kollidera med. Kommer man från sidorna åker sprite:n rakt igenom kollisionsobjektet.Smiley

tycker den borde rapportera krock även om sprit:en är mitt i den andra sprite:n (vilket den är när den åker rakt igenom).

Kan det vara skillnad på spritensstorlek och texturensstorlek? Det borde vara samma ifall ja inte modifierat sprite:n och/eller texturen. (ska testa byta till texture height etc... istället för sprite height)

t.ex. denna Balloon fight så nåt måste jag ju göra fel, har skickat ett meddelande till personen i fråga (som gjort balloon fight) får se ifall jag får svar. Hoppas på det, såg att han använde version1.7 alpha av bitmask biblioteket.

Finns det några andra små kollisionshanteringsbibliotek där ute? Helst som funkar tillsammans med hge och fortfarande har support?

tack för all hjälp hittills!

-------------------------
c++

Senast redigerad 18:38 - 6:e Januari 2009


Dread 19:57 - 6:e Januari 2009 | Post #6
Medlem
Inlägg: 135


Skicka PM
Fasikens, den ska funka har räknat pixlar nu också, den går in i bitmask_setbit varje gång som alpha är skilt från 0.

min kod för att jämföra:

  1. void gameobject::setHitTable()
  2. {
  3. DWORD * indexColor = hge->Texture_Lock(mSprite->GetTexture(), true);
  4. int width = mSprite->GetWidth();
  5. for(int y = 0; y<mSprite->GetHeight(); ++y)
  6. {
  7. for (int x = 0;x<width;x++)
  8. {
  9. DWORD *color = &indexColor[x+y*width];
  10. if((GETA(*color) != 0))
  11. {
  12. bitmask_setbit(mHitTable, x,y);
  13. }
  14. }
  15.  
  16. }
  17.  
  18. hge->Texture_Unlock(mSprite->GetTexture());
  19. }


,,ndrade tillbaka från textureWidth/height, då jag klipper ut sprite:s från en större bild, så blev bredden 256 istället för 64, samma sak med höjden.


min kod för jämförelse:
  1. bool gameobject::isCollidingPixel(gameobject* collObj)
  2. {
  3. if(isCollidingBox(collObj))
  4. return bitmask_overlap(mHitTable, collObj->getCollisionTable(),
  5. mPosX - collObj->getPosX(), mPosY -collObj->getPosY()) != 0;
  6. else
  7. return false;
  8. }

dokumentation om bitmask_overlap:
  1. /* Returns nonzero if the masks overlap with the given offset. */

Och om hur den räknar offset står här enligt dokumentationen:
  1. The overlap tests uses the following offsets (which may be negative):
  2.  
  3. *
  4. * +----+----------..
  5. * |A | yoffset
  6. * | +-+----------..
  7. * +--|B
  8. * |xoffset
  9. * | |
  10. * : :


Kanske skänker ytterligare lite ljus över mitt problem och kanske en eventuellt lösning, men jag hittar ingetSmiley

(letar fortfarande alternativ om jag inte hittar en lösning på detta snart, börjar tröttna på att "leka" med detta nu)

tack på förhand.

-------------------------
c++

Senast redigerad 19:59 - 6:e Januari 2009


Slash 02:52 - 7:e Januari 2009 | Post #7
Medlem
Inlägg: 141


Skicka PM
Jag blev lite sugen på att testa Uffes lib och för mig verkar det fungera ganska bra (kanske lyckades välja ett lyckosamt testfall, vem vet?Smiley )

I alla fall, skrev ihop ett litet test. Det gör en bitmask med en cirkel, sedan ritar den ut masken så man själv kan bedömma om den kolliderar men en likadan mask. Dessutom skriver den ut hurvida libbet tyckte att den kollidera eller inte. Kollar man på utdata så ser man att det verkar stämma. Här är den snabbt hopslängda koden:

  1. #include "bitmask.h"
  2. #include <cmath>
  3. #include <cstdio>
  4.  
  5. // Ritar ut två maskor på varandra '1' är första masken, '2' den andra
  6. // och 'X' är där de korsar.
  7. void printmasks(bitmask *m1, bitmask *m2, int offX, int offY)
  8. {
  9. for (int y = 0; y < m1->h && y < m2->h; y++)
  10. {
  11. for (int x = 0; x < m1->w && y < m2->w; x++)
  12. {
  13. bool b1 = bitmask_getbit(m1, x, y) != 0;
  14. bool b2 = false;
  15. if (x >= offX && y >= offY)
  16. b2 = bitmask_getbit(m2, x-offX, y-offY) != 0;
  17.  
  18. if (b1 && b2)
  19. printf("X");
  20. else if (b1)
  21. printf("1");
  22. else if (b2)
  23. printf("2");
  24. else
  25. printf(".");
  26. }
  27. printf("\n");
  28. }
  29. }
  30.  
  31. void printtest(bitmask *mask, int offX, int offY)
  32. {
  33.  
  34. printmasks(mask, mask, offX, offY);
  35. printf("Collide: %s\n",
  36. bitmask_overlap(mask, mask, offX, offY) ? "true" : "false");
  37. }
  38.  
  39. int main()
  40. {
  41. const int width = 20;
  42. const int height = 20;
  43.  
  44. bitmask *mask = bitmask_create(width, height);
  45.  
  46. // Gör en cirkel-maska
  47. for (float x = 0; x < width; x+=.1)
  48. {
  49. const int y = sqrt(width*width/4-4-(x-width/2)*(x-width/2));
  50. bitmask_setbit(mask, x, width/2+y);
  51. bitmask_setbit(mask, x, width/2-y);
  52. }
  53.  
  54. // Skriv ut tre test-fall
  55. printtest(mask, 10, 10);
  56. printtest(mask, 12, 12);
  57. printtest(mask, 14, 14);
  58.  
  59. bitmask_free(mask);
  60. return 0;
  61. }


Och utdata från testet:

....................
......11111111......
....111......111....
...11..........11...
..11............11..
.11..............11.
.1................1.
11.................1
1..................1
1..................1
1..................1
1...............222X
1.............222..1
11...........22....1
.1..........22....1.
.11........22....11.
..11.......2....11..
...11.....22...11...
....111...2..111....
......1111X111......
Collide: true
....................
......11111111......
....111......111....
...11..........11...
..11............11..
.11..............11.
.1................1.
11.................1
1..................1
1..................1
1..................1
1..................1
1..................1
11................2X
.1..............22X.
.11............2211.
..11..........2211..
...11........2211...
....111......X11....
......111111XX......
Collide: true
....................
......11111111......
....111......111....
...11..........11...
..11............11..
.11..............11.
.1................1.
11.................1
1..................1
1..................1
1..................1
1..................1
1..................1
11.................1
.1................1.
.11..............11.
..11............1122
...11..........1122.
....111......11122..
......11111111.22...
Collide: false


Så då vet du att det finns åtminstone ett fal där libbet fungerar, om det är till någon tröstSmiley


-------------------------
Ingen sigantur!



Dread 09:46 - 7:e Januari 2009 | Post #8
Medlem
Inlägg: 135


Skicka PM
Jag testade att ta bort "väggarna" och skriva ut 1/0 om den registrerade kollision, märkligt nog så gjorde den inte det hela tiden, och ibland en bit utanför spriten ( ovanför, typ små trapsteg som sticker ut från spriten).
jag kör för enkelhetens skull med två sådana (den blev lite av kapad när jag klippte ut den från bilden), den ska egentligen vara 64x64.

Den enda större skillnaden jag kan se, är att jag fyller cirklarna med 1:or, respektive 2:or (om jag skulle använt ett snarlikt sätt för att rita upp det).

och att jag fick använda
  1. extern "C"
  2. {
  3. #include "bitmask.h"
  4. }

annars gnällde VS2008 hela tiden, tydligen var det nåt om att kompilatorn inte kunde "blanda" C och C++ kod annars, eller nåt snarlikt var det iaf. Borde inte göra nån större skillnad tycker man.

EDIT:
fast vänta nu...

Tror fasen jag kommit på det nu. Blir till att försöka få rätt på detta på tåget.
Eftersom mSprite->GetTexture() returnerar hela sprite-sheet:et så blir det inte "ny" rad när 64 tar slut (där sprite:n slutar), utan vid 256 istället, så DWORD *color kontrollerar antagligen på fel ställen i setHitTable-funktionen, kan det vara så helt enkelt? Måste ta mig tusan vara så, det skulle förklara mycket, då rapporterar den nog större delen av spriten som icke kolliderbar. Detta måste jag undersöka alltså!

sprite sheet:et där jag klipper ifrån ser ut så här (det är en bild):

Alltså jag tror nästan det måste vara det här som spökar. Ska nog posta på HGE och höra ifall det verkligen är tänkt att getTexture ska funka så, kan vara en bugg i resurshanteraren kanske.

-------------------------
c++

Senast redigerad 09:46 - 7:e Januari 2009


Dread 15:47 - 7:e Januari 2009 | Post #9
Medlem
Inlägg: 135


Skicka PM
Funkar nu, var det som var "fel" perfekt, tack för all hjälp!

-------------------------
c++



Sidor: 1

Forum huvudsida -> Programmering -> pixelperfekt collision
Atom feed

Du får inte posta i den här tråden | Till toppen