Guida RPG Maker XP Script #5 – Classi & SottoClassi


Salve e benvenuti in questa nuova parte della guida, inizio a scriverla una settimana dopo l’eppifania ma dato che mi piace prendermela comoda non so quando la finirò quindi potrebbe sembrare strano quello che sto per dire… avete passato un buon natale? E a capodanno avete fatto attenzione? Si, insomma, i soliti convenevoli, si tratta pur sempre del primo contenuto del blog di quest’anno.

Nella parte precedente abbiamo iniziato col vedere le prime cose concrete, ora inizieremo ad approfondire un pò l’una un pò l’altra, sia teoria (che può servire per capire meglio) ma soprattutto la pratica, iniziando a dare per scontate alcune cose per accelerare. Spiegherò nello specifico cosa sono le classi e le sottoclassi, essendo l’argomento principale va per la terza volta ripetuta una cosa che dissi per far capire meglio come gestire gli script: una classe va considerato un edificio mentre i def le sue stanze… aggiungiamo però che una sottoclasse vuol dire che gli edifici sono legati, ma solo in un senso, cioè una sottoclasse può “sfruttare” anche le stanze (quindi i def) della classe a cui è legato ma non il contrario; nello specifico col termine “script” si intendono le varie classi e sottoclassi che vanno a gestire una determinata cosa, per fare un esempio, se provate a capire come funziona il sistema di battaglia preinserito noterete che sfrutta una moltitudine di classi.

– CLASSE –

Come al solito facciamo alcuni passi indietro spiegando una cosa alla volta, per bene, iniziamo col approfondire il concetto “classe”. A livello di codice una classe è racchiusa tra il codice “class” al codice “end”, quest’ultimo è presente molto spesso quindi non fate confusione, l’ultimo “end” è della classe, gli altri sono dei def o condizioni (ricordo che anche gli input sono condizioni) ed esistono classi sia passive (def initilize) sia attive (def main).

Ciò che so è totalmente da autoditatta, le basi le ho imparate studiando gli script preinseriti (anche se avevo comunque una piccola cultura di javascript) poi cazzeggiando un pò ho approfondito; ebbene ho proprio intenzione di spiegare alcune cose prendendo come punto di riferimento gli script preinseriti.

Apriamo quindi “Script Editor” e vediamo cosa c’è: un gruppo di classi che iniziano con la parola “Game” (Gioco), un gruppo con “Sprite” e giusto 2 con “Spriteset” (gli sprite sono le immagini dei personaggio o altro), poi “Window” (Finestra), “Arrow”, (Freccia), “Interpreter” (giusto per completezza ma per ora ignoratelo), “Scene” (Scena, come abbiamo visto nella parte precedente) poi si conclude con “Main” (che deve sempre trovarsi in fondo a tutti).

– “Main” in verità non contiene un classe ma solo un codice che si avvia prima di ogni altra cosa, ha lo scopo di vedere se tutto è regolare all’interno delle classi.
– In seguito ci sono le classi “Scene”, senza le quali sarebbe impossibile fare qualsiasi cosa, il gioco non esisterebbe senza tali e contengono tutte “def main”; “Scene_Title” è quella che viene avviata per prima di base, si tratta della schermata di titolo, quella dove vedete le opzioni “New Game”, “Continue” e “Shutdown”, prepara anche tutto ciò che serve all’interno del gioco. Tornando alle generiche “Scene”, durante tutto il gioco non saranno attive più di 1 conteporaneamente, allo stesso tempo però non sarà possibile continuare con nessuna di loro attive (ricordate che sto spiegando prendendo in considerazione solo quelle preinserite).
– Quelle “Window”, come suggerisce, gestiscono le finestre e soltando le finestre, in base ovviamente a determinati input o condizioni varie; sono tutte sottoclassi ma questo lo vedremo dopo.
– Le classi “Game” gestiscono i dati di gioco, ci starebbe una marea di cose da spiegare ma meglio evitare, col tempo capirete da soli con ovviamente vari incipit e esempi su come utilizzarle; tanto per anticipare qualcosa, tutto ciò che contiene il database è possibile estrarlo proprio attraverso queste classi.
– Per tutte le altre, tranne “Interpreter”, vengono sfruttate all’interno di una battaglia o altri casi complessi da spiegare in poche parole ma come “Game” vedremo a tempo debito.

Abbiamo dato una spolverata generale ma, sempre vedendo le classi preinserite, alcune sono ripetute più volte con l’aggiunta di numeri, 1, 2, 3… ma se andate a vedere il nome della classe è in verità uguale per tutte… cosa? Riguardo a quando dissi che non è possibile creare più classi con lo stesso nome, come lo spiego questo? Beh, lo spiego così: “def initialize” o “def main” è presente solo in 1 di loro, per la precisione quello presente più in alto in lista, se sarebbero classi diverse vorrebbe dire che quelle successive alla prima non sono scritte correttamente ma in verità, appunto, appartengono alla stessa classe ma per motivi d’ordine si è preferito scrivere i vari def separatamente ma dando lo stesso nome alla classe, si fa capire al programma che vanno considerati come un unica classe o edificio, un pò come avere il capanno degli attrezzi in giardino, appartiene alla stessa abitazione anche se separato dalle mura; ah, questo da non confondere con le sottoclassi che sono cose diverse.

– SOTTOCLASSE –

Prima di approfondire alcuni dettagli andiamo a dare una spolverata anche all’argomento “sottoclasse”; le sottoclassi sono strutturate allo stesso modo di una classe, def main o initialize e via dicendo, solamente per un piccolo (visivamente) dettaglio, di fianco a “class” c’è il nome della classe, giusto? Ma, nelle sottoclassi, c’è il nome di un’altra classe divisi però da un “<“, cioè “class sottoclasse < classe” e come anticipato questo fa in modo che la sottoclasse possa utilizzare i def della classe specificata come se fossero suoi, riguardo a ciò vediamo ora nei dettagli.

– DEF –

É arrivato il momento di spiegare per bene cosa sono questi def, alcune cose dovreste già averle capite ma immagino non totalmete; tornando alla solita frase, i def li possiamo considerare come stanze delle classi, ogni classe ha le sue stanze, come in tutte le abitazione c’è, mister ovvio, una stanza d’entrata in questo caso può essere di 2 tipologie, main o initialize, in base a loro la classe viene gestita diversamente, tanto che, se si entra da main si dovrà uscire sempre da essa, se invece si entra da initialize si uscirà li dove ci si trova finito ciò che si è fatto, è come se l’idraulico dopo aver fatto il suo lavoro esca dalla finestra più vicina a lui; ci sono alcune eccezioni, è possibile avere una classe sia con initialize che main, cosa accade in questi casi? Appena avviata parte initialize poi però main prende le “redini”, quindi è da chiamare comunque con “$scene =”.

Detto ciò, come richiamare un def (cioè far avvenire ciò che il codice al suo interno descrive)? Semplicemente scrivere il nome del def, ma questo è diverso per initialize e main che come detto sono l’entrata e l’entrata serve solo a entrare, appunto, o gestire i movimenti all’interno della classe; ma qual’è di specifico il nome del def? Similmente come una classe, un def è racchiuso tra la scritta “def” e “end”, il suo nome si trova subito dopo “def”, ovviamente non attaccato ma con uno spazio.
Come anticipai tutti i def all’interno di una classe devono avere un nome diverso, lo stesso non vale con classi diverse, per esempio classe1 ha def1, classe1 non potrà avere un ulteriore def chiamato “def1”, o almeno potrebbe ma quello più in alto sovrascrive tutti gli altri (solo durante la “lettura” del codice ma restano scritti), insomma il punto è che invece classe2 potrà senza problemi avere un def chiamato “def1”, senza creare complicanze.
Ma è possibile richiamare un def di una classe diversa da quella attuale? Certo che si; come detto per chiamare una classe si usa “NomeClasse.new” (aggiungere prima “$scene = ” per cambiare classe def main attiva), per richiamare invece un def della classe si fa “NomeClasse.new.nomedef” (in minuscolo perchè così conviene scriverli i nomi dei def).

– ARGOMENTI –

Forse è una traduzione di “arguments” troppo letterale ma serve giusto per avere una parola italiana, per una più facile comprensione (ehm… chi ha detto “def”?), ma cosa sono? Davanti al nome di alcuni def c’è una parentesi con delle scritte a loro interno, in caso di più di 1 divise da una virgola, quelli presenti in main o initialize vanno considerati come appartenenti alla classe; queste scritte non sono altro che variabili pretese, nel senso che servono per forza per la riuscita del codice, infatti se mancano, o scritte di più del necessario, esce un messaggio di errore che finisce con una parentesi del tipo “(n of m)”, dove “n” è il numero di “argomenti” scritti mentre “m” quelli necessari.
Ma ora vorreste sapere come specificarli, no? Semplicemente quando chiamate un def con argomenti, nel codice di richiamo dovete aggiungere una parentesi con il numero di valori necessari, divisi da una virgola, per esempio “nomedef(x, y)”, dove ovviamente “x” e “y” sono solo d’esempio; come detto però, in caso di main o initialize con argomenti, vanno considerati appartenenti alla classe, infatti in questo caso vanno specificati al richiamo della classe “NomeClasse.new(x, y)”, in caso invece di richiamo def di una classe diversa “NomeClasse.new.nomedef(x, y)”; volendo si può direttamente utilizzare un array che ha il giusto numero di valori, facendo “nomedef(array)”.
In alcuni casi gli argomenti hanno già un valore scritto nel codice, del tipo “(a=0)”, in tal caso vuol dire che “a” prenderà quel valore se non gli viene dato nessuno al momento del richiamo.

– RETURN –

In tal caso preferisco non tradurre dato che mi riferisco proprio al codice “return”, nella parte precedente della guida l’ho spiegato in modo grossolano ma ora vedremo nei dettagli.

Volendo si potrebbe tradurre con “ritorna” ma ritorna dove? Nella sezione del codice “visitato” in precedenza; segue un esempio per far capire bene:

class Classe
_def initialize
__def1
__def2
_end
_def def1
__return
__def3
_end
_def def2
_end
_def def3
_end
end

I trattini bassi servono solo per ordine, ignorateli quindi nel senso pratico (intendo non scriveteli anche voi in un codice). Insomma, un codice scritto in questo modo, il def3 è messo li giusto per, viene si richiamato dal def1 ma anche no… vediamo con ordine:
“Classe.new” per chiamare questa classe; si va nel def1 fino a “return”, quindi si torna alla sezione precedente, in questo caso il def initialize ma non all’inizio ma prosegue avviando il codice dopo il richiamo del def1; così facendo si chiama il def2 e così si conclude la classe… ecco che il def3 viene del tutto ignorato dato che si trova dopo il return del def1.
Spero che con questo sia chiaro il funzionamento del return, anche se ci sono altre cosette da specificare.

Tornando a parlarle degli “argomenti”, in alcuni casi vengono specificati dopo un return, anche se senza parentesi ma infatti la funzione è diversa, serve solo per introdurre la cosa con cose già spiegate, simili. Vediamo lo stesso esempio di prima con alcune modifiche:

class Classe
_def initialize
__a = def1
__def2
_end
_def def1
__b = 1
__return b
__def3
_end
_def def2
_end
_def def3
_end
end

Cosa è cambiato? C’è un “a = def1” e un’altra variabile “b”, che prende il valore 1 e specificata nel return, cosa comporta ciò? Con “a = def1” il def1 viene comunque chiamato agendo allo stesso modo se non ci fosse “a = “, ciò che cambia è quel “return b”, che praticamente fa “tornare”, o prendere, il valore di “b” ad “a”, dato che “b” gli viene dato come valore 1, a sua volta “a” diventa uguale a 1.

– RICAPITOLANDO –

Come al solito ricapitolare aiuta a capire meglio, però avendolo spiegato in modo principalmente teorico ora lo faremo in modo pratico, andando ad aggiornarne il script su cui stiamo lavorando nella guida con le cose “nuove”: prima cosa stavolta non useremo solo 1 classe ma ben 2, una “scene” e l’altra “window”, che come abbiamo visto aiuta ad avere maggiore ordine.

class Scene_Guida_Blog
  #--------------------------------------------------------------------------
  # * Main Processing
  #--------------------------------------------------------------------------
  def main
    # Make title graphic
    @sprite = Sprite.new
    @sprite.bitmap = RPG::Cache.title($data_system.title_name)
    # Make window
    @fin = Window_Guida_Blog.new
    @txt = "..."
    # Execute transition
    Graphics.transition
    # Main loop
    loop do
      # Update game screen
      Graphics.update
      # Update input information
      Input.update
      # Frame update
      update
      # Abort loop if screen is changed
      if $scene != self
        break
      end
    end
    # Prepare for transition
    Graphics.freeze
    # Dispose of command window
    @fin.dispose
    # Dispose of title graphic
    @sprite.bitmap.dispose
    @sprite.dispose
  # end main
  end
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    # input
    update_command
    # fin update
    @fin.refresh(@txt)
  # end update
  end
  #--------------------------------------------------------------------------
  # * Update Command
  #--------------------------------------------------------------------------
  def update_command
    # If X button was pressed
    if Input.press?(Input::X)
      # Play decision SE
      $game_system.se_play($data_system.decision_se)
      # txt
      @txt = "Input.press? X"
      return
    end
    # If Y button was pressed
    if Input.trigger?(Input::Y)
      # Play decision SE
      $game_system.se_play($data_system.decision_se)
      # txt
      @txt = "Input.trigger? Y"
      return
    end
    # If Z button was pressed
    if Input.repeat?(Input::Z)
      # Play decision SE
      $game_system.se_play($data_system.decision_se)
      # txt
      @txt = "Input.repeat? Z"
      return
    end
    # If B button was pressed
    if Input.trigger?(Input::B)
      # Play cancel SE
      $game_system.se_play($data_system.cancel_se)
      # Switch to map screen
      $scene = Scene_Map.new
      return
    end
  # end update_command
  end
# end Scene_Guida_Blog
end

Inziamo col vedere “Scene_Guida_Blog”, nella parte pratica non è cambiato nulla dall’ultima volta che lo abbiamo visto, l’ho solamente adattato ai nuovi argomenti spiegati, partendo col cambiare nome quindi ricordate di cambiarlo anche dove viene richiamata scrivendo “$scene = Scene_Guida_Blog.new”.

class Window_Guida_Blog < Window_Base
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    super(0, 0, 640, 120)
    self.contents = Bitmap.new(width-32, height-32)
    refresh
  # end initialize
  end
  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  def refresh(txt = "...")
    self.contents.clear
    self.contents.draw_text(0, 0, 640-32, 120-32, "#{txt}", 1)
  # end refresh
  end
# end  Window_Guida_Blog
end

Ecco “Window_Guida_Blog” che gestisce la finestra allo stesso modo, con la differenza che lo fa in una classe a parte; come vediamo “refresh” nel “scene” viene richiamato specificando un “argomento”, che di base vale “…” ma cambia se gli viene dato un valore diverso.
Scrivendo “self” si riferisce alla finestra, senza specificare il nome datogli perchè appunto in questa classe non ha nome ma è la classe in se; “super” fa riferimento alla classe di cui questa è sottoclasse, quidi “Window_Base”, questo non fa altro che sostituire il codice “Window_Base.new” visto in precedenza, volendo potreste comunque utilizzarlo.
In questa classe vedete che “width” e “height” sono scritti da soli senza nemmeno “self.”, questo perchè il programma capisce già che si fa riferimento alle misure specificate in “super(…)”.

Per il resto credo che si possa concludere qui, ci sono ancora tantissime cose da dire ma mi toccherà spiegare argomenti diversi e lunghi quindi meglio pensarci in un ulteriore parte della guida; quindi non ci resta che salutarci, buona fortuna se state già lavorando a un gioco.

Lascia un commento