• Programmazione Android
  • CORSI ONLINE
  • Web Agency

Logo

Corsi di programmazione web e mobile online
Navigation
  • Home
  • CORSI ONLINE
  • Tutorial Pratici
  • GUIDE COMPLETE
    • Corso completo di C
    • Corso videogame con Cocos2d
    • Programmazione Cocoa Touch
  • Sezioni
    • Libri e manuali
    • Tips & Tricks
    • Risorse utili
    • Strumenti di Sviluppo
    • Materiale OpenSource
    • Framework
    • Guide Teoriche
    • Guide varie
    • Grafica e Design
    • iPad
    • News
    • Video Tutorial
    • Windows Phone
  • Pubblicità
  • About
    • Chi siamo
    • Pubblicazioni
    • Collabora
    • Sostieni devAPP

T#112 – Animazioni ed effetti per le nostre applicazioni iPhone e iPad

By the Evil Doc Ham | on 31 Gennaio 2013 | 2 Comments
Senza categoria

tutorial-beginner-devAPP In questo nuovo tutorial di programmazione iOS parleremo di un argomento interessante e utile ad ogni sviluppatore iPhone o iPad: le animazioni. Se per un genere di applicazioni queste sono infatti addirittura fondamentali (vedi i videogame) per molte altre possono tornare utili ed essere sfruttate per rendere più gradevoli le applicazioni mobili che si stanno realizzando. Lungo il tutorial prenderò ad esempio la mia app “ABC x Alieni” (è gratuita per pochi giorni, scaricatela! 😉 ), un gioco per bambini di età prescolare, di cui illustrerò e renderò open source gran parte del codice. Questo gioco mi è stato “commissionato” dai miei figli e in un primo tempo avevo pensato di utilizzare cocos2D, poi visto che le dinamiche mi sembravano relativamente semplici ho pensato che potesse essere una buona palestra per studiare Core Animation e le animazioni in generale. Questo per un motivo molto semplice: i framework “game” mal si adattano alla realizzazione di app “utility” o business e anche solo la loro integrazione con l’UIKit è talora farraginosa e poco pratica. Le tecniche illustrate in questo articolo potranno invece facilmente essere utilizzate in qualunque app vogliate sviluppare al fine di renderla più accattivante e piacevole, requisito ormai fondamentale anche in una semplice to-do list (vedi il successo mondiale dell’app Clear)!

Nota: Una piccola precisazione prima di iniziare: siete liberi di utilizzare tutto o in parte il codice riportato (possibilmente citando la fonte…) ma non le immagini (opera di Irene Dapo), che sono fornite al solo titolo di rendere completo il tutorial.

Il Progetto

Cominciamo con una breve panoramica. La schermata di gioco dell’app, presenta due bottoni direzionali su cui viene gestita la pressione continua (quindi non solo il tap singolo), un oggetto in movimento (il carrellino), due alieni che si animano (e alle volte saltano) quando il carrello si muove nella loro direzione, un oggetto che cade dall’alto. Inoltre muovendo il dispositivo l’ombra degli oggetti cambia di inclinazione.

La disposizione di partenza degli oggetti nella view è fatta usando un normalissimo xib di una UIView di un UIViewController. Rimando alle guide di base la creazione del progetto, del view controller e dello xib. Vi faccio notare però i primi due “trucchi” usati: primo i vari oggetti che verranno animati non sono delle UIImageView, ma delle semplici UIView, perché ci interessa definire qui solo l’ingombro e la posizione, e secondo le “sprite” sono messe in modo che gli oggetti siano sempre contro il bordo destro o sinistro, a prescindere dalle dimensioni della view, in modo da ottenere senza sforzo un’app che si adatti per l’iPhone 4 o 5. Non è sempre possibile, ma spesso con piccoli accorgimenti come questo si può evitare di dover avere 2 differenti xib per gestire le differenti versioni dello schermo degli iPhone.

La gestione degli eventi avviene nel “game loop”, ovvero un ciclo “infinito”, scandito da un timer, che ogni decimo di secondo controlla e aggiorna lo stato del gioco. Così facendo ho già deciso la cadenza degli eventi: aggiornamenti, movimenti e interazioni sono valutate ogni decimo di secondo, un tempo che mi sembrava adatto al tipo di gioco che volevo realizzare: simpatico, non frenetico e “rilassante”.

Completano il tutto alcune funzioni per la generazione di numeri casuali (usate per scegliere da quale punto e quale numero far cadere e per decidere quanto in alto far saltare gli alieni) e l’attivazione della rilevazione dell’accellerometro: quest’ultimo non è usato per gestire il movimento, ma per dare un tocco pseudo-3D al gioco, aggiungendo delle ombre che si muovano in base all’inclinazione, come se gli oggetti avessero una profondità e cambiasse l’inclinazione della luce.

Per comprendere meglio quanto appena descritto e quanto seguirà vi consiglio di scaricarvi il progetto allegato (vedi a fondo articolo) e provarlo.

Tecniche usate: Sprite Sheet

Una tecnica molto usata per le animazioni nei videogiochi è quella dello Sprite Sheet, chiamati anche “sprite atlas”, atlanti), ovvero la creazione di una (grossa) immagine contenente tutti i vari “frame” di un’animazione per poi visualizzarne un pezzo per volta. Perché complicarsi la vita vi chiederete? Non è più semplice avere 10 immagini separate e visualizzare di volta in volta quella necessaria? Beh forse è più semplice, ma le prestazioni cambiano drasticamente: ogni volta che si visualizza un’immagine questa viene caricata nella memoria della GPU (il processore grafico) e questa operazione è abbastanza lenta. Mentre mostrare una parte dell’immagine che già è in memoria è di gran lunga più veloce. Quindi la tecnica migliore consiste nel creare i vari “fotogrammi” singolarmente e poi accorparli usando metodi più o meno ottimizzati. Ovviamente consuma più memoria, quindi ci vuole un po’ di attenzione.

Esistono Tool professionali (pensati per cocos2D, Corona e altri framework simili) che consentono di “impacchettare” all’estremo le immagini, eliminando le parti trasparenti e memorizzando poi delle mappe di coordinate grazie alle quali si potrà ricostruire l’oggetto iniziale, ma qui vi illustro il metodo più semplice e “fai da te”, ovvero quello in cui tutti i frame hanno la stessa dimensione e orientamento. Così facendo basta sapere quanto è grande il frame di riferimento e individuare la porzione giusta dell’atlante da visualizzare. Non solo, creare uno sprite sheet diviene una semplice operazione di copia-incolla gestibile con Anteprima, senza ricorrere a Photoshop, Gimp o altri programmi specializzati.

Un ultimo dettaglio riguarda le dimensioni. Nella GPU gli sprite sheet posso avere come dimensione solo quadrati con potenze di due di lato: perciò avere un atlas rettangolare di 1025 per 500 pixel vuol dire occupare un quadrato di 2048×2048 pixel in memoria! Ma questo è solo un limite della GPU, non di Core Animation, quindi in questo tutorial ho volutamente usato spritesheet rettangolari, perché lo scopo era illustrare le potenzialità del framework, anche a livello di semplicità: credetemi se non dovete animare troppi oggetti è inutile stare ad impazzire per convincere il vostro grafico della necessità di “schiacciarli” in pochi pixel 😉

Per gestirli ho rielaborato una classe open source di Miguel Angel Friginal (trovata mentre cercavo altro, come spesso capita), di cui per correttezza ho mantenuto il nome originale. Vediamo come funziona.

Per prima cosa dobbiamo creare uno sprite sheet, o meglio una subclass di CALayer che lo gestisca. La init di questo layer vuole due parametri: l’immagine con l’atlas degli sprite e la dimensione del singolo sprite. L’immagine diventerà il contents del layer (che contenendo tutti gli sprite li caricherà una volta sola nella memoria della GPU) mentre il CGSize sarà usato per definire il bounds (la dimensione) del layer e il suo contentsRect. Quest’ultimo è un parametro un po’ “strano” a prima vista, perché è un CGFrame che può contenere solo valori (float) nel range 0.0-1.0. Serve ad indicare quale porzione del contents deve essere realmente visualizzata, ma è una misure relativa, dove 0 è niente e 1 è tutto.

Quindi se gli dico (0.5,0.5),(1,1) gli dico che il quarto in basso a destra dell’immagine complessiva è quello che va visualizzato nel layer:

- (id)initWithImage:(CGImageRef)img sampleSize:(CGSize)size;{
   
   self = [super init];

   if (self != nil){
      self.contents = (__bridge id)img;
      self.spriteIndex = 1;
      CGSize sampleSizeNormalized = CGSizeMake(size.width/CGImageGetWidth(img), size.height/CGImageGetHeight(img));
      self.bounds = CGRectMake( 0, 0, size.width, size.height);
      self.contentsRect = CGRectMake( 0, 0, sampleSizeNormalized.width, sampleSizeNormalized.height );
   }

   return self;
}

La property “spriteIndex” indica quale sprite vogliamo visualizzare (per semplicità di calcolo deve partire da 1. Pertanto quando dobbiamo visualizzare il layer basterà calcolare il “contentsRec”t in base a quella property e automaticamente verrà visualizzato lo sprite desiderato.

/* calcola le coordinate esatte della porzione di sheet da visualizzare */
- (void)display;
{
if ([self.delegate respondsToSelector:@selector(displayLayer:)])
{
[self.delegate displayLayer:self];
return;
}

unsigned int currentSampleIndex = [self currentSampleIndex];
if (!currentSampleIndex)
return;

CGSize sampleSize = self.contentsRect.size;
self.contentsRect = CGRectMake(
((currentSampleIndex - 1) % (int)(1/sampleSize.width)) * sampleSize.width,
((currentSampleIndex - 1) / (int)(1/sampleSize.width)) * sampleSize.height,
sampleSize.width, sampleSize.height
);
}

A questo punto non ci resta che usarlo: prima lo creo e posiziono:

self.zipSprite = [MCSpriteLayer layerWithImage:[UIImage imageNamed:@"ZIP2.png"].CGImage sampleSize:CGSizeMake(100, 132)];
self.zipSprite.position = CGPointMake(50, 66);
[self.zipView.layer addSublayer: self.zipSprite];

Quindi, nei punti dove gestisco la logica che sta dietro all’animazione degli sprite mi basterà cambiare l’indice, ovvero:

[self.zipSprite showFrame:zipIndex];

Ombre e accellerometro

Spesso l’accellerometro è usato nei giochi per gestire il movimento del personaggio. Io invece l’ho usato per dare un po’ di senso prospettico al gioco. Gli oggetti in movimento hanno infatti un’ombra che si muove in base all’inclinazione del dispositivo, dando un piacevole effetto di profondità alla scena. Le ombre sono una caratteristica fornita dai CALayer e gestibile con semplici property, quindi è facile ottenere buoni effetti con poche istruzioni. Vediamole:

- (void)viewDidAppear:(BOOL)animated{
   [super viewDidAppear:animated];
   // mi registro agli eventi dell'accellerometro
   UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];
   accelerometer.delegate = self;
   accelerometer.updateInterval = 0.25;
   ...
}

qui informo iOS che sono interessato agli eventi dell’accellerometro, che mi saranno comunicati in:

- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration{
   CGSize shadow = CGSizeMake(acceleration.y*GAMELOOP_ShadowsRadius,(acceleration.x+acceleration.z)*GAMELOOP_ShadowsRadius);
   self.zipView.layer.shadowOffset = shadow;
   ...
}

dove semplicemente calcolo il raggio e l’inclinazione dell’ombra e lo imposto a tutti gli oggetti che mi interessano.

Pressione continua

iOS non fornisce automaticamente un UIGestureRecognizer per gestire la pressione continua di un bottone: ci sarebbe il “long press”, ma in realtà non è la stessa cosa e non va bene per simulare un joypad. In questo gioco l’interazione è molto semplice, basta premere (e tener premuto…) uno dei bottoni e il carrellino si muoverà in quella direzione.

Per ottenere il risultato creo due IBAction e in Interface Builder le assegno ad alcuni eventi significativi dei due bottoni di direzione: theTouchDown all’event Touch Down e theTouchUp a Touch Up Inside e a Touch Up Outside. In entrambi i metodi setto la variabile globale moveButtonPressed che verrà poi utilizzata durante il game loop per sapere se e in che direzione muovere il carrellino.

/* il bottone risulta premuto fino a che non ricevo un touch up */
- (IBAction)theTouchDown:(id)sender{
   moveButtonPressed = ((UIButton*)sender).tag;
}

/* fine della "pressione continua" */
 - (IBAction)theTouchUp:(id)sender{
   if (((UIButton*)sender).tag == moveButtonPressed) // solo se il sollevato è quello che aveva originato la pressione
      moveButtonPressed = 0;
}

In pratica mi “segno” solo qual è il bottone che il giocatore sta premendo, poi gestirò le conseguenza di questa azione nel game loop.

Movimento

Qui ho usato un mix di tecniche, cercando sempre la strada più semplice nel rispetto della fluidità del gioco. Essendo gli oggetti dei CoreAnimationLayer avrei potuto usare le animazioni di quel framework (potenti ma non sempre banali da gestire), ma dato che ogni CALayer è incapsulato dentro una UIView e che queste ultime hanno delle animazioni di alto livello facilissime da usare la scelta è caduta su queste ultime.

Ad esempio il carrello lo muovo così:

// cambio lo sprite da visualizzare
[self.carrelloSprite showFrame:carrelloIndex];
// e lo muovo: l'animazione viene fluida ma nello stesso tempo a "scatti" che sa molto di giochino per bimbi
[UIView animateWithDuration:GAMELOOP_AnimationInterval delay:0 options:UIViewAnimationCurveLinear animations:
^{
self.carrelloView.center = CGPointMake(xpos,self.carrelloView.center.y);
} completion:nil];

Mentre per gli alieni che saltano uso semplicemente due animazioni “accodate”:

/* l'oggetto è stato preso, gli alieni saltano felici :) */
- (void) zipZapHappy{
   // faccio fare ai due alieni un salto di altezza casuale
   float zipH = (arc4random()%50)+10;
   float zapH = (arc4random()%50)+10;
   // l'uso di UIViewAnimationCurveEaseOut per la parte di salita e di easeIn per la discesa rende il salto più "naturale"
   [UIView animateWithDuration:GAMELOOP_AnimationInterval*8 delay:0 options:UIViewAnimationCurveEaseOut animations:
^{
self.zipView.center = CGPointMake(self.zipView.center.x,self.zipView.center.y-zipH);
self.zapView.center = CGPointMake(self.zapView.center.x,self.zapView.center.y-zapH);
} completion:^(BOOL finished) {
[UIView animateWithDuration:GAMELOOP_AnimationInterval*7 delay:0 options:UIViewAnimationCurveEaseIn animations:
^{
self.zipView.center = CGPointMake(self.zipView.center.x,self.zipView.center.y+zipH);
self.zapView.center = CGPointMake(self.zapView.center.x,self.zapView.center.y+zapH);
} completion:nil];
}];
...
}

Prima faccio una animazione “in frenata” (UIViewAnimationCurveEaseOut) verso l’alto, poi una “in accelerazione” (UIViewAnimationCurveEaseIn) verso il basso: questo unito all’altezza casuale del salto rende il movimento degli alieni più naturale, pur senza usare alcun motore fisico come Box2d, Chipmunk e simili.

Da notare che essendo la scelta degli sprite e lo loro visualizzazione indipendente da queste animazioni, se il giocatore preme i bottoni gli alieni si animeranno anche mentre saltano: un effetto speciale completamente gratuito! 😉

Conclusione

Spero che questo tutorial vi abbia dato le basi per poter esplorare Core Animation e fornito spunti per impreziosire le UI delle vostre app con effetti e animazioni gradevoli e di effetto. Nel codice ci sono molti commenti che dovrebbero aiutare a comprendere meglio il tutto, ma se avete qualche dubbio o domanda potete aprite un post qui sul nostro forum gratuito.

Alla prossima
Il Malvagio Dott. Prosciutto
(@theEvilDocHam)

Se avete problemi con il progetto presentato, questo è il link per scaricare il nostro esempio (in arrivo).

Vuoi ringraziare l’autore di questa guida?
Offrigli un caffè scaricando la sua applicazione 🙂

ABC-x-Alieni

Share this story:
  • tweet

Tags: animazioni xcodeCore AnimationiOS Dev Tutorialios game developmentsprite iossprite sheetsspritesheetTutorial Pratici

Recent Posts

  • Parte il percorso programmatori iOS in Swift su devACADEMY.it

    20 Dicembre 2017 - 0 Comment
  • Android, crittografare dati velocemente con Encryption

    24 Settembre 2018 - 0 Comment
  • Sql2o, accesso immediato ai database tramite Java

    3 Settembre 2018 - 0 Comment
  • Okio, libreria per ottimizzare l’input/output in Java

    27 Agosto 2018 - 0 Comment

Related Posts

  • 04. Sprite e texture atlas (sprite sheets): animiamo in nostri personaggi

    23 Luglio 2012 - 15 Comments
  • T#104 – CAEmitterLayer: Creiamo un simpatico generatore di fiocchi di neve con il nostro iPhone

    15 Dicembre 2011 - 7 Comments
  • T#097 – Animiamo il testo all’interno di un’UILabel

    17 Giugno 2011 - 17 Comments

Author Description

Fabio detiene uno dei più assurdi nickname del panorama iOS: 'il Malvagio Dottor Prosciutto', opera dei suoi figli. Twitta di iOS e mondo mobile in generale col nick @theEvilDocHam

2 Responses to “T#112 – Animazioni ed effetti per le nostre applicazioni iPhone e iPad”

  1. 4 Febbraio 2013

    Simone

    Bel tutorial aspetto l’esempio!

  2. 17 Agosto 2013

    Cronoluis

    Ciao, potreste caricare l’esempio? Qualcosa non mi quadra…

    Grazie mille, fantastici!

Leave a Reply

Your email address will not be published. Required fields are marked *


*
*

Corso online di programmazione android e java

SEZIONI

  • Android
  • Comunicazioni
  • Contest
  • Corsi ed Eventi
  • Corso completo di C
  • Corso programmazione videogiochi
  • Framework
  • Grafica e Design
  • Guida rapida alla programmazione Cocoa Touch
  • Guide Teoriche
  • Guide varie
  • iPad
  • Le nostre applicazioni
  • Libri e manuali
  • Materiale OpenSource
  • News
  • Pillole di C++
  • Progetti completi
  • Risorse utili
  • Strumenti di Sviluppo
  • Swift
  • Tips & Tricks
  • Tutorial Pratici
  • Video Tutorial
  • Windows Phone

Siti Amici

  • Adrirobot
  • Allmobileworld
  • Apple Notizie
  • Apple Tribù
  • Avvocato360
  • Blog informatico 360°
  • bubi devs
  • fotogriPhone
  • GiovaTech
  • iApp-Mac
  • iOS Developer Program
  • iPodMania
  • MelaRumors
  • Meritocracy
  • SoloTablet
  • TecnoUser
  • Privacy & Cookie Policy
©2009-2018 devAPP - All Rights Reserved | Contattaci
devAPP.it è un progetto di DEVAPP S.R.L. - Web & Mobile Agency di Torino
Str. Volpiano, 54 - 10040 Leini (TO) - C.F. e P.IVA 11263180017 - REA TO1199665 - Cap. Soc. € 10.000,00 i.v.

devACADEMY.it

Vuoi imparare a programmare?

Iscriviti e accedi a TUTTI i corsi con un’unica iscrizione.
Oltre 70 corsi e migliaia di videolezioni online e in italiano a tua disposizione.

ISCRIVITI SUBITO