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:

Stabilna
2024.4.0.137 • 2024.4.0.168
wydana 16 dni temu
LTS
2022.0.2.51 • 2022.0.2.49
wydana 214 dni temu
Beta
2024.400.0.556 • 2024.400.0.571
wydana 18 dni temu
= IDE, = Runtime
Użytkownicy online
1 użytkownik aktywny:
gości: 1,
(~ostatnie 15 minut)
Discord
17 użytkowników online na discordzie:
Papaj, Alice, Carl-bot, RogerDodg3r, Draczeq, p..., Add92, Kowu, Tival, LadyLush, Chell, Arrekin, Dyno, LeD, l..., Krzysiek1250, TobiasM (Morgo)
Shoutbox
Chell (11:15, 17.05.24)
nareszcie będzie można zaimplementować padający śnieg w gierce
gnysek (18:33, 16.05.24)
JS zmierza do GM xD
I am Lord (16:29, 05.05.24)
Czaję
gnysek (13:38, 04.05.24)
Nie wbudowują tego przez warunki licencyjne. Ale kto wie, może jako prefab zobaczymy to już w kolejnym wydaniu, bo wtedy prefaby mają zostać dodane.
I am Lord (12:17, 03.05.24)
Aha to trzeba coś tam jeszcze kombinować tak
gnysek (18:05, 01.05.24)
Tak, w osobnym rozszerzeniu (na githubie YYG).
I am Lord (17:56, 30.04.24)
funkcje z fmod są już?
gnysek (20:44, 11.04.24)
Niektórzy dlatego wybierają GMEdit. Ale ja liczę na Code Editor 2, tylko na razie zbyt zbugowany jest.
Tymon (16:11, 11.04.24)
Stitch dla mnie osobiście jest lepszy bo nie musze kopać się z interfejsem GMa i mogę tylko pisać kod.
Starsze wpisy znajdziesz w Archiwum.
Ankieta
Ile zarobiłeś do tej pory na grach stworzonych w GM?