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.11.0.179 • 2024.11.0.227
wydana 34 dni temu
LTS
2022.0.3.85 • 2022.0.3.99
wydana 50 dni temu
Beta
2024.1300.0.720 •
2024.1300.0.733
 0.14.0

wydana  6 dni temu
= IDE, = Runtime, = GMRT
Użytkownicy online
1 użytkownik aktywny:
gości: 1,
(~ostatnie 15 minut)
Discord
Shoutbox
Wojo (10:25, 27.12.24)
Jak tworzyłeś* ah ta niecną autokorekta (kiedyś też stworzyłem apki na androida w sumie)
Wojo (10:23, 27.12.24)
O siemka baca, czasami myślę o tobie w kontekście tego jak tworzyłem apki na androida. Swoją drogą czasami zapominam, że forum istnieje bo cały ruch teraz utrzymuje się na discordzie, ale pora to zmienić!
Uzjel (20:17, 10.12.24)
Cały ruch przeniósł się na Discorda.
MagnusArias (17:43, 01.12.24)
O matko... a ja tutaj jestem od ponad 15 lat i czasami zaglądam... biernie bo biernie, ale czasem wpadnę
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
Starsze wpisy znajdziesz w Archiwum.
Ankieta
Ile zarobiłeś do tej pory na grach stworzonych w GM?