Minecraft Whac-a-block

Whac-a-block

Il gioco che stai per creare con Minecraft si chiama “Whac-a-Block”, e si ispira al famoso videogioco “Whac-a-Mole”. L’obiettivo è quello di colpire con la spada i blocchi di luminite (ndr: chiamiamo così la pietra che si è illuminata), per farli ritornare normali blocchi di pietra.
Guadagnerai punti per ogni blocco che fai ritornare e il gioco si concluderà quando i blocchi saranno tutti di luminite.

Dividiamo il progetto in 5 parti

  1. Creare il programma: lanceremo Minecraft Python e ci assicureremo che tutto funzioni a
    dovere.
  2. Creare l’interfaccia del gioco: scriveremo il codice che farà comparire il gioco di fronte al
    giocatore di Minecraft.
  3. Creare i blocchi di luminite: scriveremo le funzioni che trasformano in maniera casuale
    alcuni blocchi di pietra in luminite.
  4. Colpire i blocchi: faremo in modo che i blocchi ritornino di pietra quando vengono colpiti.
  5. Game over: quanti punti hai fatto?

Creare il programma

  1. Lancia Minecraft e crea un mondo.
  2. Dal menù di avvio, vai alla sezione Coding e seleziona Geany dal menù a tendina.
  3. Dalla finestra di Geany crea un nuovo programma selezionando File/New Window e salvalo con File/Save.
  4. Importa tutte le librerie di Python di cui avrai bisogno:
    import mcpi.minecraft as minecraft
    from mcpi import block
    import random
    from time import sleep

    Il modulo mcpi.minecraft serve per interagire il programma che scriviamo con Minecraft Pi
    block serve per richiamare i blocchi di Minecraft usando i loro nomi
    random serve per generare numeri in maniera casuale
    sleep serve per inserire ritardi nell’esecuzione del programma

  5. Crea un collegamento a Minecraft: Pi Edition e pubblica un messaggio nella finestra di chat di Minecraft:
    mc = Minecraft.create()
    mc.postToChat("Minecraft Whac-a-Block”)
  6. Esegui il programma.
    Non farà molto, ma, se tutto funziona a dovere, leggerai la scritta Minecraft Whac-a-Block nella finestra di chat di Minecraft.

Creare l’interfaccia del gioco

Creiamo ora l’interfaccia del nostro gioco: un muro 3×3 di blocchi di pietra, che si trasformano in maniera casuale in luminite.

Il muro dovrà apparire di fronte al giocatore, e, quindi, dobbiamo prima di tutto determinare la sua posizione:

x, y, z = mc.player.getTilePos()

Usa quindi questa posizione con la funzione setBlocks() per creare il muro di pietra:

mc.setBlocks(x - 1, y, z + 3, x + 1, y + 2, z + 3, block.STONE.id)

Per avvisare il giocatore dell’inizio del gioco, pubblica un paio di messaggi nella chat e inserisci un ritardo nel programma usando la funzione time.sleep():

mc.postToChat("Get ready ...")
sleep(2)
mc.postToChat(“Go")

Riassumendo, il programma scritto fino adesso è:

import mcpi.minecraft as minecraft
from mcpi import block
import random
from time import sleep

mc = Minecraft.create()
mc.postToChat("Minecraft Whac-a-Block”)

x, y, z = mc.player.getTilePos()
mc.setBlocks(x - 1, y, z + 3, x + 1, y + 2, z + 3, block.STONE.id)

mc.postToChat("Get ready ...")
sleep(2)
mc.postToChat(“Go")

Esegui il programma e vedi cosa succede.

Dovresti vedere l’interfaccia del gioco direttamente di fronte al giocatore, e i messaggi “Get ready …” e “Go” nella chat.

Creare i blocchi di luminite

Adesso dobbiamo scrivere il codice per trasformare in maniera casuale i blocchi di pietra in luminite.

Per farlo useremo la funzione random.randint(start, end) per selezionare in maniera casuale uno dei blocchi del muro.

Definiamo, prima di tutto, la variabile numero_blocchi_luminite per tenere conto del numero di blocchi che si sono già illuminati (cioè che sono stati trasformati in luminite), e la variabile punti per tenere conto del punteggio raggiunto dal giocatore.
Poiché siamo all’inizio del gioco, assegnamo a entrambe il valore 0:

numero_blocchi_luminite = 0
punti = 0

Poiché il gioco finisce quando tutti i blocchi saranno illuminati, programma deve ripetersi in loop fino a quel momento.
Creiamo, quindi, un loop while che esegue le istruzioni finché numero_blocchi_luminite assume valore 9 (cioè finché tutti i blocchi sono trasformati in luminite).

Inseriamo anche un ritardo di 0.5 secondi nel programma; se non lo facessimo, sarebbe così veloce, che non il giocatore non riuscirebbe a colpire nessun blocco!

while numero_blocchi_luminite < 9:
	sleep(0.5)

D'ora in avanti, daremo per scontato che il codice che scriviamo sia interno al loop while.

Dobbiamo fare in modo che un blocco a caso del nostro muro di pietra venga trasformato in luminite.
Non è così semplice come potrebbe sembrare: infatti, cosa succederebbe se il blocco scelto fosse già stato trasformato in luminite? Il nostro programma deve tenere in considerazione questa eventualità.

Il metodo che useremo è invece piuttosto semplice: il programma sceglierà una posizione in maniera casuale, controllerà che il blocco in quella posizione sia di pietra, e nel caso non lo fosse (e cioè fosse di luminite), sceglierà una nuova posizione in maniera casuale... e così via fino a che non troverà un blocco di pietra non ancora trasformato.

Creiamo la variabile trasformato_in_luminite e le assegnamoli valore False.
Quindi, creiamo un loop while che continuerà fino a che trasformato_in_luminite non assume il valore True.
Dovremo anche aumentare di 1 il valore di numero_blocchi_luminite per tenere conto di ogni nuovo blocco illuminato:

numero_blocchi_luminite = numero_blocchi_luminite + 1
trasformato_in_luminite = False
while not trasformato_in_luminite:

Quando un blocco di pietra viene trasformato in luminite, a trasformato_in_luminite verrà assegnato il valore True e il loop terminerà.

All’interno del loop usiamo la funzione random.randint(start, end) per generare casualmente una posizione x (compresa fra -1 e 1) e una posizione y ( compresa fra 0 e 2).

xPos = x + random.randint(-1, 1)
yPos = x + random.randint(0, 2)
zPos = z + 3

Usiamo la funzione getBlock(x, y, z) e if per verificare che il blocco in quella posizione sia STONE.
Nel caso lo sia, trasformiamololo in Luminite con setBlock(x, y, z, Id) e rendiamo trasformato_in_luminite = True.
In caso contrario, il programma dovrà tornare all'inizio del loop e genererà un'altra posizione casuale:

if mc.getBlock(xPos, yPos, zPos) == block.STONE.id:
	mc.setBlock(xPos, yPos, zPos, block.GLOWSTONE_BLOCK.id)
	trasformato_in_luminite = True

Nota: Al posto di usare le ID dei blocchi (ad es. pietra = 1, luminite = 89), puoi usare il modulo Block, che contiene tutte le ID e tutti i nomi dei blocchi (ad es. block.STONE.id).

Riassumendo, il programma scritto fino adesso è:

import mcpi.minecraft as minecraft
from mcpi import block
import random
from time import sleep

mc = Minecraft.create()
mc.postToChat("Minecraft Whac-a-Block”)

x, y, z = mc.player.getTilePos()
mc.setBlocks(x - 1, y, z + 3, x + 1, y + 2, z + 3, block.STONE.id)

mc.postToChat("Get ready ...")
sleep(2)
mc.postToChat(“Go")

numero_blocchi_luminite = 0
punti = 0

while numero_blocchi_luminite < 9:
	sleep(0.2)
	numero_blocchi_luminite = numero_blocchi_luminite + 1
	trasformato_in_luminite = False
	while not trasformato_in_luminite:
		xPos = x + random.randint(-1, 1)
		yPos = x + random.randint(0, 2)
		zPos = z + 3
		if mc.getBlock(xPos, yPos, zPos) == block.STONE.id:
			mc.setBlock(xPos, yPos, zPos, block.GLOWSTONE_BLOCK.id)
			trasformato_in_luminite = True

Lancia il programma.

Dovrebbe apparire l'interfaccia del gioco, i blocchi si dovrebbero trasformare uno dopo l'altro in luminite, e il programma dovrebbe terminare quando sono stati trasformati tutti.

Colpire i blocchi

Il giocatore potrà colpire i blocchi facendo clic con il tasto destro del mouse quando ha in mano la spada.

L'API di Minecraft ha funzioni che ti permettono di determinare quali blocchi sono stati colpiti.

Usando la funzione events.pollBlockHits() puoi ottenere la lista degli eventi accaduti dall'ultima volta in cui l'hai richiamata. Fra questi c'è anche la lista dei blocchi che sono stati colpiti.

Possiamo usare questa funzione per determinare la posizione del blocco colpito, e quindi la funzione getBlock(x, y, z) per verificare se sia di luminite. In caso affermativo, useremo la funzione setBlock(x, y, z, blockId) per farlo ritornare di pietra, e diminuiremo il numero dei blocchi illuminati e aumentaremo il punteggio del giocatore.

Nel loop while, inserisci il codice seguente per scandire la lista di eventi:

for hitBlock in mc.events.pollBlockHits():

Nota: hitBlock() contiene gli eventi che sono accaduti. Ci sono molte informazioni, fra le quali la lista dei blocchi colpiti, quale faccia è stata colpita e chi li ha colpiti. Puoi vedere queste informazioni in Python con lil comando print hitBlock().

Usiamo getBlock(x, y, z) con hitBlock() e if per determinare se il blocco colpito è di luminite.
Nel caso lo sia, usiamo setBlock(x, y, z, blockId) per trasformarlo in pietra prima di ridurre il valore di numero_blocchi_illuminati e aumentare di 1 il punteggio del giocatore punti:

if mc.getBlock(hitBlock.x, hitBlock.y, hitBlock.z) == block.GLOWSTONE_BLOCK.id:
	mc.setBlock(hitBlock.x, hitBlock.y, hitBlock.z, block.STONE.id)
	numero_blocchi_illuminati = numero_blocchi_illuminati - 1
	punti = punti + 1

Esegui il programma.

Dovrebbe apparire il gioco e, questa volta, quando i blocchi illuminati dovrebbero spegnersi se li colpisci con la spada facendo clic con il tasto destro del mouse.

Game over

Devi ora far sapere al giocatore quando il gioco è finito e quanti punti ha ottenuto.
L’ultimissima riga di codice del programma sarà:

mc.postToChat("Game Over - punti = " + str(punti))

Il programma completo è:

import mcpi.minecraft as minecraft
from mcpi import block
import random
from time import sleep

mc = Minecraft.create()
mc.postToChat("Minecraft Whac-a-Block”)

x, y, z = mc.player.getTilePos()
mc.setBlocks(x - 1, y, z + 3, x + 1, y + 2, z + 3, block.STONE.id)

mc.postToChat("Get ready ...")
sleep(2)
mc.postToChat(“Go")

numero_blocchi_luminite = 0
punti = 0

while numero_blocchi_luminite < 9:	

	sleep(0.2)
	
	for hitBlock in mc.events.pollBlockHits():
		if mc.getBlock(hitBlock.x, hitBlock.y, hitBlock.z) == block.GLOWSTONE_BLOCK.id:
		mc.setBlock(hitBlock.x, hitBlock.y, hitBlock.z, block.STONE.id)
		numero_blocchi_illuminati = numero_blocchi_illuminati - 1
		punti = punti + 1
	
	numero_blocchi_luminite = numero_blocchi_luminite + 1
	trasformato_in_luminite = False
	while not trasformato_in_luminite:
		xPos = x + random.randint(-1, 1)
		yPos = x + random.randint(0, 2)
		zPos = z + 3
		if mc.getBlock(xPos, yPos, zPos) == block.STONE.id:
			mc.setBlock(xPos, yPos, zPos, block.GLOWSTONE_BLOCK.id)
			trasformato_in_luminite = True

mc.postToChat("Game Over - punti = " + str(punti))

E adesso?

Al momento il gioco che hai costruito è piuttosto semplice, ma, partendo da questa base, puoi fare molte cose in più.
Ecco alcune idee:

  • La difficoltà del gioco dipende da quanto il programma attende prima di accendere un nuovo blocco. Per ora abbiamo posto time.sleep(0.2). Un valore più alto semplifica il gioco, uno più basso lo rende più difficile. Prova a fare qualche esperimento per vedere quale valore ti sembra essere il migliore.
  • Cosa succede quando il giocatore sbaglia e colpisce un blocco di pietra e non uno di luminite? Puoi modificare il programma in modo che, in questo caso, il blocco diventi di luminite? Questa modifica costringerebbe il giocatore a stare più attento e richiederebbe una sua maggiore bravura.
  • Di solito i videogiochi sono semplici all'inizio e diventano progressivamente più difficili. Puoi fare in modo che il tuo gioco diventi è più difficile man mano aumenta il punteggio?

  • This learning resource is provided for free by the Raspberry Pi Foundation under a Creative Commons licence.
    Find more at raspberrypi.org/resources and github.com/raspberrypilearning.