In questa parte tratteremo gli argomenti fino alla classe impostazioni. Riporterò il codice completo, comprese le funzioni che abbiamo già analizzato nelle puntate precedenti (specificando eventuali modifiche).
Come al solito, non si capirà immediatamente lo scopo del programma, ma seguendolo passo passo, arriverete a comprendere tranquillamente questi concetti. Pensate solamente che queste basilari nozioni possono essere implementate secondo le vostre singole necessità, la base è sempre la stessa: creatività ed ingegno per risolvere un problema (però anche la soluzione non deve essere troppo ostica o difficile per poter essere applicata!!!).
Codice
#!/usr/bin/python2 # -*- coding: ascii import pygame from pygame.locals import * from sys import exit import cPickle def tps(orologio, fps): temp = orologio.tick(fps) tps = temp /1000. return tps def carica_imm_sprite(nome,h,w,num): immagini = [] if num is None or num == 1: imm1 = pygame.image.load("data/"+nome+".png").convert_alpha() imm1_w, imm1_h = imm1.get_size() for y in range(int(imm1_h/h)): for x in range(int(imm1_w/w)): immagini.append(imm1.subsurface((x*w,y*h,w,h))) return immagini else: for x in range(1,num): imm1 = pygame.image.load("data/"+nome+str(x)+".png").convert_alpha() immagini.append(imm1) return immagini class giocatore(pygame.sprite.Sprite): def __init__(self,nome,altezza,larghezza,xy,num): pygame.sprite.Sprite.__init__(self) self.immagini = carica_imm_sprite(nome,altezza,larghezza,num) self.immagine = self.immagini[0] self.coordinate = (int(xy[0]),int(xy[1])) self.animazione = False self.anim_corrente = 0 self.tempo_animazione = 0.0 self.passo1 = pygame.mixer.Sound("data/sound/passo1.wav") self.passo2 = pygame.mixer.Sound("data/sound/passo2.wav") self.Passi = pygame.mixer.Channel(1) def anime(self,frame): if self.animazione == False: self.anim_corrente = 0 self.immagine = self.immagini[frame] return else : if self.tempo_animazione<0.075: self.tempo_animazione += orologio.get_time()/1000. else: self.immagine = self.immagini[self.anim_corrente+frame] if self.Passi.get_sound() is None or self.Passi.get_sound() != self.passo1: if self.Passi.get_busy() == False: self.Passi = self.passo1.play() else: self.Passi.queue(self.passo1) else : if self.Passi.get_busy() == True: self.Passi.queue(self.passo2) if self.anim_corrente < 3: self.anim_corrente +=1 else: self.anim_corrente = 0 self.tempo_animazione = 0 def update(self, direzione,frame): self.animazione = True self.anime(frame) x,y = self.coordinate if direzione == "basso": y += 1*speed*tempo_passato self.coordinate = x,y return if direzione == "alto": y -= 1*speed*tempo_passato self.coordinate = x,y return if direzione == "destra": x += 1*speed*tempo_passato self.coordinate = x,y return if direzione == "sinistra": x -= 1*speed*tempo_passato self.coordinate = x,y return elif direzione == "stop": self.Passi.stop() self.animazione = False class ingame(): def __init__(self, player1, screen1, base): self.giocatore = player1 self.screen = screen1 self.base = base def render(self): self.screen.blit(self.base, (0,0)) self.screen.blit(self.giocatore.immagine, self.giocatore.coordinate) class impostazioni(): def __init__(self): self.larghezza_schermo = 640 self.altezza_schermo = 480 self.full = False self.bool_hw = False self.bool_buff = False self.bool_opengl = False self.depth = 32 self.frequenza = 44100 self.dimensione = -16 self.canali = 2 self.buffer = 4096
Analisi
Le funzioni tps e carica_imm_sprite sono rimaste immutate, compreso il loro funzionamento, quindi non spenderò ulteriori parole a riguardo.
La classe giocatore è simile alla classe sprite che abbiamo creato nell’esempio precedente. Unica differenza sostanziale è la mancata gestione di Rect, perché in questo caso vogliamo solo muovere il nostro giocatore sullo schermo e memorizzare la sua posizione con un salvataggio. La gestione dell’animazione è identica, come la sua renderizzazione.
Passiamo invece alla nuova classe ingame:
- Questa classe ha il compito di mantere e gestire l’istanza di gioco. In questo caso viene inizializzata con il giocatore corrente, lo schermo dove si deve renderizzare e l’immagine dello sfondo.
- Il render è molto semplice e consiste solo nel fatto di stampare a video l’immagine dello sfondo con il giocatore.
Questa classe è necessaria, in questo esempio, per passare dal menù al gioco e viceversa. Separando le due cose infatti, è più semplice gestire quello che sono le semplici meccaniche di gioco dalla gestione esterna. Nella nostra situazione però, come già accennato, sarà sempre il menù ad avere il “comando” della situazione.
Anche la classe impostazioni fa la sua prima comparsa in questo esempio:
- Come possiamo vedere, questa classe ha bisogno solo di essere inizializzata con delle impostazioni di base. Per ora non abbiamo impostato nessun metodo che lavori su di essa, perché ho preferito agire direttamente sui suoi componenti per cambiare lo stato delle cose. Essendo gestita in questo modo, non ha bisogno di altro perché ci servirà solo per creare il nostro primo oggetto impostazioni, che sarà salvato con cPickle (vedremo in seguito come), per poi essere modificato dal menù a seconda delle nostre esigenze.
Le componenti di questa classe sono le seguenti:
- Larghezza ed altezza dello schermo.
- Quattro variabili booleane per memorizzare lo stato dei flag : full (Fullscreen o no), bool_hw (per l’accelerazione hardware), bool_buff (per l’utilizzo del Double Buffer), bool_opengl (per settare l’utilizzo dell’opengl).
- Depth indica la profondità dello schermo.
- Frequenza, dimensione, canali e buffer invece rappresentano le variabili di controllo per inizializzare il mixer. In questo esempio non eseguiremo cambiamenti a queste ultime, ma basterà aggiungere poche righe per adattarlo anche a questo compito, visto che il caricamento delle impostazione è sempre lo stesso e viene eseguito con tutti gli elementi di impostazioni. Dimensione ha un valore negativo per ovviare a problemi di compatibilità con sistemi operativi differenti da windows (infatti quest’ultimo non fa distinzioni e comunque sia, la qualità audio non cambia).
Per ora l’opengl è solo impostabile come flag, ma quasi sicuramente riscontrerete degli errori. Questo perché ancora non abbiamo visto come inizializzare una scena in OpenGL. Più avanti toccheremo anche questo tasto, un pò più pesante da digerire.
Conclusioni
Iniziamo così la nostra analisi riguardante questo nuovo esempio. Per ora c’è poco da dire e da capire, ma con la prossima parte le cose saranno più complicate, visto che parleremo della classe menù. Quello che continuerei a sottolineare è il fatto che non è facile trovare la soluzione alle proprie esigenze, ed è ancora più difficile trovare una soluzione adattabile a molte situazioni.
Essendo proprio quest’ultimo il mio obbiettivo, potete un pò capire il mio disagio nel presentarvi questi esempi, perché potrebbero essere utili solo ai miei scopi, ma inutili e difficilmente comprensibili e/o utilizzabili dalla maggior parte delle persone che leggono.
Per questo cerco sempre di specificare anche le differenti possibilità di utilizzo delle funzioni che presento, così da far capire le varie scelte che si possono presentare durante la stesura del codice o nella creazione del videogioco in se, mettendovi in condizione di gestire la situazione con le vostre forze.
Con questo vi saluto e vi do appuntamento alla prossima parte.