Mam problem z rozdzielczością kolizji AABB.
Rozwiązuję przecięcie AABB, rozwiązując najpierw oś X, a następnie oś Y. Ma to na celu uniknięcie tego błędu: http://i.stack.imgur.com/NLg4j.png
Obecna metoda działa dobrze, gdy obiekt porusza się w odtwarzaczu, a gracz musi zostać popchnięty poziomo. Jak widać na .gif, poziome kolce popychają gracza poprawnie.
Jednak gdy pionowe kolce przemieszczają się do gracza, oś X jest nadal rozpatrywana jako pierwsza. To uniemożliwia „wykorzystanie kolców jako podnośnika”.
Kiedy gracz porusza się do pionowych kolców (pod wpływem grawitacji, wpada w nie), pcha się na oś Y, ponieważ na początku na osi X nie zachodziło na siebie nakładanie się.
Coś, co wypróbowałem, to metoda opisana w pierwszej odpowiedzi tego linku: wykrywanie kolizji obiektów prostokątnych 2D
Jednak kolce i poruszające się obiekty poruszają się, zmieniając ich położenie, a nie prędkość, i nie obliczam ich następnej przewidywanej pozycji, dopóki nie zostanie wywołana metoda Update (). Nie trzeba dodawać, że to rozwiązanie również nie działało. :(
Muszę rozwiązać kolizję AABB w taki sposób, aby oba opisane powyżej przypadki działały zgodnie z przeznaczeniem.
To jest mój obecny kod źródłowy kolizji: http://pastebin.com/MiCi3nA1
Byłbym naprawdę wdzięczny, gdyby ktoś mógł się temu przyjrzeć, ponieważ ten błąd występował w silniku od samego początku, a ja starałem się znaleźć dobre rozwiązanie, bez żadnego sukcesu. To poważnie zmusza mnie do spędzania nocy na przeglądaniu kodu kolizji i uniemożliwia mi przejście do „zabawy” i kodowanie logiki gry :(
Próbowałem wdrożyć ten sam system kolizji, co w wersji demonstracyjnej platformówki XNA AppHub (kopiując i wklejając większość rzeczy). Jednak błąd „skakania” występuje w mojej grze, podczas gdy nie występuje w wersji demo AppHub. [bug bug: http://i.stack.imgur.com/NLg4j.png ]
Aby skoczyć, sprawdzam, czy gracz jest „na ziemi”, a następnie dodaj -5 do Velocity.Y.
Ponieważ Velocity.X gracza jest wyższy niż Velocity.Y (patrz czwarty panel na schemacie), onGround jest ustawiany na true, kiedy nie powinien, i pozwala graczowi skakać w powietrzu.
Wydaje mi się, że tak się nie dzieje w wersji demonstracyjnej AppHub, ponieważ prędkość gracza nigdy nie będzie wyższa niż Velocity.Y, ale mogę się mylić.
Rozwiązałem to wcześniej, rozwiązując najpierw na osi X, a następnie na osi Y. Ale to zepsuło kolizję z kolcami, jak powiedziałem powyżej.
źródło
Odpowiedzi:
OK, zorientowałem się, dlaczego demo platformówki XNA AppHub nie ma błędu „przeskakiwania”: demo testuje płytki kolizji od góry do dołu . Gracz stojący naprzeciw „ściany” może nakładać się na wiele płytek. Kolejność rozwiązywania jest ważna, ponieważ rozwiązanie jednej kolizji może również rozwiązać inne kolizje (ale w innym kierunku).
onGround
Właściwość ma wartość tylko wtedy, gdy kolizja jest rozwiązany poprzez wciśnięcie player up na osi y. Ta rozdzielczość nie nastąpi, jeśli poprzednie rozdzielczości pchały odtwarzacz w dół i / lub w poziomie.Byłem w stanie odtworzyć błąd „skakania” w demie XNA, zmieniając ten wiersz:
do tego:
(Poprawiłem także niektóre stałe związane z fizyką, ale to nie powinno mieć znaczenia).
Być może sortowanie
bodiesToCheck
na osi Y przed rozwiązaniem kolizji w grze naprawi błąd „skakania”. Sugeruję rozwiązanie kolizji na „płytkiej” osi penetracji, jak robi to demo XNA i sugeruje Trevor . Zauważ również, że odtwarzacz demonstracyjny XNA jest dwa razy wyższy niż kolidujące płytki, co zwiększa prawdopodobieństwo wielokrotnego zderzenia.źródło
Position = nextPosition
bezpośrednio wfor
pętli, w przeciwnym razieonGround
nadal występują niepożądane rozdzielczości (ustawienia ) kolizji . Gracz powinien być popychany pionowo w dół (i nigdy w górę) podczas uderzania w sufit, dlategoonGround
nigdy nie powinien być ustawiony. W ten sposób robi to demo XNA i nie mogę tam naprawić błędu „pułapu”.onGround
jest ustawiony, więc nie mogę ustalić, dlaczego skakanie jest niepoprawnie dozwolone. Demo XNA aktualizuje swoją analogicznąisOnGround
właściwość wewnątrzHandleCollisions()
. Ponadto, po ustawieniuVelocity.Y
na 0, dlaczego grawitacja nie zaczyna już przesuwać odtwarzacza w dół? Domyślam się, żeonGround
właściwość jest niepoprawnie ustawiona. Zobacz, jak aktualizuje się wersja demo XNApreviousBottom
iisOnGround
(IsOnGround
).Najprostszym rozwiązaniem będzie sprawdzenie obu kierunków kolizji z każdym obiektem na świecie przed rozwiązaniem kolizji i po prostu rozwiązanie mniejszej z dwóch wynikowych „kolizji złożonych”. Oznacza to, że rozpatrujesz w najmniejszej możliwej ilości, zamiast zawsze najpierw rozstrzygać x lub zawsze najpierw y.
Twój kod wyglądałby mniej więcej tak:
duża rewizja : po przeczytaniu komentarza do innych odpowiedzi, wydaje mi się, że w końcu zauważyłem niepotwierdzone założenie, które spowoduje, że to podejście nie zadziała (i które wyjaśnia, dlaczego nie mogłem zrozumieć problemów, że niektóre - ale nie wszystkie - ludzie widzieli z takim podejściem). Aby to rozwinąć, oto trochę więcej pseudokodów, pokazujących bardziej wyraźnie, jakie funkcje, do których się wcześniej odwoływałem, powinny faktycznie działać:
To nie jest test „pary obiektów”; nie działa, testując i rozwiązując poruszający się obiekt indywidualnie dla każdego kafelka mapy świata (takie podejście nigdy nie zadziała niezawodnie i zawodzi w coraz bardziej katastrofalny sposób, gdy zmniejszają się rozmiary kafelków). Zamiast tego testuje jednocześnie poruszający się obiekt względem każdego obiektu na mapie świata, a następnie rozwiązuje go na podstawie kolizji z całą mapą świata.
W ten sposób upewniasz się, że (na przykład) pojedyncze kafelki w ścianie nigdy nie odbijają gracza w górę iw dół między dwoma sąsiadującymi kafelkami, w wyniku czego gracz zostaje uwięziony w jakiejś nieistniejącej przestrzeni „między” nimi; odległości rozwiązywania kolizji są zawsze obliczane aż do pustej przestrzeni na świecie, a nie tylko do granicy pojedynczej płytki, na której może znajdować się kolejna solidna płytka.
źródło
if(fabs( yDistanceToResolve ) < fabs( xDistanceToResolve ) || xDistanceToResolve == 0) { object->Move( 0.f, yDistanceToResolve ); } else { object->Move( xDistanceToResolve, 0.f ); }
Edytować
Mam poprawiła go
Wydaje się, że kluczową rzeczą, którą zmieniłem, jest klasyfikacja skrzyżowań.
Skrzyżowania są albo:
i rozwiązuję je w tej kolejności
Zdefiniuj kolizję z ziemią jako gracz znajdujący się co najmniej 1/4 drogi na płytce
To jest zderzenie z ziemią, a gracz ( niebieski ) usiądzie na płytce ( czarny )
Ale to NIE jest zderzenie z ziemią, a gracz „poślizgnie się” po prawej stronie płytki, kiedy na nią wyląduje
Dzięki tej metodzie gracz nie będzie już przyłapany na ścianach
źródło
Uwaga:
Mój silnik wykorzystuje klasę wykrywania kolizji, która sprawdza kolizje ze wszystkimi obiektami w czasie rzeczywistym, tylko gdy obiekt się porusza i tylko na kwadratach siatki, które aktualnie zajmuje.
Moje rozwiązanie:
Kiedy natrafiłem na takie problemy w platformówce 2D, zrobiłem coś takiego:
- (silnik wykrywa możliwe kolizje, szybko)
- (gra informuje silnik, aby sprawdził dokładne kolizje dla konkretnego obiektu) [zwraca wektor obiektu *]
- (obiekt patrzy na głębokość penetracji, a także poprzednią pozycję względem poprzedniej pozycji innego obiektu, aby ustalić, z której strony się wysunąć)
- (obiekt się porusza i uśrednia jego prędkość z prędkością obiektu (jeśli obiekt się porusza))
źródło
W grach wideo, które zaprogramowałem, podejście polegało na tym, aby mieć funkcję informującą, czy twoje stanowisko jest prawidłowe, tj. bool ValidPos (int x, int y, int wi, int he);
Funkcja sprawdza ramkę ograniczającą (x, y, wi, he) względem całej geometrii w grze i zwraca false, jeśli występuje jakieś skrzyżowanie.
Jeśli chcesz się poruszyć, powiedz w prawo, zajmij pozycję gracza, dodaj 4 do x i sprawdź, czy pozycja jest prawidłowa, jeśli nie, sprawdź za pomocą + 3, + 2 itd.
Jeśli potrzebujesz również grawitacji, potrzebujesz zmiennej, która rośnie, dopóki nie uderzysz w ziemię (uderzanie w ziemię: ValidPos (x, y + 1, wi, he) == prawda, y jest tutaj dodatnie w dół). jeśli możesz przesunąć tę odległość (np. ValidPos (x, y + grawitacja, wi, on) zwraca wartość true) upadasz, przydatne czasem, gdy nie powinieneś być w stanie kontrolować swojej postaci podczas upadku.
Teraz twoim problemem jest to, że masz obiekty w swoim świecie, które się poruszają, więc przede wszystkim musisz sprawdzić, czy twoja stara pozycja jest nadal aktualna!
Jeśli tak nie jest, musisz znaleźć odpowiednią pozycję. Jeśli obiekty w grze nie mogą poruszać się szybciej niż powiedzmy 2 piksele na obrót gry, musisz sprawdzić, czy pozycja (x, y-1) jest poprawna, a następnie (x, y-2), a następnie (x + 1, y) itd. itd. należy sprawdzić całą przestrzeń między (x-2, y-2) do (x + 2, y + 2). Jeśli nie ma prawidłowej pozycji, oznacza to, że zostałeś „zmiażdżony”.
HTH
Valmond
źródło
Mam kilka pytań, zanim zacznę na nie odpowiadać. Po pierwsze, w oryginalnym błędzie, w którym utknąłeś w ścianach, czy te płytki po lewej stronie były pojedynczymi kafelkami, a nie jedną dużą płytką? A jeśli tak, to czy gracz utknął między nimi? Jeśli tak, to upewnij się, że Twoje nowe stanowisko jest prawidłowe . Oznacza to, że musisz sprawdzić, czy występuje kolizja w miejscu, w którym każesz graczowi się ruszyć. Rozwiąż więc minimalne przemieszczenie, jak opisano poniżej, a następnie przenieś gracza na podstawie tego tylko wtedy, gdy możerusz się tam. Niemal też pod nosem: P To faktycznie wprowadza kolejny błąd, który nazywam „przypadkami narożnymi”. Zasadniczo w kategoriach narożników (jak lewy dolny róg, w którym poziome kolce wychodzą w twoim .gif, ale jeśli nie byłoby kolców) nie rozwiązałoby kolizji, ponieważ wydawałoby się, że żadna z generowanych przez ciebie rozdzielczości nie prowadzi do prawidłowej pozycji . Aby rozwiązać ten problem, po prostu sprawdź, czy kolizja została rozwiązana, a także listę wszystkich minimalnych rozdzielczości penetracji. Następnie, jeśli kolizja nie została rozwiązana, zapętlaj każdą wygenerowaną rozdzielczość i śledź maksymalne rozdzielczości X i maksymalne Y (maksima nie muszą pochodzić z tej samej rozdzielczości). Następnie rozstrzygnij kolizję na tych maksimach. Wydaje się, że to rozwiązuje wszystkie problemy, jak również te, które napotkałem.
Kolejne pytanie, czy kolce pokazują jedną płytkę, czy pojedyncze płytki? Jeśli są to pojedyncze cienkie płytki, być może będziesz musiał zastosować inne podejście do tych poziomych i pionowych niż te, które opisuję poniżej. Ale jeśli są to całe kafelki, powinno to działać.
W porządku, więc w zasadzie to właśnie opisywał @Trevor Powell. Ponieważ używasz tylko AABB, wszystko, co musisz zrobić, to dowiedzieć się, ile jednego prostokąta przenika drugi. To da ci ilość na osi X i Y. Wybierz minimum z dwóch i przesuń kolidujący obiekt wzdłuż tej osi o tę kwotę. To wszystko, czego potrzebujesz, aby rozwiązać kolizję AABB. NIGDY nie będziesz musiał poruszać się wzdłuż więcej niż jednej osi w takiej kolizji, więc nigdy nie powinieneś być zdezorientowany co do tego, który ruch wykonać jako pierwszy, ponieważ będziesz poruszał się tylko o minimum.
Oprogramowanie Metanet ma klasyczny tutorial podejścia tutaj . Zmienia się również w inne kształty.
Oto funkcja XNA, którą utworzyłem, aby znaleźć wektor nakładania się dwóch prostokątów:
(Mam nadzieję, że jest to łatwe do naśladowania, ponieważ jestem pewien, że istnieją lepsze sposoby na jego wdrożenie ...)
isCollision (Rectangle) to dosłownie tylko połączenie z XNA's Rectangle.Intersects (Rectangle).
Przetestowałem to na ruchomych platformach i wydaje się, że działa dobrze. Zrobię jeszcze kilka testów bardziej podobnych do twojego .gif, aby się upewnić i zgłoś się, jeśli to nie zadziała.
źródło
To proste.
Przepisz kolce. To wina kolców.
Twoje kolizje powinny mieć miejsce w dyskretnych, namacalnych jednostkach. Problem, o ile widzę, to:
Problem dotyczy kroku 2, a nie 3 !!
Jeśli starasz się, aby rzeczy stały się solidne, nie powinieneś pozwalać, aby przedmioty wsuwały się w siebie w ten sposób. Gdy znajdziesz się na skrzyżowaniu, jeśli stracisz miejsce, problem staje się trudniejszy do rozwiązania.
Kolce powinny idealnie sprawdzić istnienie gracza, a kiedy się poruszają, powinny go popchnąć w razie potrzeby.
Łatwym sposobem na osiągnięcie tego jest posiadanie przez gracza
moveX
imoveY
funkcje, które rozumieją krajobraz i będą pchały gracza o określoną deltę lub tak daleko, jak to możliwe bez uderzania w przeszkodę .Zwykle będą wywoływane przez pętlę zdarzeń. Mogą być jednak również wywoływane przez obiekty w celu popchnięcia gracza.
Oczywiście gracz nadal musi reagować na kolce jeśli on idzie do nich .
Nadrzędną zasadą jest, że po każdym ruchu obiektu powinien kończyć się w pozycji bez przecięcia. W ten sposób niemożliwe jest uzyskanie usterki, którą widzisz.
źródło
moveY
nie uda się usunąć skrzyżowania (ponieważ przeszkadza mu inna ściana), możesz ponownie sprawdzić skrzyżowanie i spróbować moveX lub po prostu go zabić.