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
42 użytkownicy online na discordzie:
Kysiu, 🧁Cupcake🧁, Moho, Nikas, MR.PEkinczyk, Alice, Carl-bot, p..., GibkiKaktus, Wielki Druid, GMRussell, OdrzuconyKrakers, 𝕳𝖚𝖌𝖔 𝕲𝖔𝖓𝖝𝖆𝖑𝖊𝖝, Kalor, r..., Moldis, Pako, Arrekin, MagnusArias, yazaa, Domeen0, Dyno, Deusald, szmalu, Morro, LeD, Miłosz, 21Lancz, Voytec, Ulti, m..., bagno, Danieo, Sporek, l..., moeglich, s..., Krzysiek1250, Shockah, Cosplyfanka, exigo, 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?