Nasze strony: gmclan.org gameonly.pl ps-plus.pl gameswithgold.pl n-switch.pl hmt.pl
Fastbar
Powrót do strony głównej
Trzymaj pliki na gmclan.org!
Game Maker w pytaniach i odpowiedziach!
Polska dokumentacja
Tabela wyników ligi 24
Pobierz GM
Akademia GMCLANu
Akademia GMCLANu
Menu
Panel użytkownika
Jesteś niezalogowany!

Nie masz konta? Zarejestruj się
Kategorie bazy artykułów
Artykuły -> AKADEMIA: Game Maker Studio 2 -> Recepty
Lekcja w akademii
×
Zapisywanie użytych przedmiotów w grze (np. skrzyń)
autor: gnysek (11.10.22), czas czytania: 6 minut, 9 sekund
Pokaż na pełym ekranie× Zamknij pełny ekran
Gotowy przykład pobierzecie tutaj: gmclan.org/.../saving_used_items.yyz

Jednym z zagadnień, które często pojawia się w grach, jest zapamiętywanie, czy gracz wykonał już w jakimś miejscu akcje. Kto programuje już chwilę, całkiem szybko wpadnie na to, że najlepiej skorzystać w tym celu z tablic i tam zapisywać jako true/false informację na ten temat.
Problem pojawia się jednak w momencie, gdy trzeba jakoś oznaczyć przedmioty w roomach, żeby było wiadomo, który indeks w tablicy odpowiada któremu przedmiotowi. Prostym sposobem byłaby zmienna, która przechowuje indeks tablicy, ustawiana np. w Creation Code, ale taki system nie jest łatwy do zarządzania, bowiem gdy usuniemy pewien przedmiot, nie możemy nie wiedzieć którego nam brakuje. Poza tym, gdy nagle dodamy nowy etap, możemy nie pamiętać, na jakiej cyfrze skończyliśmy (teoretycznie tabela powinna mieć ten sam rozmiar, ale co jeśli od razu zarezerwowaliśmy np. 100 miejsc na zaś?). Innym sposobem którym widziałem, jest ds_mapa, w której przy otwarciu zapisujemy klucze w postaci:
object_get_name(id) + string(x) + string(y), gdyż raczej nie zdarzy się, że dwa obiekty których stan chcemy zapamiętać stoją w tym samym miejscu (to wręcz przeszkadzałoby np. w ich użyciu i wykryciu kolizji). Niestety, wszystko się zepsuje w momencie, gdy taki obiekt przesuniemy. Warto jednak wiedzieć, że ten sposób wymaga najmniej kodu, więc nie odradzam go całkowicie.

Ja natomiast wybrałem sposób nieco trudniejszy w implementacji (ok 30 linijek kodu), ale za to bardziej praktyczny. Korzysta on z pewnej mało używanej właściwości GameMakera - mianowicie faktu, że każda dodana w roomie instancja dostaje swoją "stałą", której można użyć w kodzie (w formacie inst_8ZNAKOW). Plusem jest to, że można tę nazwę ustawić samemu (ale nie trzeba), można też swobodnie usunąć przedmiot ze środka kolejki (i automatycznie kolorowanie składni przestanie go podświetlać, co ułatwi jego zlokalizowanie), a można go nawet przenieść do innego roomui i nadać starą nazwę, żeby kod nadal działał. Wykorzystuje on dwie tablice, dzięki czemu łatwo go też zapisać do pliku.

Wskazówka:
Nie korzystamy z DS_MAP, gdyż id ukrywające się pod stałą mogą się zmienić, gdy dodamy/usuniemy inne obiekty z danego roomu. Nigdy nie zakładaj, że id zasobów i instancji się nie zmienią - dlatego nie używaj ich jako kluczy struktur/tablic, oraz nie zapisuj do pliku.

Na początek stworzymy sobie dwa obiekty:

obj_player (dowolny sprite):

Kod poruszania w step:
kodx += (-keyboard_check(vk_left) + keyboard_check(vk_right)) * 4;
y += (-keyboard_check(vk_up) + keyboard_check(vk_down)) * 4;

Kod do testów, w Key Pressed R
kodroom_restart();
Teraz stworzymy obj_chest, któremu stworzymy sprite z dwiema klatkami - skrzynią zamkniętą (0) i otwartą (1). Ustawmy też FPS w sprite na 0, dzięki czemu nie będzie się animował i zawsze pokaże zerową klatkę jako domyślną.
obj_chest:

Create:
kodopened = false;
Kod w kolizji z obj_player:
kodif (opened == false) {
opened = true;
image_index = 1;
}

Teraz można w roomie wstawić jednego playera, kilka skrzyń i przetestować, czy działają kolizje i otwieranie skrzyń.



Etap drugi

Utwórzmy teraz nowy skrypt, ja nazwałem go "chest_scripts". Skorzystamy z właściwości GameMakera 2.3+ (2021+), która pozwala na zdefiniowanie zmiennych przed rozpoczęciem gry - wystarczy napisać kod poza function(). Zdefiniujemy sobie tablicę z naszymi skrzyniami (w moim przypadku są cztery), kopiując ich identyfikatory z room editora. Znajdziecie je w inspektorze po zaznaczeniu instancji, lub klikając dwa razy (input tekstowy z inst_XXXXX). Możecie je dowolnie zmienić, ale ja zostawiłem te losowe.



Stworzymy też od razu tablicę z informacją czy skrzynia jest otwarta/zamknięta - o tym samym rozmiarze, domyślnie z wartościami false. Żeby nie musieć jej zawsze modyfikować, skorzystamy z array_create, gdzie rozmiarem tablicy będzie rozmiar tablicy ze skrzyniami, pobrany przy pomocy array_length:

kodglobal.chests = [inst_18134AA6, inst_1C8CC5E6, inst_2B0F70D8, inst_1AFF602];
global.opened = array_create(array_length(global.chests), false);

Wskazówka:
Powyższy kod wykona się tylko gdy gra startuje i nie wykona się ani przy room_restart(), ani nawet przy game_restart();

Teraz, wypadałoby napisać funkcję, która sprawdza, czy dana skrzynia jest otwarta. W tym samym jedynym w naszej grze skrypcie, dodamy kod, który "przelatuje" tablicę i sprawdza, czy ID obecnej skrzyni w ogóle jest na liście i jeśli ją znajdzie, zwróci informację o tym, czy jest otwarta. Dzięki użyciu dwóch tablic, nawet jeśli zmienią się ID skrzyń w grze, nie zmieni się ich pozycja w tablicy. Gdyby skrzynia była kluczem ds_mapy, mimo tej samej "stałej", miałaby ona inną wartość i skrzynie by się losowo zamykały po zmianach w roomie - bowiem tę wartość GM ustawia dopiero w momencie kompilacji.
Funkcja, poza tym, że zwraca, czy skrzynia jest otwarta, od razu zmienia index sprite'a na 1.

kodfunction check_if_opened() {
image_index = 0;
var len = array_length(global.chests);
for(var i = 0; i < len; i++) {
if (global.chests[i] == id) {
if (global.opened[i] == true) {
image_index = 1;
return true;
}
}
}

return false;
}

Została ostatnia funkcja - zapisująca otwarcie skrzyni. Napiszemy ją w bardziej uniwersalny sposób, który pozwoli zapisać też w przyszłości zamknięcie skrzyni. Zmianę sprite'a wywołamy poprzednią funkcją check_if_opened(), dzięki czemu od razu obsłużymy oba przypadki otwarcia i zamknięcia.

kodfunction chest_save() {

var len = array_length(global.chests);
for(var i = 0; i < len; i++) {
if (global.chests[i] == id) {
global.opened[i] = opened;
check_if_opened(); // change sprite
return true; // exit for-loop early
}
}

return false;
}

Pozostaje poprawić kod w obj_chest, aby korzystał z nowych funkcji. Dodajemy/zmieniamy pierwszy skrypt w create, a drugi w step:

Create (po zmianach):
kodopened = false;
check_if_opened(); // nowa linijka

Kolizja:
kodif (opened == false) {
opened = true;
chest_save(); // zmieniona linijka
}

I to tyle. Wszystko działa, a po odpaleniu gry wystarczy wcisnąć "R", aby zobaczyć, że skrzynie są zapamiętane.

Co dalej?

Resetowanie otwarcia skrzyń (w menu, zaczynając/wczytując nową grę) wykonujemy przez ponowne wykonanie:
kodglobal.opened = array_create(array_length(global.chests), false);Warto na pewno utworzyć sobie z tego kawałka kodu funkcję, żeby nie powtarzać go dwa razy.

Natomiast zapis do pliku, pozostawiam już wam - ostatecznie można skorzystać z json_stringify do zapisu i json_parse do odczytu - dodając walidację długości i danych (najlepiej więc wczytać ją do nowej tablicy i porównać pętlą for, o długości najmniejszej (min) z dwóch długości - global.opened i wczytanych danych, rzutując wszystko na true/false).

Gotowy przykład pobierzecie tutaj: gmclan.org/.../saving_used_items.yyz
Komentarze
stron: 1

1


av

Adriann (22:17, 18.10.2022)

Wreszcie udało mi się przetestować, super przykład
Jedyny minus to potrzeba ręcznego umieszczenia listy obiektów ale w zasadzie to drobiazg.
Dobra robota!

av

gnysek (8:58, 19.10.2022)

Niestety, co prawda istnieje opcja automatycznego generowania takiej listy, ale jak usuniesz/dodasz dowolną instancję, lub zmienisz kolejność roomów, to całość może nie działać z sejvami. Można też zapisać stringa z nazwą obiektu i x oraz y, ale tutaj znów - przesunięcie rozwali stare gry.

W ten sposób który tutaj jest - można przenieść skrzynię w dowolne miejsce w dowolnym roomie, zachować lub podmienić "stałą" nazwy i gra nadal będzie działać (można też dać undefined jak chcemy skrzynię wywalić na zawsze, a zachować kompatybilność).

Teoretycznie jest to więc ręczna robota, ale w praktyce więcej czasu oszczędza go 10x tyle na przyszłość.

YYG chyba planowało tagi dla instancji w rommach i to by rozwiązało sprawę, no ale w ostatnich wersjach z plików .yy zniknęły takie wpisy, więc się wycofali.

av

Adriann (16:24, 19.10.2022)

Nie no jest spoko, i łatwo było dodać to do systemu zapisu

av

gnysek (14:13, 20.10.2022)

Być może da się zrobić wygodniejszy system, ale na pewno nie w tak małej ilości kodu. Lepszy wymagałby pewnie tyle pracy, że chyba szkoda się za niego brać (chyba, że ktoś by chciał zrobić z niego bibliotekę do dzielenia się ).

stron: 1

1



Dodaj komentarz:
Treść:

GMCLAN to serwis o programie Game Maker i nie tylko.
[ Polityka prywatności ]
Copyright © 2002-2022. GMCLAN.ORG
Wszelkie prawa zastrzeżone. Kopiowanie materiałów bez zgody redakcji zabronione!
© 2002-2017 Ranmus, © 2017-2022 {=|=} fable_inside();

[ Czas generowania strony: 0.03362 sekund ] [ Liczba zapytań MySQL: 10 ]