Delayed function e scomodità di alarm

Scripts e snippets scritti in GameMaker Language (gml)
Rispondi
Avatar utente
boxbuilder
Membro
Messaggi: 158
Iscritto il: 25/06/2015, 10:37
Specialità: programmatore
Uso: GM:Studio 1.4 HTML5
Contatta:

Delayed function e scomodità di alarm

Messaggio da boxbuilder »

Ciao a tutti, forse è solo una mia sega mentale ma ho sempre trovato scomoda la non esistenza di uno "script_execute delayed" rispetto a Unity, UE e altri ameni engine nei quali invece è disponibile. Certo ci sono gli alarm, ma per me sono scomodi!

Così mi sono costruito un metodo forse un po' macchinoso ma decisamente efficace che vi illustro subito:

innanzi tutto creiamo uno script script_execute_delayed :

Codice: Seleziona tutto

///script_execute_delayed( position, delay_seconds, function, arg0, arg1,  arg2, ...);

var time = argument[1]*room_speed;
if(argument[1] == 0){
    time = 1;
}

with(instance_create(0,0,obj_delay_function)){
    if(!is_undefined(argument[0])){
        position = argument[0];
    }
    alarm[0] = time;
    script = argument[2];
    for(var i = 3; i < argument_count; i++){
        _argument[i-3] = argument[i];
    }
}
come vedete alla funzione passiamo i seguenti parametri:

position, ovvero il riferimento (id di istanza o oggetto) all'interno del quale verrà eseguito lo script.
delay_seconds, ovvero il numero di secondi di ritardo rispetto all'esecuzione della funzione (per me è comodo così, ma se volete potete usare millisecondi o cicli step).
function, che è ovviamente la funzione da eseguire
arg0, arg1, ... gli argomenti vari da passare alla funzione (diciamo da zero a molti).


Ora se vedete com'è fatto lo script, il tutto viene gestito attraverso la creazione di un fantomatico oggetto obj_delay_function
obj_delay_function è un oggetto invisibile e senza sprite con al suo interno solo un evento create e il famigerato alarm[0] che non mi piace ma che essendo wrappato dallo scipt non da più di tanto fastidio.
Immagine


nel create dove dichiaro le variabili derivate dallo script:

Codice: Seleziona tutto

position = undefined;
script = undefined;
_argument = undefined;
e nel Alarm 0 Un altro script in cui avvio la funzione inviata:

Codice: Seleziona tutto

var _scr, _arg;
_scr = script;
_arg = _argument;

if(is_undefined(position)){
    position = id;
}

with(position){
    if(_arg != undefined){
        switch(array_length_1d(_arg)){
            case 1:
            script_execute(_scr,_arg[0]);
            break;
            case 2:
            script_execute(_scr,_arg[0],_arg[1]);
            break;
            case 3:
            script_execute(_scr,_arg[0],_arg[1],_arg[2]);
            break;
            case 4:
            script_execute(_scr,_arg[0],_arg[1],_arg[2],_arg[3]);
            break;
            case 5:
            script_execute(_scr,_arg[0],_arg[1],_arg[2],_arg[3],_arg[4]);
            break;
            case 6:
            script_execute(_scr,_arg[0],_arg[1],_arg[2],_arg[3],_arg[4],_arg[5]);
            break;
            case 7:
            script_execute(_scr,_arg[0],_arg[1],_arg[2],_arg[3],_arg[4],_arg[5],_arg[6]);
            break;
            case 8:
            script_execute(_scr,_arg[0],_arg[1],_arg[2],_arg[3],_arg[4],_arg[5],_arg[6],_arg[7]);
            break;
            case 9:
            script_execute(_scr,_arg[0],_arg[1],_arg[2],_arg[3],_arg[4],_arg[5],_arg[6],_arg[7],_arg[8]);
            break;
            case 10:
            script_execute(_scr,_arg[0],_arg[1],_arg[2],_arg[3],_arg[4],_arg[5],_arg[6],_arg[7],_arg[8],_arg[9]);
            break;
        }
    } else {
        script_execute(_scr);
    }
}
instance_destroy();
qui c'è un po' di sporcizia, praticamente non ho trovato un modo per eseguire script_execute passando il numero specifico di argomenti, se non attraverso lo switch anzi qualche dritta sarebbe bene accetta e molto gradita.

comunque l'effetto finale è molto bello, quando scrivo script_execute_delayed(this,2,stampa,"ciao mondo") provo una gran soddisfazione.

Avatar utente
Tizzio
GMI Honor
Messaggi: 5836
Iscritto il: 29/06/2010, 23:43
Specialità: programmazione
Contatta:

Re: Delayed function e scomodità di alarm

Messaggio da Tizzio »

perché, invece di creare un instanza ad ogni chiamata, non utilizzi un oggetto monoistanza persistente con una coda di script da eseguire?
In pratica crei una lista di timer, finché non è vuota ogni step decrementi il loro tempo e quando termina esegui e rimuovi l'elemento
(comunque dovrebbe esistere la variabile argument_count al posto di array_length_1d)

Per lo switch, non credo che in GM ci sia un modo piu' efficiente
TLDR : va bene tenere il codice che hai scritto

[pippe inutili]
Alcune possibilità:
- Passi l'array di arguments allo script in questione (brutto e rovina l'usabilità, ma leggermente piu' veloce dello switch)
- Crei un secondo wrapper e usi una sorta di jump table in cui l'elemento i-esimo di un array viene associato a uno script che si occupa di smistare gli arguments passati tramite l'array "argument"
Detto in altro modo, crei 10 script diversi che chiamano script_execute usando come parametri quelli dell'array passato.
Il costo è di una chiamata a funzione in piu', che in GM non è così basso: poi in teoria yoyocompiler sfrutta le ottimizzazioni del compilatore C++ che fanno queste cose in automatico.
[/pippe inutili]

Avatar utente
boxbuilder
Membro
Messaggi: 158
Iscritto il: 25/06/2015, 10:37
Specialità: programmatore
Uso: GM:Studio 1.4 HTML5
Contatta:

Re: Delayed function e scomodità di alarm

Messaggio da boxbuilder »

Tizzio ha scritto:(comunque dovrebbe esistere la variabile argument_count al posto di array_length_1d)
certo, infatti la uso all'interno di script_execute_delayed per popolare l'array che contiene gli argomenti da passare all'oggetto.
Tizzio ha scritto:perché, invece di creare un instanza ad ogni chiamata, non utilizzi un oggetto monoistanza persistente con una coda di script da eseguire?
Sono sicuro che se facessi come dici tu lo script sarebbe più efficiente, effettivamente creare e distruggere un'istanza non è un'attività a buon mercato sul lato delle risorse, però il sistema multi_istanza presenta alcuni vantaggi non trascurabili, ad esempio non avere un'immagine persistente da gestire, spesso nel mio codice distruggo tutte le istanze di una room, indipendentemente dal fatto che queste siano persistent; oddio, potremmo risolvere usando un singleton, cioè quando esegui lo script cerchi l'obj_delay_manager, se non esiste lo crei... Insomma, mi sa che seguirò il tuo consiglio.

invece il suggerimento dei 10 script non mi piace, la mia non è una necessità di ottimizzazione delle performance ma di pulizia e leggibilità del codice.

comunque grazie. Ormai GML è una seconda casa per me (dopo Java, C#, PHP e l'ormai crollata actionscript).

Jak
Admin
Messaggi: 12355
Iscritto il: 19/08/2009, 16:20
Specialità: Programmazione 3D
Uso: GM:Studio 2
Contatta:

Re: Delayed function e scomodità di alarm

Messaggio da Jak »

boxbuilder ha scritto:invece il suggerimento dei 10 script non mi piace, la mia non è una necessità di ottimizzazione delle performance ma di pulizia e leggibilità del codice.
E' pura implementazione, la "facciata" dello script resta identica mentre il codice è più performante il che non è MAI un male, sopratutto se fai una libreria.
Inoltre è anche più elegante come codice piuttosto che creare istanze che potrebbero venire disattivate automaticamente.
Time to feel, time to believe
Dare to see what may come of our future
Lift your head, broaden your gaze
Speak your mind and your thoughts they will follow you

Rispondi

Chi c’è in linea

Visitano il forum: Nessuno e 4 ospiti