Jednostki spawnujące w świecie wywołanym hałasem Perlina?

16

W mojej grze opartej na hałasie Perlin natknąłem się na pewne problemy. Spójrz na załączony zrzut ekranu poniżej.

wprowadź opis zdjęcia tutaj

Białe obszary, które widzisz, to ściany, a czarne obszary można chodzić pieszo. Trójkąt pośrodku to gracz.

Zaimplementowałem fizykę w tej grze, rysując ją na fakturze (białe lub czarne piksele), a następnie pobierając ją z procesora.

Jednak teraz mam inny problem. Chcę, aby jednostki (lub creepy, jakkolwiek je nazwiesz) stale pojawiały się na krawędzi ekranu. Chodzi o to, że w końcowej grze będzie „mgła wojny”, która i tak nie pozwoli graczowi zobaczyć tego tak daleko.

Uznałem, że mogę po prostu zeskanować piksele na krawędzi ekranu i sprawdzić, czy ich tekstura fizyki jest czarna, a następnie odradzać tam losowo rzeczy. Jeśli jednak spojrzysz na zrzut ekranu po raz drugi, w lewym górnym rogu znajduje się przykład, w którym nie chciałbym, aby stwory się pojawiały (ponieważ nie byłyby w stanie dotrzeć do gracza z tego miejsca) .

Czy w jakiś sposób GPU może ustalić te miejsca odrodzenia dla mnie, czy w inny sposób? Pomyślałem o stworzeniu wektorów między proponowanym punktem na brzegu ekranu a odtwarzaczem, a następnie podążaniu za nim co 10 wokseli i sprawdzeniu, czy ściana nie zderzy się, zanim pojawi się tam jednostka.

Jednak wyżej zaproponowane rozwiązanie może być zbyt obciążające procesor.

Wszelkie sugestie w tej sprawie?

Uwaga 1 W przypadku jednostek odradzających się, nie chcę używać żadnego rodzaju odnajdywania ścieżek, aby uniknąć kolizji na ścianie, gdy jednostki te biegną w kierunku gracza. Dlatego jednostki muszą się pojawiać na krawędzi ekranu, w miejscu, w którym chodzenie w linii prostej w kierunku gracza nie koliduje z żadnymi ścianami.

Mathias Lykkegaard Lorenzen
źródło
1
Czy mapa porusza się z graczem, czy gracz porusza się na mapie? Czy mapa się zmieni? Jeśli nie, sugeruję wypełnienie wszystkich nieosiągalnych punktów w momencie generowania, abyś nie musiał się o nie martwić.
dlras2
Jeśli gracz się porusza, jednostki będą potrzebowały metody znalezienia ścieżki. Jeśli chcesz mieć wklęsłe obszary, będziesz miał ten problem i musisz zapewnić rozwiązanie dla poruszającej się jednostki, która próbuje dotrzeć do poruszającego się gracza ... Znalezienie ścieżki.
Blau
1
Lub, mówiąc inaczej, komentarz Blau: Twoje pytanie nie ma poprawnej odpowiedzi (poza trywialnym przypadkiem mapy bez płytek / pikseli w ogóle), jeśli gracz może się poruszyć. Nadal wymaga zdefiniowania, co masz na myśli przez „linię prostą”, aby uzyskać odpowiedź, jeśli gracz stoi w miejscu.
Martin Sojka,

Odpowiedzi:

3

Istnieje bardzo przydatny algorytm do tego zadania, znacznie bardziej wydajny niż algorytm powodziowy w tej sytuacji (jego złożoność to czas działania jest proporcjonalny do wielkości granicy, a nie zalany obszar): jest to algorytm marszowych kwadratów. Pomysł jest prosty: rozpocznij lokalizację gracza (punkt środkowy ekranu), wybierz kierunek i idź, aż znajdziesz ścianę. Kiedy zderzasz się ze ścianą, uruchamiasz algorytm: wybierasz orientację (w górę lub w dół) i zaczynasz maszerować nad granicą tego obszaru, podświetlając piksele. To daje ci wewnętrzną granicę dla dozwolonego obszaru. Następnie po prostu sprawdzasz, czy punkty kandydujące do jednostek spawnujących leżą na tej granicy.

Jest to zasada, którą należy przestrzegać podczas uruchamiania granicy:

http://en.wikipedia.org/wiki/File:Marchsquares.png (meh Nie mogę jeszcze publikować zdjęć)

Opis Wikipedii (choć ma dużo większą złożoność, ponieważ jest używany z innymi aplikacjami):

http://en.wikipedia.org/wiki/Marching_squares

Wszechmogący
źródło
10

Zrób wypełnienie powodziowe z pozycji gracza; każdy „zalany” obszar jest wówczas ważnym obszarem zabaw, a wszystkie pozostałe są ścianami.

EDYCJA: Jeśli chodzi o dodatkowy wymóg „osiągalny w linii prostej”, należy pamiętać, że w dyskretnej przestrzeni musisz to zdefiniować nieco dalej. Na przykład wszystkie powyższe ścieżki mogą być prawidłową „linią prostą” w takim środowisku i widziałem je wszystkie w grze w pewnym momencie:

warianty „linii prostej”

W szczególności większość z nich nie jest przemienna - co oznacza, że ​​fakt, że można dotrzeć do B z linii A w „linii prostej”, nie oznacza, że ​​można również dotrzeć do A z linii B; żadna z nich niekoniecznie musi być prawdziwa.

Ponadto pojawia się pytanie, w jaki sposób poradzisz sobie z ruchem po przekątnej, jeśli jeden lub oba punkty „boczne” blokują.

Martin Sojka
źródło
Czy można to całkowicie zaimplementować w HLSL?
Mathias Lykkegaard Lorenzen,
@Mathias Lykkegaard Lorenzen: Tak, wątpię, wykonując każdy krok algorytmu jako moduł cieniujący piksele i renderując na przykład między dwoma obiektami docelowymi tekstur, ale ... dlaczego ? Prawdopodobnie będziesz potrzebować informacji z algorytmu na CPU, przynajmniej do znalezienia ścieżki.
Martin Sojka,
1
@Mathias Lykkegaard Lorenzen: To nieco różni się od tego, o co prosiłeś. W takim przypadku: jak zdefiniować „linię prostą”, biorąc pod uwagę schemat partycjonowania przestrzeni dyskretnej?
Martin Sojka,
2
nawet jeśli nie chcesz korzystać z funkcji wyszukiwania ścieżek, możliwe jest poproszenie procesora o wykonanie zadania wypełniania, pamiętaj, że musisz wywołać funkcję wypełnienia tylko raz, a wtedy będziesz mieć teksturę 3 kolorów określającą ścianę, wolne przestrzenie i spawalne spacje. dla tekstury 4096 x 4096 wykonanie procesora wypełniania zajmie mniej niż sekundę.
Ali1S232,
1
Chodzi o to, że musisz uruchomić to wypełnienie powodziowe tylko raz, a nawet jeśli twój teren zmieni się podczas gry, musisz zaktualizować tylko te sekcje, które są dotknięte, i przepełnienie powodziowe przebiega przez nie, co jest piekielnie szybkie.
TravisG,
1

Co powiesz na pozwolenie spawnu? Nie widzę w tym żadnego konkretnego problemu.

aaaaaaaaaaaa
źródło
A co jeśli odrodzą się za ścianą? Jak sprawisz, by dotarły do ​​gracza?
Mathias Lykkegaard Lorenzen,
1
może to stanowić problem, jeśli gra ma scenariusz zabicia wszystkich wrogów i spawnuje 50 wrogów, ale kilku odrodziło się za ścianą. Gracz nie byłby w stanie zabić wrogów, a scenariusz się nie zakończył.
Lie Ryan,
1
Inne jednostki nadal mogą nie być w stanie dotrzeć do gracza, w zależności od tego, jak gracz się poruszy po tym, jak się odrodzą, w każdym przypadku będziesz musiał odrodzić niektóre jednostki.
aaaaaaaaaaaa
mgła wojny pokryje gburowate spawny
KRB
1
To i tak nie zadziała, gdy gracz się poruszy.
aaaaaaaaaaaa
1

jeśli ważne jest, aby zaznaczyć graczowi tylko punkty z prawidłową linią prostą, możesz użyć następującego algorytmu (kod c ++), zużywa on więcej niż normalne wypełnienie. mogą występować drobne błędy (będę zadowolony, jeśli ktoś je poprawi), ponieważ sam nie testowałem kodu, ale wpadniesz na pomysł.

void straightlineFill(Point startPoint, Texture target)
{
    queue<Point> pointQueue;
    for (int dx = -1;dx <=1;dx ++)
            for(int dy = -1;dy <=1;dy++)
                if(dx != 0 && dy != 0)
                    pointQueue.push(point(startPoint.x + dx, startPoint.y + dy));
    while (!pointQueue.empty())
    {
        point front = pointQueue.front();
        pointQueue.pop();
        if (target.pixelAt(front) == COLOR_SPAWNABLE||
            target.pixelAt(front) == COLOR_WALL||
            target.pixelAt(front) == COLOR_NOT_SPAWNABLE)
                continue;
        taraget.setPixelAt(front, colorFilled); 
        for (int dx = -1;dx <=1;dx ++)
            for(int dy = -1;dy <=1;dy++)
                if(dx != 0 && dy != 0)
                    pointQueue.push(point(front.x + dx, front.y + dy));
        // up until now was the normal floodfill code
        // and here is the part that will do the real straight line checking

        // lineDX & lineDY will keep how much the line we are checking is skewed
        int lineDX = front.x - startPoint.x;
        int lineDY = front.y - startPoint.y;

        // step will show us how much we have to travel to reach each point of line
        point step;
        if (abs(lineDX) < abs(lineDY))
        {
            if (lineDX < 0)
                step = point(-1,0);
            if (lineDX == 0)
                if (lineDY < 0)
                    step = point(0,-1);
                else
                    step = point(0,1);
            if (lineDX > 0)
                step = point(1,0);
        }

        if (abs(lineDX) < abs(lineDY))
        {
            if (lineDY < 0)
                step = point(0,-1);
            if (lineDY == 0)
                if (lineDX < 0)
                    step = point(-1,0);
                else
                    step = point(1,0);
            if (lineDY > 0)
                step = point(0,1);
        }

        // moved will keep how much we have traveled so far, it's just some value that will speed up calculations and doesn't have any mathematical value
        point moved = 0;

        // spawnable will keep if the current pixel is a valid spawnpaint

        bool spawnable = true;

        // now we will travel on the straight line from player position to the edges of the map and check if whole line is valid spawn points or not.

        while (target.isInside(front))
        {
            front = front + step;
            moved = moved + point(step.x * lineDX, step.y * lineDY);
            if (step.x != 0 && moved.x < moved.y)
            {
                moved.x = moved.x - lineDY * sign(lineDY);
                front.y = front.y + sign(lineDY);
            }
            if (step.y != 0 && moved.y < moved.x)
            {
                moved.y = moved.y - lineDX * sign(lineDX);
                front.x = front.x + sign(lineDX);
            }

            if (target.getPixelAt(front) == COLOR_WALL)
                spawnable = false;

            if (spawnable)
            {
                target.setPixelAt(front,COLOR_SPAWNABLE);
                for (int dx = -1;dx <=1;dx ++)
                    for(int dy = -1;dy <=1;dy++)
                        if(dx != 0 && dy != 0)
                            pointQueue.push(point(front.x + dx, front.y + dy));             
            }
            else
            {
                target.setPixelAt(front,COLOR_NOT_SPAWNABLE);
            }
        }
    }
}
Ali1S232
źródło
1

Możesz wypełnić mapę kolorami reprezentującymi obszary wypukłe ... w ten sposób możesz spawnować swoją jednostkę, jeśli leży w tym samym obszarze. Lub możesz łatwiej wyszukiwać dostępne obszary.

To są dane statyczne, więc możesz je wstępnie skalibrować.

musiałeś wypełnić punkt znajdujący obraz, w którym następuje zmiana z konwekcyjnego na wypukły, wizualnie wydaje się łatwy do znalezienia, masz dwie sytuacje:

  1. Poziomo: tam, gdzie zmienia się kolor pomarańczowy z niebieskim.
  2. Pionowo: gdzie czerwony zmienia się na zielony i pomarańczowy.

wprowadź opis zdjęcia tutaj

Blau
źródło
To nie działa Spójrz na prawy dolny róg, szczególnie na kroplę w czerwonym obszarze. Jest całkowicie wypukły, więc nie ma żadnych zmian, które spowodowałyby użycie innego koloru, ale wyraźnie nie ma linii prostej od dolnej części czerwonego na prawej krawędzi do skrajnej prawej części czerwonej na dolnej krawędzi.
Loren Pechtel,
@Loren Pechtel to jest własnoręcznie robiony, masz rację, jest tam błąd, to moja wina, ale możesz zdać sobie sprawę, że jest to ta sama sytuacja, co przejście z pomarańczowego na niebieski.
Blau,
@Loren Pechtel, przypomnij, że głównym celem jest unikanie odradzania się w obszarach takich jak żółty. Ta metoda zapewnia, że ​​jeśli uwolnisz wroga w tym samym obszarze, w którym znajduje się gracz, jest to możliwe. Oczywiście wdrożenie tego może być trudne, ale pomysł jest ważny.
Blau
Twoja wersja nie pomaga. Dwa punkty na wypukłej krzywej nigdy nie będą miały prawnej linii prostej, kropka. Więcej dywizji nie pomoże.
Loren Pechtel
proszę sprawdzić definicję wypukłego odnoszącego się do obszarów lub zbioru punktów ... en.wikipedia.org/wiki/Convex
Blau
1

Oto coś, czego faktycznie użyłem w mojej własnej grze (świat generowany przez hałas 2D, prawie dokładnie tak jak twój) - Promienie. Zacznij od gracza, określ orientację (losowo, jeśli chcesz) i idź wzdłuż tej linii, aż coś trafisz (krawędź ekranu LUB asteroida). Jeśli uderzysz w krawędź ekranu (a nie asteroidę / białą kroplę), to wiesz, że od krawędzi ekranu do gracza jest prosta, otwarta linia. Następnie spawnuj potwora w miejscu, w którym trafiłeś. Jeśli trafisz asteroidę, ponownie wykonaj test.

khyperia
źródło
0

Innym rozwiązaniem (bez GPU), którego można użyć, jest wyszukiwanie ścieżek. Przed narysowaniem mapy znajdź ścieżkę z każdego potencjalnego punktu odradzania na każdej krawędzi mapy i sprawdź, czy jest ścieżka do centrum. * Wyszukiwanie ścieżek jest całkiem OK pod względem kosztów / wydajności, ale możesz to zrobić przed rozpoczęciem gry, jeśli to problem.

Każdy punkt odradzania, który nie ma ścieżki, może zostać umieszczony na liście wykluczeń; lub odwrotnie (dowolny punkt ze ścieżką można umieścić na liście włączającej).

ashes999
źródło