Sviluppare un gioco in Python : Menù e salvataggi. Seconda Parte.

Introduzione

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.

Press ESC to close