DevTutorial #14 – Creiamo il nostro primo gioco, Pong! (Parte II)

Siamo pronti per la seconda parte del tutorial relativo al nostro gioco Pong! Nella scorsa puntata abbiamo visto come dichiarare ed implementare i vari componenti che ci occorrono, ed abbiamo anche dato una fisica alla pallina. Oggi implementeremo invece tutte le parti restanti, ovvero il movimento delle due racchette, un’intelligenza per il computer e la gestione dei punteggi.

Ovviamente le implementazioni non saranno perfette, ma abbastanza semplici, in modo da poter essere comprensibili a tutti. Chi volesse proporre soluzioni migliori può farlo tranquillamente nei commenti! Quindi, sotto con la programmazione!

1. Interazione dell’utente

La prima cosa che dobbiamo gestire è l’interazione dell’utente, che deve poter giocare con il nostro Pong! Dobbiamo iniziare, quindi, implementando la possibilità di muovere la proprio barra! Per far questo modifichiamo il “touchesBegan” in “PongTutorialViewController.m” nella seguente maniera:

Immagine 1

Questo servirà solo per rilevare l’inizio di un’azione da parte dell’utente. Il movimento vero e proprio, invece, è gestito da un altro metodo, “touchesMoved”, che infatti viene richiamato nel caso in cui la partita sia in corso. Ovvero, se la partita è già stata avviata e l’utente esegue un movimento (ovvero tocca lo schermo), significa che vuole muovere la sua barra, quindi noi richiamiamo il metodo “touchesMoved”, che si occupa proprio di gestire il movimento. Ecco il codice di tale metodo:

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
	//creiamo un oggetto con le informazioni del "touch" fatto dall'utente
	UITouch *touch = [[event allTouches] anyObject];
	//rileviamo le coordinate del tocco fatto dal'utente
	CGPoint location = [touch locationInView:touch.view];
	//creiamo un oggetto con le nuove coordinate in cui dobbiamo spostare la barra
	//(quella dell'utente, ovvero la barraDue)
	CGPoint xLocation = CGPointMake(location.x,barraDue.center.y);
	//muoviamo la barra dell'utente
	barraDue.center = xLocation;
}

Cosa fa questo metodo? Potete già inturilo dai commenti presenti nel codice. Quando l’utente compie un movimento sullo schermo, noi intercettiamo l’azione (chiamata “touch”), da cui ricaviamo le coordinate, e muoviamo di conseguenza la barra dell’utente (la barraDue, ovvero quella posta in basso; avremmo potuto anche scegliere la barraUno, non c’era nessuna differenza!).

3. Gestiamo la collisione tra gli oggetti

Ora dobbiamo gestire la collisione tra i vari componenti. In altre parole, dobbiamo far si che la pallina rimbalzi quando arriva su una delle due barre, e non passi attraverso come succede ora. Potrebbe sembrare complicato gestire le collisioni, invece si rivelerà tutto molto semplice grazie ad un componente offerto da Cocoa: “CGRectIntersectsRect”. Questo oggetto eseguirà i calcoli necessari, e ci dirà se due rettangoli (perchè sia la pallina che le barre sono contenuti in rettangoli di tipo UIImageView) sono in contatto tra di loro.
Andiamo nel metodo “gameLoop” ed inseriamo il seguente codice:

Immagine 3

Semplice vero? Non facciamo altro che usare il componente “CGRectIntersectsRect”, che ci dirà se i due oggetti sono a contatto. In tal caso invertiamo la velocità della pallina, proprio come avevamo già fatto in precedenza per invertire la direzione quando si arriva al bordo dello schermo.

4. AI: l’intelligenza del computer!

Eccoci a una parte molto importante e difficile del gioco, ovvero l’Intelligenza Artificiale (AI) del giocatore comandato dal computer. Purtroppo elaborare un algoritmo efficacie e realistico sarebbe troppo complicato per questo tutorial, e certo non è il nostro scopo. Ci limiteremo, quindi, a una semplice implementazione, che vi permetterà di avere un computer “poco intelligente”. Per questo tutorial, comunque, sarà più che sufficiente.

La prima cosa da fare è definire una costante, che sarà la velocità di reazione del computer. Nel file “PongTutorialViewController.m” inseriamo quindi la seguente dichiarazione:

Immagine 4

Ovviamente potrete variare questo valore per avere un computer con una diversa intelligenza. Più alto sarà il valore, più il computer sarà veloce e quindi “forte”; analogamente, abbassando il valore il computer sarà lento e “debole”.

Inseriamo ora il codice dell’AI. In “gameLoop” inseriamo le seguenti istruzioni:

Immagine 5

Analizziamo quello che abbiamo scritto. Il primo “if” aggiunge un po’ di difficoltà al computer, in quanto gli permette di muoversi solo quando la pallina è nella sua metà campo; quindi, se la pallina sarà nella metà alta dello schermo, la barra del computer potrà muoversi, altrimenti dovrà rimanere ferma. I due “if” successivi, invece, muovo la racchetta del computer. Se la pallina (e per la precisione il suo centro) è a sinistra della racchetta (primo if), spostiamo il centro della barra verso sinistra, sottraendo dalle coordinate il valore della velocità del computer. Stessa cosa effettuiamo se la pallina è a destra della barra, ovviamente spostando verso destra la barra stessa.

Come potete vedere non è nulla di particolare, è un algoritmo molto semplice. Ci permetterà, comunque, di poter giocare al nostro personalissimo Pong! Potete anche notare, infine, come sia proprio la costante “kVelocitaMovComputer” a decidere la reattività del computer, in quanto determina la velocità con cui la barra si deve muovere verso la pallina. Più la barra è veloce, più sarà difficile per noi segnare!

5. Implementiamo il meccanismo del punteggio

L’ultima cosa che manca al nostro gioco è il conteggio del punteggio! Dobbiamo implementare proprio questo meccanismo. Il funzionamento, come potete facilmente intuire, sarà semplice: quando la pallina supera la barraUno, sarà punto per il giocatore due (ovvero l’utente), mentre quand la pallina supera la barraDue, sarà il computer ad aver fatto punto.

Iniziamo dichiarando, nel file “PongTutorialViewController.h” due variabili e un oggetto che ci serviranno:

Immagine 6

Abbiamo definito le due variabili che conterranno i due punteggi, e un metodo, che dovrà riavviare la partita quando sarà terminata. Ci sorge quindi subito un quesito: quando una partita termina? Facciamo noi una scelta e imponiamo che la partita finisca quando un giocatore totalizzi 5 punti.
Andiamo, quindi, in “PongTutorialViewController.m” e impostiamo una costante relativa a questo punteggio massimo, così:

Immagine 7

Il limite che abbiamo fissato sarà quindi a 5 punti. Ovviamente potremo variare a nostro piacere questo valore.

Dobbiamo adesso implementare il codice che conta i punti. Nel metodo “gameLoop”, dopo l’AI del computer, inseriamo questo codice:

Immagine 8

Come possiamo vedere il codice è molto semplice. Analizziamo solo il primo “if”, visto che il secondo è uguale ma cambia solo per il giocatore conrollato.Viene controllato se la coordinata y della pallina è minore di 0 (ovvero se è oltre il bordo superiore dello schermo): in caso il controllo dia esito positivo, viene incrementato il punteggio del giocatore due, e viene richiamato il metodo reset, che andremo ad implementare fra poco. Potete vedere che il metodo reset ha come parametro un confronto, tra il punteggio del giocatore e quello massimo. Questo serve in quanto il metodo reset dovrà sapere se la partita è terminata (perchè uno dei due giocatori ha raggiunto 5 punti), oppure se la partita non è ancora finita.

L’ultima cosa che dobbiamo implementare è il metodo reset. Questo metodo dovrà riportare i componenti al loro stato originale, ad ogni punto segnato. Il codice del metodo è il seguente:

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
	//creiamo un oggetto con le informazioni del "touch" fatto dall'utente
	UITouch *touch = [[event allTouches] anyObject];
	//rileviamo le coordinate del tocco fatto dal'utente
	CGPoint location = [touch locationInView:touch.view];
	//creiamo un oggetto con le nuove coordinate in cui dobbiamo spostare la barra
	//(quella dell'utente, ovvero la barraDue)
	CGPoint xLocation = CGPointMake(location.x,barraDue.center.y);
	//muoviamo la barra dell'utente
	barraDue.center = xLocation;
}

I commenti dovrebbero già darvi un’indicazione di cosa avviene all’interno di questo metodo. Si tratta, comunque, di mettere in pausa la partita, riposizionando la pallina al centro dello schermo. Dopodichè, viene eseguito un controllo sulla variabile “newGame”, che ci indica se uno dei due giocatori ha vinto: se il controllo da esito positivo, si controlla chi ha vinto, e si setta la label con un messaggio di avviso. Si azzerano, poi, i due contatori dei punteggi, per essere già pronti per una nuova partita. In caso contrario, si inserisce la scritta “Tocca per iniziare”, in modo che l’utente sappia cosa fare. Infine si settano le due label con i punteggi dei due utenti.

Abbiamo terminato!! Clicchiamo su “Build and Go!” e giochiamo con il nostro personalissimo Pong!

Immagine 10

Se Avete Problemi, questo è il nostro file di progetto.

La guida è stata creata da Andrea Busi per “iSpazio.net” e “Bubi Devs”. La versione originale inglese del tutorial è disponibile a questo indirizzo: “iPhone Game Programming Tutorial, Part 2 – iCodBlog“. I meriti quindi relativamente alla versione inglese, sono del legittimo autore.

Ingegnere informatico e sviluppatore freelance, mi occupo da anni di sviluppo per iOS (ma non solo). Dal 2008 scrivo su questo piccolo blog (con qualche lunga pausa), in cui parlo di programmazione e di qualsiasi altra cosa che mi diverta.

20 comments On DevTutorial #14 – Creiamo il nostro primo gioco, Pong! (Parte II)

  • Salve, ho visto questo tutorial molto interessante, ed anche io ho cercato di creare questo gioco; ma ho riscontrato un problema,difatti anche se il gioco conta correttamente i punti, a volte esce scritto che ha vinto il computer, anche se in realtà ho vinto io, e succede anche tutto il contrario. Ho provato anche il vostro progetto allegato, ma succede la stessa cosa. Qual’è l’errore?

  • @Francesco G.: Grazie mille, ho già provveduto a sistemare (sia il tutorial che il file del progetto)..
    Era errato il controllo nel metodo reset, avevo sbagliato oggetto.. ora dovrebbe funzionare in maniera corretta!

  • Grazie mille..poi se non disturbo potrei essere aggiornato se ci sarà qualche tutorial su come impostare altre pagine nell’applicazione, ad esempio una di informazioni, un’altra per impostare i livelli ecc? Grazie di nuovo

  • ah un ultima cosa, come è possibile poi installare l’applicazione (il gioco) sull’ipod o iphone?

  • @Francesco G.: si, prossimamente farò un tutorial sulla splash screen, poi vedrò di inventarne un altro anche 😉

    @Francesco G.: ci sono due modi: signandolo con LDID, oppure .. clicca sui link e avrai la relativa guida 😉

  • grazie mille.

  • Anche impostando kVelocitaPallaY=20 la pallina non si ferma al contatto con la barraUno
    grazie

  • @Antonio B: a me si fermava, forse dipende anche dalla dimensione della barra.. comunque prova con un valore diverso (21, 22, 23..).

  • Scusami pero se analizzi il codice reset, ogni volta che viene chiamato si ferma il gioco
    grazie

  • @Antonio B: Scusami ma non capisco.. Il metodo reset viene richiamato ogni volta che un giocatore fa punto e, se la partita è terminata, si occupa di azzerare i punteggi e iniziare una nuova partita..

  • Forse non capisco queste due prime righe all’interno del metodo
    self.statopartita = kpartitaferma
    pallina.center = self.view.center
    se la partita non è finita entra nel metodo ed esegue queste due righe poi il gameLoop trova la variabile stato a fermo.
    Ho dovuto inserire queste due righe all’interno dell’if newGame per farlo continuare.
    grazie

  • Andrea questo gioco non funziona bene, se lo esegui in questo modo si ferma ad ogni volta che un gicatore fa un punto.
    cerca di rivederlo e fare le apposite modifiche
    grazie

  • @Giancarlo B: ciao, scusa ma non capisco.. è normale che si fermi ogni volta che si fa punto, altrimenti come si fa a giocare?

  • allora, sto pensando di sviluppare questo gioco ma con delle modifiche:
    -due tipi di gioco: uno per appassionati uguale all’originale e uno più moderno per i new con movimento della barra tramite accelerometro, come posso fare a impostare due tipi di movimento barra diversi per le due view? è possibile fare un lavoro del tipo: if vista caricata è x allora … else if classe caricata è y allora…?

    P.S.
    perchè bisogna eliminare il file viewcontroller.xib? se c’è non è perchè è meglio usare quello(magari dico idiozie ma non credo che li mettano per scherzo)

    Grazie e Ciao

  • @Frahack94: ciao! sicuramente il modo che hai proposto è fattibile.. puoi scegliere di implementarlo così, tramite controlli if, oppure di creare due classi separate in base al metodo di gioco scelto..
    tieni conto che la valutazione di condizioni if richiede del tempo, quindi bisogna assicurarsi che le prestazioni del gioco non ne risentano..

    P.S: prima “andava di moda” così, molti tutorial sulla rete usavano questo modo.. puoi comunque benissimo utilizzare quella vista, semplifichi le cose 😉

  • grazie molte adesso provo.
    CIAO

  • ancora io!!!
    ti chiedo già altre due cose:
    come posso fare per impostare la partenza orizzontale del gioco? dal momento che lo sto strutturando in orizzontale in verticale non deve partire.
    come posso muovere la barra del giocatore in verticale invece che in orizzontale? perché se inverto gli assi nel touches moved mi si muove in diagonale.

    GRAZIE

  • Torno di nuovo…
    dopo aver deciso che lo facevo in verticale mi si è presentato questo problema: http://www.youtube.com/watch?v=zaEdnoxunko
    la barra del player infatti funziona come dovrebbe mentre quella del computer si muove come dovrebbe ma non fa rimbalzare la pallina,prima il problema era invertito, la prima si muoveva ma non faceva rimbalzare mentre la seconda era a posto; si sono invertite in seguito forse alla loro ricreazione in IB.
    Spero tu abbia ancora pazienza verso me…
    P.S.
    poresti fare un tutorial sulla creazione di preferenze con una gui interna all’app e un file plist come storage?

    Grazie

  • @Frahack94: ciao.. probabilmente (anzi, sicuramente XD) ha sbagliato qualche controllo.. visto così non saprei dirti di preciso, fammi sapere se hai ancora problemi..

Leave a reply:

Your email address will not be published.

Site Footer