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
2024.8.1.171 • 2024.8.1.218
wydana 72 dni temu
LTS
2022.0.2.51 • 2022.0.2.49
wydana 401 dni temu
Beta
2024.1100.0.686 •
2024.1100.0.707
 0.13.0

wydana  6 dni temu
= IDE, = Runtime, = GMRT
Użytkownicy online
2 użytkowników aktywnych:
gości: 1, userów: 1
 Adriann
(~ostatnie 15 minut)
Discord
33 użytkownicy online na discordzie:
Kysiu, 🧁Cupcake🧁, Nikas, Alice, Nitro Slav, Carl-bot, Saus, GibkiKaktus, GMRussell, fervi, r..., antek, Michał Parkoła, Pako, Arrekin, MagnusArias, LadyLush, yazaa, Dyno, szmalu, Korodzik, LeD, Ulti, bagno, Tidżi, Danieo, g..., Huder, l..., moeglich, s..., Krzysiek1250, Shockah
Shoutbox
gnysek (11:46, 17.11.24)
Witamy, witamy!
baca (12:22, 16.11.24)
To już 25 lat.. Witam po paru latach nieobecności.
gnysek (11:05, 15.11.24)
Natomiast obecne forum istnieje od 2004, jak z iglu.cz na gmclan.org przeszliśmy i od tego czasu nie było resetów danych.
gnysek (12:35, 13.11.24)
Ogólnie GMCLAN istnieje 22 lata, ale na to trofeum nie zrobiłem (jeszcze xD)
Chell (20:41, 08.11.24)
wow, ta emotka w ogóle nie wygląda jak : O xD
Chell (20:40, 08.11.24)
tylko? :O 4tk ma 15
Borek (18:12, 07.11.24)
Właśnie dostałem powiadomienie z forum, że jestem na GMClanie 18 lat :D Ja pierdzielę...
S
Sutikku (08:43, 18.10.24)
TIL, gamemaker jest starszy ode mnie
gnysek (16:04, 15.10.24)
Za równo miesiąc, GameMaker kończy 25 lat.
Wojo (15:38, 05.09.24)
Ciekawe
Starsze wpisy znajdziesz w Archiwum.
Ankieta
Ile zarobiłeś do tej pory na grach stworzonych w GM?