GM 2023+: Singleton w GM
Poniedziałek, 13 Lutego 2023, 23:52
Czas czytania 3 minuty, 23 sekundy
Zgodne z GM:
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
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
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
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
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
Funkcja "heal" wykona się więc w obecnym kontekście - jeśli jest to skrypt (poza funkcjami), to ustawimy zmienną
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
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