Kolizja mapa-gracz C++


#1

Cześć!
Macie może jakieś rady dotyczące kolizji? Chciałbym zrobić kolizję mapa-gracz taką, która mogłaby się przydać już w trochę późniejszych projektach. Na razie co mam to renderowanie mapy w taki sposób

rysowanie mapy:

Summary
void GameplayScreen::DrawMap(SDL_Renderer *renderer)
{
    for (int y = map.size() - 1; y >= 0; --y)
	    for (int x = 0; map[y].size(); ++x)
	    {
		    SDL_Rect destRect;
		    if (map[y][x] != "0,0")
		    {
			    SDL_Rect srcRect;

			    srcRect.w = 32;
			    srcRect.h = 32;
			    srcRect.x = bNum(map[y][x]).first * srcRect.w;
			    srcRect.y = bNum(map[y][x]).second * srcRect.h;

			    destRect.x = (x * 32 + posX)
			    destRect.y = (y * 32 + posY)
			    destRect.w = 32;	
			    destRect.h = 32;	

			    vBlock[Earth]->Draw(renderer, srcRect, destRect);
		}
	}
}

metoda bNum

Summary
std::pair<int, int> GameplayScreen::bNum(std::string map)
{
    return std::pair<int, int>
    (    //w pliku tekstowym klocki pokazywane są jako np. 1,0 0,0 0,1
         //szuka pierwszej liczby, która oznacza przesunięcie na osi X w obrazku
         //czyli np. 0 * 32 = 0; czyli oznacza to, że klocek zaczyna się na 0 x = 0, a kończy w zależności od 
         //długości czyli np. x = 32;
         // 1 * 32 = 32; czyli klocek zaczyna się od x = 32; a kończy x+=32; czyli 64
	    atoi(map.substr(0, map.find(',')).c_str()), 
         // tutaj tak samo jak wyżej tylko, ze na osi Y
	    atoi(map.substr(map.find(',') + 1).c_str())
    );
}

w nowym, testowym projekcie próbowałem zrobić coś takiego:

Summary
void Gameplay::Update()
{
    p.Update();

    for (int y = 0; y < 5; ++y)
	    for (int x = 0; x < 5; ++x)

		    if (map[y][x] == 1) {

			    int mx = x * 32;
			    int my = y * 32;
			    int mxw = (x + 32) * 32;
			    int myh = (y + 32)  * 32;
                                
                //p oznacza player, nazwałem to tak brzydko, bo to tylko testowy projekt
                //mx, my itd. m oznacza map
			    int px = p.rectP.x;
			    int py = p.rectP.y;
			    int pxw = p.rectP.x + p.rectP.w;
			    int pyh = p.rectP.y + p.rectP.h;

			    if (px <= mxw && pxw >= mx && py <= myh && pyh >= my)
			    {
				    if (pxw >= mx)
				    {
					    p.rectP.x = mx - p.rectP.w;
				    }
				    else if (my <= pyh)
				    {
					    p.rectP.y = my + p.rectP.h;
				    }
			    }
			//else std::cout << "NO COLLISION" << std::endl;
		}
}

ale jakoś mi nie wychodziło


#2

Witam, jestem bardzo początkującym w tym temacie, ale sam też wiele razy miałem problem w temacie kolizji. Żebym mógł dobrze odpowiedzieć na te pytanie chciałbym się zapytać czy twoja kolizja ma automatycznie przesuwać gracza w tej funkcji czy tylko wykrywać jaka ona występuje ? Do tego typu gry polecałbym tego typu algorytm:

      auto bx = box.x +box.width/2; // x środka bloku
      auto by = box.y + box.height/2; // y środka bloku
      auto px = x + width/2; // x środka gracza
      auto py = y + height/2; // y środka gracza

      auto dx = abs(px - bx); // obliczanie odległości x
      auto dy = abs(py - by); // obliczanie odległości y

            if (dx > dy) {
                if (px > bx)
                  x = x + (width/2 + box.width/2 - dx);
                else
                  x = x - (width/2 + box.width/2 - dx);
			}
          else{
               if (py > by)
                y = y + (height/2 + box.height/2 - dy);
              else
                y = y - (height/2 + box.height/2 - dy);
		  }

Oczywiście stosujemy go kiedy kolizja jest już wykryta. Jest to algorytm przystosowany bardziej do mojej gry, ale wydaje mi się że po kilku modyfikacjach może u ciebie także zadziałać :smiley:


#3

int mxw = (x + 32) * 32; nie powinno być: int mxw = (x * 32) + 32; ?
Rozumiem że ma być kolizja prostokąta z mapą, więc można sprawdzić każdy róg osobno, powiedzmy if (map[rog1x][rog1y]==0 && map[rog2x][rog2y]==0 … brak kolizji.
Dodam jeszcze że to wszystko jest zrobione jakoś na odwrót, powinieneś sprawdzić czy 4 punkty nie kolidują z mapą a nie przeszukiwać całą mapę czy coś na niej jest. Jeżeli to mapa z bloczków 32x32 to: if map[player_x/32][player_y/32]==0 && map[player_x/32+32][player_y/32]==0 …


#4

Nie, nie powinno być int mxw = (x * 32) + 32;. To co jest w tym momencie w projekcie testowym jest zrobione dobrze do czasu kiedy chce żeby była jakaś akcja mapa-gracz, np. zatrzymanie. Nie mam bladego pojęcia jakby to zrobić.
napisałem w nim

Summary
 if (pxw >= mx)
 {
      p.rectP.x = mx - p.rectP.w;
 }
 else if (my <= pyh)
 {
      p.rectP.y = my + p.rectP.h;
 }

i o ile na osi X działa dobrze, to na Y sie pierdzieli. Na pewno jest jakiś lepszy sposób żeby to zrobić, dlatego na forum piszę.


#5

Więc mówisz, że powinienem zrobić coś takiego

auto x1 = player.x/32;
auto x2 = player.x/32 + 32;
auto y1 = player.y/32;
auto y2 = player/32 + 32;

i wtedy loop

for(int x = x1; x < x2; ++x)
    for(int y = y1; y < y2; ++y)
    {
        if(map[x][y] != "0,0")
        {
            No i jak teraz przestawić naszego gracza?
        }
    }

#6

Przyjrzałem się temu bliżej i do końca nie rozumiem tego. Gracz porusza się co 32 piksele czyli co klocek ? Mapa wygląda jak siatka w zeszycie ? Bo ten format mapy wygląda jakoś dziwnie, standardowo mapa powinna to być powiedzmy: int mapa[5][5], mapa[0][0] to klocek na pozycji x 0-32, y 0-32, mapa[0][1] - x 0-32, y 32-64. Taką małą mapę to zamiast z pliku ładować można od razu wpisać: unsigned char mapa[3][3]={1,1,1,
1,0,1 ,
1,1,1};


#7

I tak jest jak mowisz. Tylko że zamiast int mapa przyjmuje string

Dla int map[0][0] x:0-32;y:0-32
Czyli mapa dla intow byla by taka
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 1 2 3
1 1 1 1 1

I taki zapis pozwala mi przechodzić po między blokami tylko na osi x, co znaczy że jeśli chciałbym klocek, który na osi y zaczyna się na 32 a kończy na 64, to tego nie zrobię.

Dlatego moja mapa wygląda tak
0,0 0,0 0,0 0,0 0,0
0,0 0,0 0,0 0,0 0,0
1,0 0,2 1,1 0,5 0,0

0,0 oznacza klocek zaczynający się na x 0-32 y 0-32

1,0 oznacza klocek x 32-64 y 0-32
0,2 oznacza klocek x 0-32 y 64-96