GM 2023+: Singleton w GM

Poniedziałek, 13 Lutego 2023, 23:52
Czas czytania 3 minuty, 23 sekundy
Zgodne z GM: gms2
GM 2023 wprowadza nową składnię, która pozwala pobierać statyczne wartości konstruktorów za pomocą prostszej składni.
GameMaker w wersjach 2023+ (od 2023.1) wprowadza nowe funkcje - static_get() i static_set(), które pozwalają pobrać "statyczną" część funkcji, struktur i konstruktorów, lub nadpisać tę przypisaną do jakiegoś obiektu.

Wskazówka:
Zwykła struktura domyślnie nie ma statycznej części, jedynie te utworzone przez = new nazwa_konstruktora();, lub takie, na których użyto static_set()

Ot, załóżmy, że mamy taki kod:

kodfunction add_points(_score = 0) {
static points = 0;
points = points + _score;
return points;
}

Dzięki zastosowaniu "static", zmienna "score" jest na stałe przypisana do funkcji "add_score" i nie znika pomiędzy jej wywołaniami (wszystkie inne zmienne, jeśli nie są poprzedzone var, będą odczytywać/zapisywać zmienną o danej nazwie w obecnym kontekście, najczęściej w obiekcie).

Takie statyczne, są tak naprawdę strukturą "ukrytą" w zakamarkach GMa (mona założyć, że funkcja/struktura sprawdzając swoje zmienne, najpierw sprawdza, czy ma swoją lokalną wersję, potem czy ma statyczną, a dopiero gdy nie znajdzie obu rzuca błędem o nieznanej zmiennej) i funkcja static_get pozwala nam dostać bezpośrednią referencję do tej normalnie ukrytej struktury. Możemy wtedy zmienić jej wartości spoza danej funkcji, czy nawet dodać/usunąć wartości. Dzięki static_set możemy je nawet podmienić - i coś, co było wcześniej instancją jednej klasy, nagle stanie się instancją innej.

Twócy GMa postanowili, że pozwolą się dobrać się do tej struktury w jeszcze prostszy sposób, dzięki czemu dostaliśmy w GMie

Singleton
W przypadku funkcji, które zawierają jakiś static, jeśli te statyczne wartości zostały zainicjowane pierwszy raz, możemy pisać teraz:
kodnazwa_funkcji.wartosc;
Wskazówka:
W momencie kompilacji, GM widząc nazwa_funkcji.cokolwiek, przerabia kod na static_get(nazwa_funkcji).cokolwiek

W ten sposób można odnosić się czy do funkcji, czy do konstruktorów - trzeba jednak pamiętać, aby chociaż raz je zainicjować.

kodenum diff {
easy, medium, hadrd
}

function Game() constructor {
static difficulty = diff.easy;
static points = 0;
static hp = 100;
}

Game(); // inicjalizacja pierwszy raz, może być w globalnym skrypcie, zaraz za definicją
show_debug_message(Game.hp); // zwraca: 100

Uwaga!
Warto jednak pamiętać, że skoro tak naprawdę używamy "static_get", dostając strukturę która zawiera nasze statyczne dane, wykonując funkcje z tej struktury, ich kontekst wykonania zmienia się, na obecny (bo wyciągamy funkcję na zewnątrz i tam uruchamiamy). Wszelkie zmienne nadpiszą więc te w obecnym kontekście (globalny, instancja), a nie te w tej strukturze!

Co to oznacza? Załóżmy, że dodamy do naszego konstruktora funkcję "leczącą":
kodfunction Game() constructor {
static hp = 10;

// nowa funkcja
static heal = function() {
hp = 100; // ! tu kryje się błąd !
}
}

Pisząc

kodGame.heal();
Wyobraźmy sobie, że tak naprawdę napisaliśmy:

kodvar a = static_get(Game).heal; // dostajemy referencję na funkcję, nie całą strukturę
a(); // odpala się w obecnym kontekście, nie wewnątrz struktury static_get(Game)

Wskazówka:
Wbrew pozorom, nie wykonamy with(static_get(Game)) {hp = 100;} - co jest prawidłową składnią GMową i wykonałoby się w kontekście struktury statycznej. Jest prostsza opcja.

Funkcja "heal" wykona się więc w obecnym kontekście - jeśli jest to skrypt (poza funkcjami), to ustawimy zmienną global.hp = 100;. Jeśli instancja, to jej zmienna "hp" ustali się na 100 (jeśli instancja tej zmiennej nie miała - no to już ją ma...).

Od wersji 2023.1 GameMaker pozwala już jednak zmieniać wartości używając podobnej składni:

kodfunction Game() constructor {
static hp = 10;

static heal = function() {
Game.hp = 100; // można by też napisać static_get(Game).hp = 100;
}
}

Game();

show_debug_message(Game.hp); // zwróci 10
Game.heal();
show_debug_message(Game.hp); // zwróci 100

Grafika: /upload/ajax/20230213_9377dd8f120eeddc57c2ab4fc79b1a45.png
Komentarze (łącznie 2):
gnysek (Wto., 14 Lut. 23, 11:56)
#1

Russell potwierdził na Discordzie, że brak opcji zapisu (np. Game.hp = 100) to bug i w 2023.2 powinni to naprawić - do tego jednak czasu nie aktualizuję artykułu :)

gnysek (Pon., 27 Lut. 23, 17:30)
#2

Ok, sprawdziłem, w wersji 2023.2 jest to już naprawione i można pisać "Game.hp = <cos>".

Najnowsze wersje GameMakera:

Stabilna
2023.8.1.102 • 2023.8.1.148
wydana 14 dni temu
LTS
2022.0.1.31 • 2022.0.1.30
wydana 169 dni temu
Beta
2023.800.0.406 • 2023.800.0.429
wydana 30 dni temu
= IDE, = Runtime
Użytkownicy online
2 użytkowników aktywnych:
gości: 1, userów: 1
 Adriann
(~ostatnie 15 minut)
Discord
43 użytkownicy online na discordzie:
ACCESS_ViOLATiON, HappyOrange, Draczeq, MKP, OdrzuconyKrakers, Alice, Nitro Slav, Carl-bot, PolTomski, Voytec, DungeonFairy, Wielki Druid, Murrri, Grela, Kowu, TobiasM (Morgo), fervi, Uzjel, Cysior, Alkapivo, lethian, Pako, Arrekin, LadyLush, Dominator2v, LeD, Domeen0, szmalu, Adriann, Fylyps, ZYGZAK, Ulti, bagno, Dyno, Mtax, 🧁Cupcake🧁, g..., Kandif, Danieo, Krzysiek1250, lakas, SzymonKe, Kuzyn
Shoutbox
art22pl (17:45, 29.09.23)
jest chyba jakiś problem z załączaniem zdjęć do postu
gnysek (14:59, 28.09.23)
Poszedł dziś update strony :)
Adriann (21:06, 14.08.23)
Jest..hoho:D
Adriann (21:04, 14.08.23)
Nie znam tego, poszukam. Nie mówisz mam nadzieję o tej podmiance wewnątrz skryptu?
gnysek (10:38, 14.08.23)
Przecież jest: Search & Replace. Do tego nazwy skryptów i zasobów akurat Feather podmienia praktycznie bez problemów.
Adriann (14:39, 12.08.23)
Przydałaby się opcja do globalnej zmiany nazwy zmiennejobiektu we wszystkich skryptach i obiektach :D Tak to jest dość ciężko zabrać się za sprzątanie
SimianVirus7 (10:39, 12.08.23)
To nie burdel, to nieład artystyczny
exp (00:35, 12.08.23)
Feather nie zrozumiał, że burdel jest elementarną częścią mojego projektu
exp (00:34, 12.08.23)
Miałem to samo, nie tyle otworzyłem stary projekt, co w końcu ściągnąłem aktualizację (bo olewałem ten komunikat od chyba półtora roku)
gnysek (23:48, 10.08.23)
Kolizje tilesetowe rządzą!
Starsze wpisy znajdziesz w Archiwum.
Ankieta
Kiedy wyjdzie GameMaker (Studio) 3.0?