(tworznie gier) Kolizja z przedmiotem podczas spadania postaci

0

Siema. Zrobiłem funkcja Kolizja():

public static boolean Kolizja(Vector WektorPołożenia, Postać postać, Textury box) {
		return (WektorPołożenia.x < box.Xposition + box.Width && WektorPołożenia.x + postać.Width > box.Xposition
				&& WektorPołożenia.y + postać.Height > box.Yposition && WektorPołożenia.y < box.Yposition + box.Height);
	}

Wszystko działa, tzn zwraca wartosc true, gdy wjezdzam z kazdej strony na przeszkodę. Problem zaczyna się, gdy zaimplementowalem metodę skok(). Kiedy postac skacze na tyle wysoko, aby byc ponad przeszkodą to gdy probuje na nią wskoczyć, to przelatuje przez nią. Od spodu wszystko działa, a chce zeby postac zatrzymala się na przeszkodzie. Jakaś rada, pomysly ?

Funkcja skok():

public void skok() {

		WektorPołożenia.y -= WektorPrędkości.y;
		WektorPrędkości.y -= 15;

		if (WektorPołożenia.y < TopMapBorder) {
			WektorPołożenia.y = TopMapBorder;
		}

		if (WektorPołożenia.y > BottomMapBorder - postać.Height) {
			WektorPołożenia.y = BottomMapBorder - postać.Height;
			jump = false;
			WektorPrędkości.y = 100;
		}

		if (Kolizja(WektorPołożenia, postać, box)) // OD DOŁU
		{
			WektorPołożenia.y = box.Yposition + box.Height;
		}
	}
1
  1. Box.pozycja -> prawda, że też może być wektorem? Tak samo, jak gracz.pozycja. Jednolite nazwy...
  2. Taki sposób sprawdzania kolizji z otoczeniem jest dość wadliwy... Sama funkcja sprawdzająca kolizję też, bo sprawdzasz czy cały prostokąt postaci znajduje się w całym prostokącie platformy (a to jest możlwie tylko jeśli platforma jest większa od postaci i postać weszła w całą platformę a nie jest np. jedną nogą poza platformą). No i nawet jak przerobisz to, żeby sprawdzało wszystkie punkty, to i tak nie uwzględnisz sytuacji, kiedy prostokąty się krzyżują (a może się tak stać w skrajnych przypadkach, w zależności od wielkości przesunięcia/klatek na sekundę). Najprościej by było jakbyś zrobił obiekt "prostokąt", który będzie zawierał definicję wszystkich odcinków odpowiednio dla postaci i przeszkody. Kolizje byś badał sprawdzając, czy którekolwiek odcinki się przecinają. To podejście nie jest idealne, ale jest lepsze niż sprawdzanie punktów.
1

@furious programming: przywołuję Cię :D
Wprawdzie język inny, ale zasada wykrywania kolizji podobna to Twojego platformersa. Może coś ciekawego dodasz :)

@Spwrtt - poczytaj wpisy Furoius'a na minibligu, opisywał tam różne aspekty tworzenia swoje gierki, był też odcinek o ruchu postaci i wykrywaniu kolizji.
https://4programmers.net/Profile/49548/Microblog

1

Sprawdzenie kolizji 2 prostokątów z bokami równoległymi do osi układu współrzędnych to sprawdzenie czy:

  1. co najmniej jeden z wierzchołków prostokąta A znajduje się wewnątrz prostokąta B (intersekcje + A zawiera się w B)
  2. lub przynajmniej 1 wierzchołek B znajduje się wewnątrz A1. (przypadek kiedy cały B zawiera się w A)
  3. lub dowolnie wybrany odcinek pionowy A przecina się z dowolnie wybranym odcinkiem poziomym B.

Warto sobie wprowadzić pojęcie (i przy okazji klasę) MBR - minimum bounding rectangle, zaimplementować w niej metody do badania kolizji

Sprawdzanie czy odcinki się nie przecinają nie jest już takie banalne, a ze względów wydajnościowych poprzedza się takie sprawdzenie właśnie prostym badaniem kolizji 2 prostokątów

Problem jaki możesz spotkać, to prędkość i klatki ilość klatek fizyki - czasami obiekty mogą przeniknąć przez siebie a kolizja nie zdąży nastąpić - np. szybko poruszający się mały pocisk trafiający w cienką ścianę.

1

Jest jeszcze taki fajny algorytm ;)

https://www.gamedev.net/articles/programming/general-and-gameplay-programming/2d-rotated-rectangle-collision-r2604/

Wiadomo, pewnie u Ciebie prostokąty się nie obracają, ale i tak zadziała ;)

Tylko przy takim wykrywaniu kolizji nie dowiesz się za bardzo, z której strony nastąpiła... Czyli Twoja postać skacząca stanie na platformie po uderzeniu w jej bok.
Dlatego zachęcam do badania kolizji z otoczeniem zanim ona nastąpiła przy użyciu raycastów. Kiedy idziesz w prawo to strzelasz w prawo raycastami o długości równej potencjalnemu przemieszczeniu, zanim w ogóle nastąpił ruch. Jeśli nastąpiła kolizja, to skracasz wektor przemieszczenia do takiej długości, przy której kolizja nie nastąpi.

Polecam opis tego gostka:

0

Ja bym bardzo prosił, aby mojej platformówki nie dawać jako przykładu tworzenia gier, dlatego że jest ona jedynie eksperymentem (jest wysoce specyficzna), nie zawierająca czegokolwiek związanego z poprawnym dziś gamedevem. A że sam tworzeniem ”prawdziwych” gier się nie zajmuję, to każdy aspekt tego projektu po prostu wymyśliłem. Wiem co nieco na temat tworzenia gier na Famicoma, ale taka wiedza dziś nie jest zbyt przydatna. ;)

Tak więc mogę napisać o tym, w jaki sposób zaimplementowałem kolizje bohatera z platformami w mojej demówce, jednak głównie jako ciekawostkę, bo zapewne istnieją lepsze rozwiązania. Zresztą wszystko i tak zależy od konkretnych wymagań.


W mojej demówce, bohater jest prostokątnym klockiem. Rozmiar hitboksu klocka to zawsze 14x16 pikseli – bez względu na to czy ciało bohatera jest rozciągnięte, czy nie (rozciąga się głównie podczas opadania). Owe rozciągnięcie jest jedynie efektem wizualnym, bo może takim być – w końcu kolizja z platformą trwa tak krótko, że rozciągnięte ciało bohatera nachodzące na platformę jest niezauważalne.

Platformy zbudowane są z kafli o rozmiarach 16x16 pikseli. Hitboks bohatera mieści się ładnie wewnątrz kafli, dlatego też określiłem, że do sprawdzania kolizji używane będą wierzchołki. Hitboks bohatera opisuje struktura zawierająca współrzędne wszystkich czterech wierzchołków, natomiast hitboksy danej warstwy poziomu to po prostu dwuwymiarowa macierz wartości logicznych.

Aby przeprowadzić ruchu bohatera, wykonuje się przesunięcie współrzędnych wierzchołków o zadane wartości. Dla ruchu poziomego jest to stała wartość równa 2, a dla pionowego, zależna od prędkości wznoszenia/opadania (od 1 do 16). Aby wykryć kolizję, najpierw określa się kierunek ruchu – lewo lub prawo oraz góra lub dół – i wybiera dwie pary wierzchołków, jedna para dla ruchu poziomego i jedna dla pionowego. Jeśli bohater ma być przesunięty np. w prawo, to bierze się dwa prawe wierzchołki, jeśli w dół, to dwa dolne. Następnie dodaje się offsety do tych współrzędnych i dzieli się je przez rozmiar kafli – w ten sposób uzyskujemy relatywne indeksy komórek w macierzy hitboksów warstwy poziomu.

Ostatnim krokiem jest sprawdzenie, czy komórki w macierzy hitboksów spod obliczonych relatywnych indeksów zawierają True (blok platformy) lub False (pusta przestrzeń). Jeśli False, to bohater może się przesunąć (brak kolizji), więc aktualizuje się jego pozycję, a jeśli True, to należy wyrównać pozycję do danego kafla (nastąpiła kolizja). Gra oprócz statycznych platform nie posiada żadnych innych obiektów, z którymi bohater może się zderzyć, więc kolizje z platformami to jedyne co należało oprogramować.

Oczywiście zabezpieczeń jest więcej. Należy też sprawdzać kolizje ze ścianami ekranu (dla ruchu poziomego), tak aby bohater nie mógł wyjść poza planszę, a także z granicami planszy (dla ruchu pionowego), aby bohater z niej nie wypadł (wtedy podczas testowania kolizji wykroczonoby poza zakres macierzy hitboksów). Dlatego każdy poziom określają dwa obszary:

  • widoczna przestrzeń (Area) – do której wyrównywana jest kamera oraz ciało bohatera w ruchu poziomym,
  • granice planszy (Bounds) – do których wyrównywane jest ciało bohatera w ruchu pionowym (tylko po wpadnięciu w przepaść).

Różnica tych obszarów to tzw. margines, gdyby to kogokolwiek obchodziło. Bohater może się poruszać po marginesie jedynie w ruchu pionowym. Śmierć bohatera następuje w momencie dotknięcia granicy planszy (Bounds), co zostało wykorzystane do stworzenia efektu niewielkiej przerwy, trwającej od zniknięcia bohatera pod/nad ekranem do jego zabicia, a tym samym do zaciemnienia ekranu i resetu. Owa przerwa to ruch pionowy właśnie po marginesie, a jej długość jest uzależniona od prędkości opadania (im wyższa prędkość, tym szybciej bohater dotknie granicy, więc i krótsza przerwa).

Rozmiar kafli warstw poziomów oraz rozmiar bohatera są dobrane w taki sposób, aby powyższe obliczenia nigdy się nie myliły. Maksymalna prędkość opadania określona jest jako 16 pikseli na klatkę i jest równa rozmiarowi pojedynczego kafla. Jest ona konieczna, bo gdyby nie była określona, to podczas opadania np. 30 pikseli na klatkę, bohater mógłby przelecieć przez kafel poziomu (przesunięcie współrzędnych wierzchołków byłoby większe niż rozmiar kafla). To tak gwoli ścisłości.


Ok, w dużym skrócie byłoby tyle.

Jeśli kogoś potrzebuje szczegółów to z chęcią je podam, choć tak jak wspomniałem – ten projekt jest jedynie eksperymentem. Jednym z jego założeń był opis ruchu na podstawie liczb całkowitych**1** oraz liczenie klatek zamiast czasu na podstawie delty, czyli podobnie jak miało to miejsce w grach na Famicoma. O ile było to fajne wyzwanie, to taka technika nie jest używana we współczesnych grach, więc nie polecam jej stosować.


[1] Wyjątkiem jest ruch świetlików po okręgu, do czego użyłem liczb zmiennoprzecinkowych. Co prawda floaty nie są konieczne (bo mamy np. odmianę Midpoint circle algorithm bazującą na intach), jednak użycie floatów było po prostu krótsze w implementacji.

1 użytkowników online, w tym zalogowanych: 0, gości: 1