Poruszanie myszą - top down

Piątek, 06 Października 2023, 09:10
Czas czytania 4 minuty, 39 sekund
Zgodne z GM: gms2 gms1 gm8 gm7 gm6 gm5
W tym artykule dowiesz się, jak zrobić poruszanie typu "kliknij i podejdź", jak w grach typu Diablo, czy wszlekiego rodzaju Point and Clickach. Skorzystamy z funkcji: mp_linear_step, mp_potential_step, move_towards_point.
W tym kursie chciałbym wam przedstawić różne podejścia do sterowania postacią za pomocą myszy w grze typu top-down (z widokiem z góry).

Stwórzcie nowy projekt, a w nim dwa sprite'y, o rozmiarach 32x32. Ja skorzystałem z gotowej paczki z kenney.nl. Jeśli używacie grafik w innym rozmiarze, warto je przeskalować - założeniem tego artykułu jest korzystanie z domyślnej gamemakerowej siatki 32x32 do ustawiania obiektów, gdyż jest to też artykuł wyjściowy dla kolejnego wpisu, dotyczącego wyrównywania do siatki.

Tworzymy dwa obiekty:
- obj_player
- obj_wall, któremu zaznaczamy "solid".

Grafika: /upload/ajax/20231006_f097a2e7310308f71077b5868bf7f8b4.png

Sterowanie myszą będzie działać na tej zasadzie, że po kliknięciu zapamiętamy gdzie jest miejsce docelowe i tak długo, jak do niego nie dojdziemy, postać będzie się tam przemieszczać.

W evencie Create obj_player:
koddestx = x;
desty = y;
Ustalamy, że miejsce docelowe jest takie samo, jak obecne położenie instancji obiektu gracza.

Następnie, dodajemy event Mouse > Global > Global Left Pressed, a więc oznaczający kliknięcie gdziekolwiek w całym obszarze gry i tylko sam moment kliknięcia (nie przytrzymywanie).

Wskazówka:
Zdarzenia myszy bez prefixu Global oznaczają kliknięcie na instancji obiektu i działają tylko jeśli obiekt posiada maskę kolizji o rozmiarze przynajmniej 1x1 pikseli (sprawdzana jest kolizja z czubkiem kursora). Prefix global "łapie" wszystkie kliknięcia, nie sprawdzając w ogóle kolizji.

W tym evencie ustawiamy docelowe miejsce zapamiętane w zmiennych destx/desty na obecne współrzędne myszy:
koddestx = mouse_x;
desty = mouse_y;

Następnie, wciąż w obiekcie obj_player, dodajemy event Step, w którym tworzymy kod wykonywany, gdy obecna pozycja x lub y nie jest pozycją docelową. Przedstawię 3 przykładowe warianty funkcji, których można tutaj użyć, chociaż bardziej zaawansowani użytkownicy zapewne znaleźli by kolejne rozwiązania.

mp_linear_step - liniowe dojście
Najprostszą funkcją dojścia do danego miejsca będzie funkcja mp_linear_step. Linear, czyli liniowa, spowoduje dojście do danego punktu po linii prostej. Pierwsze dwa argumenty to pozycja x,y docelowego miejsca, trzeci to prędkość na klatkę obrazu, a ostatni mówi nam, czy sprawdzać kolizje z wszystkimi dowolnymi obiektami (true), czy tylko z takimi typu solid (nie wszystkie - false). W naszym przykładzie czwarty argument ma więc wartość false, bowiem chcemy sprawdzać kolizje z obiektem obj_wall z zaznaczoną flagą "solid", a nie z dowolnym obiektem. Funkcje mp_ mają też tę zaletę, że jeśli dystans do punktu docelowego jest mniejszy niż wybrana prędkość, to poruszą one postać o tę mniejszą wartość.

kodif (x != destx or y != desty) {
mp_linear_step(destx, desty, 4, false);
}

Uwaga!
Chociaż gracz zatrzyma się, gdy trafi na ścianę, jesty x/y nie będą równe docelowym - i chociaż realnie nie ma ruchu, wciąż będzie trwało sprawdzanie czy jest on możliwy (np. usunięcie ściany spowodowałoby kontynuację).

Aby warunek dotarcia na miejsce if (x == destx or y == desty) w opisanym powyżej przypadku zaczął być spełniany, zatrzymanie można wykryć w taki sposób:
kodif (x != destx or y != desty) {
mp_linear_step(destx, desty, 4, false);
/* nowy warunek */
if (x = xprevious and y = yprevious) { /* jeśli od poprzedniej klatki ruszyliśmy się 0 pikseli */
destx = x; /* zmieniamy miejsce docelowe na obecne */
desty = y;
}
}

mp_potential_step
Aby omijać przeszkody, można skorzystać z funkcji mp_potential_step. Przyjmuje podobne argumenty jak poprzednia, ale stara się "omijać" przeszkody, znajdując drogę naokoło.

kodif (x != destx or y != desty) {
mp_potential_step(destx, desty, 4, false);
}

Wskazówka:
Jeśli punkt docelowy będzie znajdował się w miejscu, do którego nie ma dostępu (zamknięte pomieszczenie), postać zacznie krążyć kółka wokół najbliższej ściany która prowadzi do tego miejsca. Aby zatrzymać ostatecznie postać, można założyć, że czas dojścia do danego miejsca nie powinien wynosić więcej niż 2-3x więcej klatek, niż odległość w linii prostej, podzielona przez prędkość - time = point_distance(x, y, destx, desty) * spd * 3;. Można by w momencie kliknięcia myszą ustawiać alaram, który po tylu klatkach kończy ruch - ale dokładne ustawienie zależy od typu gry. Najlepiej po prostu nie robić takich pułapek, lub pusty obszar wypełnić niewidocznym, rozciągniętym obiektem typu solid.

move_towards_point
Ostatnia funkcja, która zgodnie z dokumentacją być może wydawała by się najbardziej oczywista, wybrana została nieprzypadkowo jako ostatnia. Problem z move_towards_point jest bowiem taki, że ta funkcja porusza postać wyłącznie o podaną przez nas prędkość, więc w sytuacji, gdy zostaje mniej pikseli do zrobienia (np. prędkość równa się 10, a został 1 piksel), postać "przeskoczy" punkt docelowy (poruszając się o te wybrane 10 pikseli, a więc o 9 za daleko).
kodif (x != destx or y != desty) {
move_towards_point(destx, desty, 4); /* porusz się z prędkością 4 pikseli */
}

Oczywiście i temu da się zaradzić - wytarczy sprawdzić, czy odległość do punktu docelowego jest mniejsza niż wybrana przez nas prędkość:
kodif (x != destx or y != desty) {
if (point_distance(x, y, destx, desty) <= 4) {
/* jeśli zostało mniej lub równo 4 piksele, ustaw postać w miejscu docelowym*/
x = destx;
y = desty;
} else {
move_towards_point(destx, desty, 4);
}
}

Sztuczka polega na tym, że skoro zostało nie więcej niż 4 piksele, jakikolwiek ruch sprawi wrażenie, że wykonaliśmy po prostu kruszy ruch i dotarliśmy na miejsce - dla ludzkiego oka będzie nie do zauważenia.
Warunek o dystansie jest pierwszy, aby zapobiec wykonaniu się funkcji move, oraz aby nie wykonać dodatkowego ruchu już po niej (w tej sytuacji bowiem postać ruszyła by się najpierw o 4 piksele, a potem o dodatkowe między 0.1 a 4 piksele, co w skrajnym przypadku dałoby aż 8 pikseli i było widoczne, jako "przyciągnięcie").
Komentarze (łącznie 0):
Nie ma jeszcze żadnego komentarza. Czas to zmienić

Najnowsze wersje GameMakera:

LTS
2026.0.0.16 • 2022.0.3.99
wydana 104 dni temu
Beta
2026.100.0.1142 •
2026.100.0.1093
 GMRT beta 0.21.0

wydana 7 dni temu
= IDE, = Runtime, = GMRT
Użytkownicy online
1 użytkownik aktywny:
gości: 1,
(~ostatnie 15 minut)
Discord
22 użytkownicy online na discordzie:
Miłosz, 🧁Cupcake🧁, Nikas, Nitro Slav, Carl-bot, GibkiKaktus, tomqz, Tidżi, r..., Murrri, Dyno, szmalu, OdrzuconyKrakers, Ulti, bagno, antek, MrTesterr, g..., l..., moeglich, Add92, Shockah
Shoutbox
gnysek (13:01, 04.09.26)
Dzięki! :D
S
Sutikku (11:08, 04.09.26)
gmclan to moje go-to żeby sprawdzić czy działa mi internet
Wojo (16:25, 03.09.26)
Pytanie tylko co z tym faktem możemy zrobić my jako użytkownicy? - przede wszystkim udzielać się więcej. Ja ogólnie lubię dużo mówić, dużo pisać, mało robić, ale poza mną obecnie jest może kilku użytkowników, którzy coś tutaj wkleją raz na ruski rok.
Wojo (09:25, 03.09.26)
Fora internetowe wyhamowały już dawno temu, ale teraz to nawet zaciągnęły ręczny. Przecież nic się na nich nie dzieje na ten moment. Trzeba czekać, aż może aktywność wróci
gnysek (01:00, 22.08.26)
Będzie wkrótce spory plugin do GM z kilkunastoma QoL, trwają prace ;)
Wojo (11:35, 13.08.26)
Gratuluję i dziękuję w imieniu aktywnych użytkowników GMClanu!
gnysek (23:13, 12.08.26)
Szybkie info, że GMCLAN chodzi obecnie na cloudflare więc wyciąłem boty :)
Wojo (21:21, 28.06.26)
na ten moment czerwiec prezentuje się dość przyzwoicie jesli chodzi o ilosc wpisow w shoutboxie bo aż trzy! a taki styczeń zero :0
gnysek (15:06, 10.06.26)
oraz pokój na świecie
Starsze wpisy znajdziesz w Archiwum.
Ankieta
Ile zarobiłeś do tej pory na grach stworzonych w GM?