Skuteczne wykrywanie kolizji na podstawie płytek dla wielu kwadratów?

9

obecnie pracuję nad własną wersją gry opartej na kafelkach (pomyśl Terraria, ale mniej fantastycznie (myślę, że to słowo? Przepraszam, jeśli nie jest)).

Tak czy inaczej, obecnie działam wykrywanie kolizji (nawet w przypadkach narożnych!), Co było dla mnie dużym krokiem. Jest coś niezwykle satysfakcjonującego w widzeniu, że duszek nie biegnie przez blok. Ale potem wpadłem na pomysł, aby przeprowadzić testy porównawcze. Kiepski pomysł.

1000 kwadratów, nie ma problemu. 10 000 kwadratów, dla 3 postaci było trochę opóźnione. 100 000 kwadratów (naprawdę ogromna mapa), na 3 postacie nie można było grać.

Mam problem polegający na tym, że nie chcę nawet brać pod uwagę bloków, które znajdują się zbyt daleko od gracza, postaci, przedmiotów itp., Ale nie chcę ciągle ładować tych brakujących pamięci.

Oto mój algorytm do tej pory, nie krępuj się krytykować.

foreach (Block in level)
{
    if (distance from block to player > a specified amount)
        ignore this block;
    else
    {
        get the intersection depth between the two bounding boxes
        if (depth of intersection != Zero-vector)
        {
            check y size vs x size
            resolve on smallest axis
        }
    }
}

Jak zauważysz, gdy rozmiar poziomu staje się większy, kolejność tego algorytmu rośnie o N bloków. Chciałbym nawet nie brać pod uwagę bloków, które nawet nie są blisko gracza.

Myślę, że może użyj (0,0) do (mapWidth, mapHeight) podwójnej tablicy bloków zamiast listy, obliczając strefę zagrożenia w zależności od pozycji osoby, np. Jeśli pozycja gracza wynosi (10, 20) będzie wyglądać od (0, 10) do (20, 30) itd.

Wszelkie przemyślenia i uwagi są niesamowite, dziękuję.

Ross
źródło
1
Witamy w Stackexchange! :-) Nie zapomnij przeczytać FAQ, jeśli nie wiesz, jak działa cały system kontroli jakości i reputacji.
David Gouveia,
Z pewnością te kafelki są większe niż 16 na 16 pikseli, przy 1920 na 1080 to 8100 płytek. Na pewno wiesz, gdzie znajdują się ruchome byty, i możesz sprawdzać tylko kafelki na siatce, które mogą być w zasięgu (jeśli jeden ma 160 * 160, a środek znajduje się w kafelku (12,12), potrzebujesz tylko sprawdzić między kafelkami (6 , 6) i (18,18), w sumie ~ 150 możliwych płytek.). Z pewnością płytki pod grawitacją spadają, więc wystarczy spojrzeć na kolejną płytkę poniżej.
DampeS8N
Czy uważasz, że 16x16 jest za mały? Nie byłoby mi trudno zmienić rozmiar płytek, ponieważ wszystko, co odnosi się do szerokości / wysokości, jest stałą statyczną. Wszystko, co musiałbym zrobić, to powiększyć je w Paint.NET, co jest miłe, ponieważ dodaje więcej szczegółów.
Ross,
Czy mógłbyś udostępnić swój kod kolizyjny? : /
ashes999

Odpowiedzi:

7

Tak, myślisz poprawnie. Powinieneś używać tablicy 2D, ponieważ pozwala to na indeksowanie płytek według pozycji.

Block[,] level = new Block[width, height];

A ponieważ gracz może zderzać się tylko z otaczającymi go płytkami, liczba kontroli kolizji, które należy wykonać, jest bardzo mała. To oczywiście zależy od wielkości gracza. Próbka platformówka robi to tak:

int leftTile = (int)Math.Floor((float)characterBounds.Left / tileWidth);
int rightTile = (int)Math.Ceiling(((float)characterBounds.Right / tileWidth)) - 1;
int topTile = (int)Math.Floor((float)characterBounds.Top / tileHeight);
int bottomTile = (int)Math.Ceiling(((float)characterBounds.Bottom / tileHeight)) - 1;

for (int y = topTile; y <= bottomTile; ++y)
{
    for (int x = leftTile; x <= rightTile; ++x)
    {
        // Handle collisions with the tile level[x,y] just like you were doing!
    }
}

Sprawdź próbkę, jeśli nadal masz jakieś problemy.

David Gouveia
źródło
Jest to bardzo ładny mały algorytm, nie słyszałem nawet o próbce Platformera (powinienem, ale twierdzę, że jestem ignorantem). Dziękuję Ci!
Ross,
@Ross Naprawdę? Byłbyś zaskoczony, jak podobne jest twoje rozwiązanie do próbki. :-) Pomijając część z listy, wszystko inne jest prawie identyczne (przecinają ramki graniczne, uzyskują głębokość przecięcia, rozwiązują na najmniejszej osi).
David Gouveia,
1
O rany, właśnie na to spojrzałem. >. <Chciałbym to wiedzieć 2 dni temu !! Cóż, jestem nowy w XNA, ale zajmuję się grafiką 2D (tylko OpenGL, niezbyt dużo programowania gier). Chyba powinienem najpierw sprawdzić więcej zasobów, zanim zacznę kodowanie.
Ross
1

Myślę, że moja odpowiedź byłaby twoją odpowiedzią! ;-)

Jeśli masz pozycję gracza (i rozmiar), możesz obliczyć wskaźniki otaczających kafelków (które są jedynymi, które należy szczegółowo sprawdzić). W ten sposób nie powinno mieć znaczenia, jak duża jest twoja mapa, zależy to tylko od rzeczywistej wielkości gracza, co daje więcej potencjalnych płytek do sprawdzenia.

Być może sprawdź samouczek na temat kolizji na stronie riemers.net, jeśli jeszcze tego nie zrobiłeś.

Książe Charles
źródło
Słyszałem o Riemer, ale nigdy nie szukałem, dzięki!
Ross,
1

W przypadku dużej liczby kolizji zwykle chcesz zastosować bardziej zaawansowaną strukturę , taką jak Quadtree lub Hashmap, aby sprawdzić te kolizje.

Ponieważ płytki są statyczne, sugerowałbym użycie Quadtree. Drzewo quad składa się z quadów. Każdy kwadrat składa się z czterech prostokątów, a każdy z nich jest kwadratem. Trwa to rekurencyjnie do określonego rozmiaru. Każdy quad może zawierać listę płytek, które zamieszkują ten obszar ekranu. W ten sposób, sprawdzając kolizje, możesz

  1. Ogranicz kontrole do osób w bezpośrednim sąsiedztwie
  2. Ogranicz kontrole tylko do poruszających się obiektów

Teraz, jeśli nie chcesz nawet patrzeć na kafelki poza ekranem, możesz zrobić coś takiego

public bool CheckCollision(myPosition) {
    if(quadNodes.Count > 0) {
        // This is not a leaf, keep checking
        foreach(Quad node in quadNodes) {
            if(node.Position is insideViewport && nearPlayer)
                // Continue recursion
            }
        }
    }
    else {
        // This is a leaf, do checks
        foreach(Tile tile in tileList) {
            if(collision)
                return true;
        }
        return false;
    }
}
Mike Cluck
źródło
Hmm, słyszałem o Octrees w wykrywaniu kolizji 3D, ale nigdy nie widziałem zaawansowanej struktury danych wykorzystywanej do wykrywania kolizji 2D. Dziękuję Ci bardzo!
Ross,
1
Ponieważ jego gra (zakładając, że Terraria) składa się z równomiernie rozmieszczonych płytek, użycie siatki byłoby zarówno o wiele łatwiejsze i szybsze niż poczwórne drzewo. Poczwórne drzewo działa lepiej w bardziej złożonych światach, w których siatka byłaby trudna do dopasowania, a wszystko ma dowolny rozmiar.
David Gouveia,
1
Masz rację, jeśli jest to gra oparta wyłącznie na siatce, nawet do rozmiarów postaci. Wiem, że w Terraria mają też potwory, które nie pasują do formatu siatki. Działałem przy założeniu, że podstawowy świat składa się z kafelków, ale inne obiekty będą inne i mogą przechowywać je w podobnej strukturze, aby uniknąć budowy kolejnego. Przypuszczam, że mogliby użyć siatki dla kafelków, a następnie innej struktury (w razie potrzeby) dla innych dowolnych obiektów.
Mike Cluck,
1
Właśnie to chciałem zasugerować :) Siatka powinna być używana do radzenia sobie z kolizjami z terenem, podczas gdy czteroosobowy może być używany do obsługi kolizji między obiektami.
David Gouveia,
Prawdziwe. Obecnie każda obwiednia ma wymiary mocy 2 ^. To znacznie ułatwia wykrywanie kolizji. Siatka pasowałaby na razie do moich potrzeb.
Ross