Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Interfacce touch, CCLayer e sprite

Gli aspetti progettuali e implementativi dell'interfaccia touch
Gli aspetti progettuali e implementativi dell'interfaccia touch
Link copiato negli appunti

Subito dopo l'apertura dell'AppStore (avvenuta il 10 Luglio del 2008) l'iPhone e l'iPod Touch sono stati sommersi da una valanga di giochi. Essendo il paradigma di interazione tra utente e gioco decisamente nuovo rispetto a quanto esistente (sì, il Nintendo DS aveva un'interfaccia touch ma questa era accompagnata dai classici controlli fisici) si è assistito a una serie di esperimenti nella creazione di interfacce.

Alcuni classici per CoinUp o console sono stati "portati" su iPhone inserendo un'interfaccia virtuale in sovrimpressione (ad esempio il classico stick analogico accompagnato da alcuni pulsanti) in modo da offrire al giocatore tutte le funzionalità della versione originale. Purtroppo questo tipo di soluzione presto ha mostrato i suoi limiti a causa delle seguenti ragioni:

  • L'interfaccia virtuale copre parte della schermata del gioco. Rendere i controlli semitrasparenti non aiuta perché l'utente sarà costretto a tenere le dita su di essi (ad esempio sullo stick analogico). E la dimensione limitata dello schermo aggrava il problema.
  • L'interfaccia fisica di input fornisce un feedback tattile: che si tratti dello stick di un coinup o della croce direzionale del pad di una console, l'interfaccia non non si limita a ricevere input dall'utente ma fornisce anche un feedback. Infatti tramite il tatto è possibile sapere se stiamo premendo correttamente una freccia della croce direzionale o se stiamo premendo un pulsante senza la necessità di dover controllare con lo sguardo. Su un dispositivo touch non esiste (ad oggi) questo tipo di feedback. Quindi spesso il giocatore non si rende conto di non avere il dito esattamente sul controllo virtuale e quindi crea una situazione di incertezza che mina profondamente l'esperienza.

In generale bisogna prendere atto del fatto che ogni genere di videogioco darà il suo meglio con una specifica interfaccia. Questo concetto non è nuovo, i First Person Shooter sono sempre stati più facili da giocare con una tastiera e un mouse che con un pad di una console. Un platform è più immediato se giocato con un pad. I puzzle game invece (come Angry Birds o Trainyard) probabilmente forniscono un'esperienza ottimale su un touch screen.

Quindi se avete in mente di portare un classico sull'interfaccia touch offerta dai dispositivi iOS dovreste porvi seriamente il problema di come progettare l'interfaccia in modo che sfrutti il meglio di questa tecnologia.

Un ottimo esempio è Limbo, un gioco nato per piattaforma Desktop la cui versione per iOS è stata rilasciata con un eccellente sistema di input che raramente fa sentire la mancanza di una tastiera fisica.

Se invece state progettando un gioco basato su una vostra idea dovreste affrontare il problema del controllo sin dall'inizio in modo da rendere l'interazione il più naturale possibile.

Rilevamento del touch con CCLayer

Il prossimo passo consiste nel rilevare i touch dell'utente. Vediamo come fare se, ad esempio, vogliamo rilevare il touch dello sprite sullo schermo. Purtroppo non esiste un meccanismo diretto per rendere uno sprite sensibile al touch, questo compito è svolto dalla classe CCLayer. Quindi dovremo rilevare tutti i touch ricevuto dalla classe HelloWorldLayer (che estende CCLayer) e successivamente calcolare se le coordinate del touch si sovrappongono a quelle dello sprite.

Aggiungiamo il seguente metodo al sorgente HelloWorldLayer.m

- (void)beginDetectingTouch
{
	[[CCDirector sharedDirector].touchDispatcher addTargetedDelegate:self priority:1 swallowsTouches:YES];
}

E invochiamolo nel metodo init.

- (id)init
{
	if (self = [super init]) {
		Ball * ball = [Ball ball];
		CGSize screenSize = [[CCDirector sharedDirector] winSize];
		ball.position = ccp(ball.contentSize.width/2 , screenSize.height/2);
		[self addChild:ball];
		[ball moveForever];
		[self beginDetectingTouch];
	}
	return self;
}

Abbiamo passato self come primo parametro del metodo addTargetedDelegate. Il metodo si aspetta qualcosa di questo tipo

id<CCTargetedTouchDelegate>

ovvero un puntatore a un oggetto che sia conforme al protocollo CCTargetedTouchDelegate. La classe CCLayer (che HelloWorldLayer estende) è già conforme a questo protocollo quindi il compilatore non avrà nulla da obiettare!

Se apriamo il protocollo CCTargetedTouchDelegate osserviamo che dichiara 4 metodi.

- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event;
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event;
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event;
- (void)ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event;

Implementando questo 4 metodi potremo ricevere una notifica ogni volta che, rispettivamente, l'utente tocca lo schermo, sposta il dito, solleva il dito dallo schermo o il touch viene riconosciuto come cancellato.

Solo il primo metodo è obbligatorio e CCLayer lo implementa, tuttavia noi eseguiremo un override nella nostra classe.

Aggiungiamo questo metodo a HelloWorldLayer.m

- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
	NSLog(@"%@", NSStringFromSelector(_cmd));
	return YES;
}

La prima riga stampa sul log le informazioni relative al metodo corrente. Inoltre stiamo ritornando il valore YES per comunicare che l'evento è stato gestito e non deve essere propagato.

Premete CMD+R e provate a toccare lo schermo (cliccare con il mouse dato che stiamo usando il simulatore).

Ogni volta che cliccate sullo schermo dovrebbe apparire una riga di log di questo tipo.

2013-07-16 21:16:39.866 MyFirstGame[3067:c07] ccTouchBegan:withEvent:

Rilevare il touch su uno sprite

Come accennato in precedenza, per capire se il touch è stato eseguito su uno sprite dobbiamo calcolare manualmente l'area dello schermo ricoperta dallo sprite e le coordinate del touch.

Aggiungiamo questo metodo a HelloWorldLayer.m:

- (CCSprite*)findSpriteFromTouch:(UITouch*)touch
{
	for (CCNode * node in self.children) {
		if ([node isKindOfClass:CCSprite.class]) {
			CCSprite * sprite = (CCSprite *) node;
			BOOL spriteTouched = CGRectContainsPoint(sprite.boundingBox, [self convertTouchToNodeSpace:touch]);
			if (spriteTouched) {
				return sprite;
			}
		}
	}
	return nil;
}

Il for scorre tutti i figli del layer corrente, stiamo assumendo che i figli siano di tipo CCNode (o sottoclassi) e questo è garantito da cocos2d.

Successivamente viene controllato se il nodo che stiamo controllando sia di tipo CCSprite. In tal caso viene verificato che il punto del touch sia all'interno del rettangolo che contiene lo sprite, in tal caso viene ritornato lo sprite.

Se invece le coordinate del touch non appartengono a nessuno sprite il ciclo for termina e viene restituito nil.

Possiamo ora modificare il metodo ccTouchBegan:withEvent come segue:

- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
	CCSprite * sprite = [self findSpriteFromTouch:touch];
	if (sprite) {
		NSLog(@"touch over sprite!");
	}
	return YES;
}

Provate nuovamente a compilare ed eseguire con CMD+R, stavolta dovreste vedere sul log il testo touch over sprite solo quando eseguite un touch (click) sullo sprite!

Ti consigliamo anche