Silnik gier 2D: pytanie „co jest TERAZ w tym miejscu?” — zapytania obszarowe
Silnik gier w Moonlight Animate umiał już dwie rzeczy z fizyki na żądanie: rzucić promień
(raycast — „co jest na tej linii?", np. linia wzroku wroga) oraz utrzymywać trwałą strefę
(Area2D — „kto właśnie wszedł/wyszedł z pola?"). Brakowało trzeciego, bardzo podstawowego pytania:
„jakie obiekty znajdują się TERAZ w tym prostokącie albo okręgu?" — bez zakładania na stałe żadnej
strefy, w dowolnym miejscu, jednym wywołaniem. Ten klocek właśnie dołożyliśmy.
Dwie funkcje, jedno pytanie
-- Obrażenia obszarowe (AoE): rań wszystko w promieniu 128 px wokół wybuchu
for _, cel in ipairs(mp.query_circle(wybuch_x, wybuch_y, 128)) do
zadaj_obrazenia(cel, 25)
end
-- Czy miejsce spawnu jest wolne? (prostokąt 64×64 wokół punktu)
if #mp.query_box(spawn_x, spawn_y, 64, 64) == 0 then
stworz_przeciwnika(spawn_x, spawn_y)
end
mp.query_box(x, y, szerokość, wysokość [, maska])— zwraca listę wszystkich brył, które w tej chwili nakładają się na prostokąt o środkux,yi podanych pełnych wymiarach.mp.query_circle(x, y, promień [, maska])— to samo dla okręgu.
Odpowiedź jest natychmiastowa i nie wymaga tworzenia żadnego węzła w scenie. To dopełnia komplet: promień pyta o linię, strefa Area2D o trwałe pole z historią wejść/wyjść, a nowe zapytania o dowolny obszar w danej chwili.
Dokładność, nie zgrubne pudło
Najłatwiejszy sposób na takie zapytanie to sprawdzić, czy prostokąty otaczające obiekty zahaczają o zapytany obszar. Szybkie — ale niedokładne: obiekt leżący tuż za rogiem okręgu (wewnątrz kwadratu opisanego na kole, lecz poza samym kołem) zostałby błędnie zaliczony jako trafiony. W grze to realny błąd: cios „zza węgła", spawn uznany za zajęty przez sąsiada, który wcale nie stoi na drodze.
Dlatego zapytania robią dokładny test kształtu. Dla query_circle liczy się prawdziwa odległość
od środka, nie kwadrat. Obiekt z rogu okręgu nie trafia na listę — dokładnie tak, jak gracz się
tego spodziewa.
Detale, które robią różnicę
- Bez powtórzeń. Obiekt zbudowany z kilku koliderów (np. postać z osobnym kolizerem tułowia i głowy) pojawia się na liście raz, nie tyle razy, ile ma kształtów.
- Deterministyczna kolejność. Wynik jest zawsze uporządkowany tak samo między uruchomieniami — ważne dla powtarzalności rozgrywki, testów i systemów replay.
- Tylko bryły fizyczne. Strefy-wyzwalacze (sensory) są pomijane — pytasz o solidne obiekty,
nie o niewidzialne pola triggerów, które mają własny mechanizm (
mp.bodies_in_area). - Warstwy kolizji. Opcjonalna
maskazawęża zapytanie do wybranych warstw — pocisk gracza może pytać tylko o warstwę wrogów, ignorując samego gracza i dekoracje. - Odporność na śmieciowe wejście. Ujemny promień jest przycinany do zera zamiast po cichu gubić trafienia; promień zerowy działa jak precyzyjne zapytanie punktowe („co jest dokładnie tutaj?").
Zapytania działają tak samo w skryptach Lua uruchamianych na desktopie i — po kompilacji do przeglądarki — w wersji web, bo cały silnik rozgrywki to jeden wspólny kod bez zależności od platformy.
Gdzie się przydają
Obrażenia obszarowe zaklęć i wybuchów, sprawdzanie czy jest miejsce na postawienie jednostki/wieży, zaznaczanie oddziałów prostokątem w grach strategicznych, wykrywanie „czy gracz stoi na przełączniku", zbieranie przedmiotów w zasięgu — wszędzie tam, gdzie logika pyta o obszar, a nie o pojedynczy punkt czy stałą strefę. Kolejny mały, ale fundamentalny klocek dokładany do silnika gier 2D w Moonlight Animate.