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.13.1.193 • 2024.13.1.242
wydana 56 dni temu
LTS
2022.0.3.85 • 2022.0.3.99
wydana 191 dni temu
Beta
2024.1400.0.826 •
2024.1400.0.822
 0.17.0

wydana  wczoraj
= IDE, = Runtime, = GMRT
Użytkownicy online
1 użytkownik aktywny:
gości: 1,
(~ostatnie 15 minut)
Discord
48 użytkowników online na discordzie:
Nikas, LeD, pABLO, Fox, p..., 🧁Cupcake🧁, Grela, LadyLush, OdrzuconyKrakers, Bartuszewsky, Kalor, PhysX ᴺⱽᴵᴰᴵᴬ, r..., Threef, Moldis, Pako, Arrekin, MagnusArias, Dyno, 🆅🅸🆃🅾74🅼, Deusald, szmalu, Morro, ZYGZAK, Miłosz, Voytec, Ulti, Danieo, bagno, antek, Tidżi, 21Lancz, Kysiu, Sporek, Kandif, l..., Cebul, Jayu, s..., 42traviss, Add92, Krzysiek1250, h..., d..., Shockah, exigo, m..., xVANiLL
Shoutbox
p
pablo1517 (21:48, 07.06.25)
Siema wszystkim! Co tam slychac?
gnysek (13:44, 10.04.25)
Za 3-4 miesiące GM przejdzie na wydania "półroczne", więc korzystanie z wersji beta żeby sprawdzić nowości będzie wskazane :P
Adriann (18:09, 08.04.25)
Odpowiadam, dzisiaj :D
Adriann (20:48, 04.04.25)
A kiedy te UI layery mają wejść do normalnej wersji gma?
gnysek (00:38, 11.03.25)
I jak, zobaczyłeś ? :D Trochę im zjechało na publiczny release, ale były już w ostatnich dniach lutego dostępne jak się wie, jak pobrać kandydatów do bety :P
Kuzyn (21:30, 05.03.25)
uwierzę jak zobaczę :P
gnysek (10:35, 18.02.25)
W ciągu 10 dni mają wyjść w końcu Layery UI :D
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.
Starsze wpisy znajdziesz w Archiwum.
Ankieta
Ile zarobiłeś do tej pory na grach stworzonych w GM?