Funkcje 2.3+ i ich zasięgi

Poniedziałek, 28 Czerwca 2021, 17:55
Czas czytania 5 minut, 41 sekund
Zgodne z GM: gms2
W GMS 2.3+ zniknęły skrypty, a w ich miejsce pojawiły się funkcje. Zależnie o tego, gdzie i jak zostaną zdefiniowane, mogą wpływać na inny zakres zasobów lub gry, dlatego w tym artykule wytłumaczę czego można się po GMS2 spodziewać.
W GMS 2.3+ zniknęły skrypty, a w ich miejsce pojawiły się funkcje. Skrypty są teraz jedynie grupami na jedną lub więcej funkcji i dla wstecznej kompatybilności, importując stary projekt, każda funkcja trafia do skryptu o tej samej nazwie, tworząc nowy skrypt domyślnie dostajemy też pustą funkcję o tej samej nazwie jako gotowy kod.

Funkcje stworzone w skryptach mają globalny zasięg (jak do tej pory), pojawiła się jednak funkcja tworzenia funkcji w obiektach. Jakby tego było mało, można też tworzyć funkcje anonimowe, przypisane do zmiennej. W trakcie działania programu nie tak prosto je odróżnić i trzeba pamiętać co zostało jak zdefiniowane, gdyż można łatwo wpaść na błąd programu ze względu na to, co dla danej funkcji kryje się pod słowem kluczowym self.

Funkcja nazwane:
kodfunction nazwana() {}
function nazwana(parametr, inny) {}

Funkcje anonimowe/lambda:
kodlambda = function() {}
lambda = function(parametr, kolejny) {}

Oba rodzaje funkcji wywołujemy korzystając z nazwy, do której jej przypisano + nawiasu (np. nazwana() lub lambda() ).

Zasięg funkcji:W GMS 2.3+ wszystko co definiujemy w skryptach, trafia do przestrzeni globalnej (global. ), ale dodatkowo, funkcje nazwane zdefiniowane w skryptach, trafiają do osobnej puli, tej samej co funkcje wbudowane, nazwijmy je superfunkcjami. Nie są one współdzielone referencją, więc nie da się nadpisać superfunkcji, nie da się też w obiektach używać zmiennych o tej nazwie (poza zmienną tymczasową, o ile nie zostanie użyta jako funkcja - wtedy superfunkcja ma priorytet).

Zgodnie z tymi informacjami, superfunkcje są już zdefiniowane w momencie, gdy GMS zaczyna przetwarzać skrypty i można ich używać (można też założyć, że ich definicje z tego skryptu zostały usunięte), natomiast nie dotyczy to funkcji anonimowych, które są dostępne dopiero po wykonaniu się linijki kodu która je definiuje.
Ponieważ w GMS 2.3 kolejność zasobów może być losowa (inna niż w drzewku zasobów), warto mieć to na uwadze - globalne funkcje anonimowe powinno się używać jedynie w skrypcie w którym są zdefiniowane, lub w funkcjach i obiektach które zostaną użyte po starcie gry - nie powinno się do nich odnosić w innych skryptach bezpośrednio w globalnym zasięgu.
kod// globalne funkcje nazwane - kolejność
// obie wersje są poprawne:
globalna();
function globalna() {}

//lub:
function globalna() {}
globalna();

// globalne funkcje lambda
// nieprawidłowo
lambda(); // - jeszcze nie istnieje
lambda = function() {}

// prawidłowo
lambda = function() {}
lambda();

// odwołanie do funkcji jest możliwe poprzez
globalna();
global.globalna(); // nie musi być to ta sama funkcja co wyżej, jeśli została nadpisana
global.lambda();

Wioczność i przeciążanie funkcjiFunkcja nazwane będą dostępne w zasięgu, w jakim zostały stworzone.

Funkcje stworzone w skrypcie:
- nazwane: są widoczne tak jak funkcje wbudowane są superglobalne, dodatkowo tworzona jest kopia (nie referencja!) pod słowem global. i traktowana będzie ona jak funkcja anonimowa
- anonimowe: są widoczne po prefiksie global.
Funkcje stworzone w obiekcie:
- nazwane/anonimowe: są widoczne w tym obiekcie, tak jak każda inna zmienna

Jeżeli funkcja definiuje kolejną, to jej widoczność znów zależy od miejsca, w którym została stworzona. Przykład takiej funkcji:
kodfunction a() {
function b() {}
c = function() {}
}

Funkcja nazwana stworzona przez inną funkcję w skrypcie:
- b i c nie będzie istnieć, bez wywołania a()
- jeśli została wywołana, to zarówno funkcja nazwana b jak i anonimowa c w skrypcie będzie widoczna pod swoją nazwą, oraz trafi do przestrzeni global. pozostając widoczną w innych zasobach
Funkcja nazwana stworzona przez inną funkcję w obiekcie:
- po wywołaniu a(), pojawią się w obiekcie zmienne b i c z funkcjami. Jeżeli istnieją takie same funkcje w przestrzeni global (globalvar), nie zostaną nadpisane.

Superfunkcji nie da się nadpisać, ale można nadpisać jej wersję pod zmienną globalną. Prowadzi to do dziwnej postaci, w której a() i global.a() mogą być różne.

Kod z funkcji i jego wpływ na grę zależnie od zasięgu:Superfunkcja zmienia wartości zmiennych w zasięgu, w którym jest wywołana, oznacza to, że:
kodfunction a() { value = 5; }- wywołane a() skrypcie, ustawi global.value = 5;
- wywołanie a() obiekcie, ustawi lokalną zmienną value = 5;
- wywołanie global.a() w obiekcie - tak samo jak dla aninimowej

Funkcja anonimowa zmieni wartość zmiennych w zasięgu, w którym została stworzona, oznacza to, że:
koda = function() { value = 5; }- wywołanie a() w skrypcie, lub global.a() w obiekcie, ustawi global.value = 5;

Zmiana zasięgu:

Zasięg superfunkcji zmienia się za pomocą with() - tak samo jak do tej pory.

Funkcja zdefiniowana w obiekcie, działa trochę podobnie do zmiennych tymczasowych, tzn. nie wymaga użycia other., ale wykona się w kontekście wybranego obiektu, np.:
kod// w object0
function test() { show_debug_message(object_get_name(self.object_index)); }

test(); // object0
with (object1) { test(); } // object1
with (object1) { other.test(); } // object0
jak widać, kontekst wykonania nadaje with, a nie pochodzenie funkcji.

Wszystko zmienia się jednak, gdy mówimy o funkcji anonimowej - wtedy zasady są takie same, jak używania innych zmiennych razem z with, a kontekst pozostanie taki, jak w momencie utworzenia funkcji:
kod
// w object0
test = function() { show_debug_message(object_get_name(self.object_index)); }

test(); // object0
with(object1) { test(); } // zwraca błąd, bo object1 nie ma zmiennej test
with(object1) { other.test(); } // object0

Aby zmienić kontekst funkcji anonimowej, można skorzystać z funkcji method():
kod// w object0
test = function() { show_debug_message(object_get_name(self.object_index)); }

_test = method(object1, test); // tworzy kopię funkcji "test" ale ustawia nowy kontekst
_test(); // object1
with(object1) { _test(); } // zwraca błąd, bo object1 nie ma zmiennej test
with(object1) { other._test(); } // object1
jeśli jednak skorzystamy z var, zasady się zmieniają:
kodvar _tmp = method(object1, test); // tworzy kopię funkcji "test" ale ustawia nowy kontekst
_tmp (); // object1
with(object1) { _tmp (); } // object1
with(object1) { other._tmp (); } // zwraca błąd, bo dla zmiennej tymczasowej other to object1, który nie ma zmiennej _tmp
Można sobie wyobrazić, że var+with powoduje, że niewidzialne "other." pojawia się przed nazwą naszej tymczasowej zmiennej, stąd odwrócenie sytuacji.

Użycie undefined jako nowego kontekstu:
- w przypadku zwykłej zmiennej, zachowa się tak samo, jak funkcja przed zmianą kontekstu
- w przypadku zmiennej tymczasowej, weźmie kontekst z with()

Użycie global jako nowego kontekstu spowoduje błąd - ale tylko dla tej konkretnej funkcji, bowiem global nie ma właściwości object_index. Poza tym, użycie global jest jak najbardziej możliwe i prawidłowe.
Komentarze (łącznie 0):
Nie ma jeszcze żadnego komentarza. Czas to zmienić

Najnowsze wersje GameMakera:

Stabilna
2024.8.1.171 • 2024.8.1.218
wydana 75 dni temu
LTS
2022.0.3.83 • 2022.0.3.98
wydana  3 dni temu
Beta
2024.1100.0.686 •
2024.1100.0.707
 0.13.0

wydana 9 dni temu
= IDE, = Runtime, = GMRT
Użytkownicy online
1 użytkownik aktywny:
gości: 1,
(~ostatnie 15 minut)
Discord
34 użytkownicy online na discordzie:
Kysiu, 🧁Cupcake🧁, Nikas, MR.PEkinczyk, Nitro Slav, Carl-bot, EchoDuck, p..., Wielki Druid, GMRussell, OdrzuconyKrakers, Kalor, r..., Moldis, Pako, Arrekin, LadyLush, yazaa, Domeen0, Dyno, Deusald, szmalu, LeD, Miłosz, 21Lancz, bagno, Tidżi, l..., moeglich, s..., Krzysiek1250, Shockah, Cosplyfanka, xVANiLL
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?