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

La classe Mouse

Implementare il personaggio principale di un gioco mobile per iOS, utilizzando il linguaggio Swift ed il framework SpriteKit su Xcode.
Implementare il personaggio principale di un gioco mobile per iOS, utilizzando il linguaggio Swift ed il framework SpriteKit su Xcode.
Link copiato negli appunti

Al fine di implementare correttamente il nostro gioco, dovremo scrivere 3 classi:

  • Mouse, rappresentante il nostro personaggio principale;
  • Coin, rappresentante le monete da collezionare;
  • Block, rappresentante i blocchi da evitare durante la navigazione.

Queste classi estenderanno SKSpriteNode perché, di fatto, rappresentano degli sprite visibili sullo schermo. Le classi conterranno anche i comportamenti dello sprite che rappresentano.

Infine dovremo sovrascrivere il contenuto attualmente presente in GameScene per definire la logica di gioco e per gestire l’input dell'utente.

La classe Mouse

La classe che implementeremo in questa lezione sarà Mouse. Iniziamo quindi selezionando, su Xcode, il gruppo SpaceMouse dal Project Navigator.

Figura 24 (click per ingrandire)


Scegliamo quindi File > New > File…, e selezioniamo la voce Swift File, confermando con Next.

Digitiamo a questo punto il nome Mouse nel campo Save As, e clicchiamo su Create.

Sovrascriviamo quindi il contenuto del file con il codice seguente:

import SpriteKit
class Mouse: SKSpriteNode {
	private let textureFalling = SKTexture(imageNamed: "rocketmouse_fall01")
	private let textureFlying = SKTexture(imageNamed: "rocketmouse_run02")
	private let textureDown = SKTexture(imageNamed: "rocketmouse_dead02")
	private let fire = SKSpriteNode(imageNamed: "flame1")
	private unowned var gameScene: GameScene
	init(gameScene: GameScene) {
		fire.position = CGPoint(x: -37, y: -39)
		fire.zPosition = -1
		self.gameScene = gameScene
		super.init(texture: textureFalling, color: .clear, size: textureFalling.size())
		self.addChild(fire)
		let physicsBody = SKPhysicsBody(texture: textureFalling, size: textureFalling.size())
		physicsBody.contactTestBitMask = 1
		self.physicsBody = physicsBody
	}
	required init?(coder aDecoder: NSCoder) {
		fatalError("init(coder:) has not been implemented")
	}
	func fly() {
		self.removeAllActions()
		let showFlyGraphics = SKAction.run { [unowned self] in
			self.texture = self.textureFlying
			self.fire.isHidden = false
		}
		let up = SKAction.moveBy(x: 0, y: 50, duration: 0.2)
		let showFallGraphics = SKAction.run {  [unowned self] in
			self.texture = self.textureFalling
			self.fire.isHidden = true
		}
		let fall = SKAction.moveTo(y: gameScene.frame.minY + self.frame.height / 2, duration: 1)
		fall.timingMode = .easeIn
		let showTextureDown = SKAction.run {  [unowned self] in
			self.texture = self.textureDown
		}
		let sequence = SKAction.sequence([showFlyGraphics, up, showFallGraphics, fall, showTextureDown])
		self.run(sequence)
	}
	func die () {
		let showFallGraphics = SKAction.run {  [unowned self] in
			self.texture = self.textureFalling
			self.fire.isHidden = true
		}
		let fall = SKAction.moveTo(y: gameScene.frame.minY + self.frame.height / 2, duration: 0.2)
		fall.timingMode = .easeIn
		let showTextureDead = SKAction.run {  [unowned self] in
			self.texture = self.textureDown
		}
		let die = SKAction.run {  [unowned self] in
			self.gameScene.didDie()
		}
		run(SKAction.sequence([showFallGraphics, fall, showTextureDead, die]))
	}
}

Di seguito analizziamo in modo approfondito il suddetto codice.

Le property

private let textureFalling = SKTexture(imageNamed: "rocketmouse_fall01")
	private let textureFlying = SKTexture(imageNamed: "rocketmouse_run02")
	private let textureDown = SKTexture(imageNamed: "rocketmouse_dead02")

Le prime 3 property contengono le texture che useremo per rappresentare gli stati del nostro personaggio. La prima verrà visualizzata quando esso sta cadendo, la seconda mentre (dopo aver attivato il razzo) starà volando verso l’alto, e la terza mentre si trova a contatto con il terreno.

Caricare un’immagine e inizializzare una texture è un’operazione che impegna le risorse del nostro sistema, quindi è bene avere le texture già pronte
durante l’esecuzione del gioco ed evitare di doverle creare esattamente nel momento in cui è necessario.

Teniamo a mente che un gioco come questo, che gira a 60 fotogrammi al secondo, ha a disposizione 16 millisecondi per preparare il prossimo frame, quindi è bene eseguire tutte le operazioni onerose prima che il giocatore inizi la partita.

La decisione di tenere sempre in memoria le 3 texture ha delle ripercussioni sull’occupazione della memoria, quindi questa strategia va valutata di volta in volta per trovare il giusto bilanciamento tra velocità e occupazione della RAM. In questo caso, ogni modello di iPhone supportato da iOS 10 ha RAM a sufficienza per tenere tranquillamente in memoria le 3 texture.

private let fire = SKSpriteNode(imageNamed: "flame1")

Questa property viene popolata con lo sprite che rappresenta la fiamma del razzo. Lo sprite verrà aggiunto a Mouse e rimarrà sempre presente. Semplicemente, lo imposteremo come visibile solo quando il razzo è acceso, e lo renderemo invisibile quando è spento.

private unowned var gameScene: GameScene

Avremo bisogno di conoscere le dimensioni della scena del gioco. Inoltre, dovremo notificare la classe GameScene quando Mouse muore. Per questo motivo, manteniamo anche qui un riferimento alla GameScene principale.

È interessante notare che la property gameScene è definita unowned. Questo è necessario per evitare che ARC (il sistema che gestisce la memoria nella app iOS) crei un ciclo di riferimenti che precluderebbe la deallocazioni della memoria di Mouse e GameScene.

Gli Initializer

init(gameScene: GameScene) {
		fire.position = CGPoint(x: -37, y: -39)
		fire.zPosition = -1
		self.gameScene = gameScene
		super.init(texture: textureFalling, color: .clear, size: textureFalling.size())
		self.addChild(fire)
		let physicsBody = SKPhysicsBody(texture: textureFalling, size: textureFalling.size())
		physicsBody.contactTestBitMask = 1
		self.physicsBody = physicsBody
	}

Il blocco di codice precedente rappresenta l’initializer e contiene la logica per costruire un oggetto di tipo Mouse. Al suo interno viene aggiunto lo sprite della fiamma del razzo. Viene impostato il riferimento alla GameScene principale e viene creato un corpo fisico che ci sarà utile per il rilevamento delle collisioni.

required init?(coder aDecoder: NSCoder) {
		fatalError("init(coder:) has not been implemented")
	}

Swift ci obbliga a fare l’override di un secondo initializer. Di fatto, però, non lo useremo mai; quindi possiamo inserire un fatalError al suo interno. Questo ci permette di compilare il codice.

Il metodo fly()

func fly() {
		self.removeAllActions()
		let showFlyGraphics = SKAction.run { [unowned self] in
			self.texture = self.textureFlying
			self.fire.isHidden = false
		}
		let up = SKAction.moveBy(x: 0, y: 50, duration: 0.2)
		let showFallGraphics = SKAction.run {  [unowned self] in
			self.texture = self.textureFalling
			self.fire.isHidden = true
		}
		let fall = SKAction.moveTo(y: gameScene.frame.minY + self.frame.height / 2, duration: 1)
		fall.timingMode = .easeIn
		let showTextureDown = SKAction.run {  [unowned self] in
			self.texture = self.textureDown
		}
		let sequence = SKAction.sequence([showFlyGraphics, up, showFallGraphics, fall, showTextureDown])
		self.run(sequence)
	}

Questo metodo verrà invocato da GameScene quando il giocatore tocca lo schermo. Tutte le volte che si accende il razzo, il nostro Mouse riceve una spinta verso l’alto per poi ricadere. Le action in questo metodo provocano esattamente quell’animazione.

Il metodo die()

func die () {
		let showFallGraphics = SKAction.run {  [unowned self] in
			self.texture = self.textureFalling
			self.fire.isHidden = true
		}
		let fall = SKAction.moveTo(y: gameScene.frame.minY + self.frame.height / 2, duration: 0.2)
		fall.timingMode = .easeIn
		let showTextureDead = SKAction.run {  [unowned self] in
			self.texture = self.textureDown
		}
		let die = SKAction.run {  [unowned self] in
			self.gameScene.didDie()
		}
		run(SKAction.sequence([showFallGraphics, fall, showTextureDead, die]))
	}

Il metodo die() crea ed esegue l’animazione che fa precipitare Mouse verso il terreno. Il metodo verrà invocato da GameScene non appena viene rilevata una collisione con un blocco. Al termine dell’animazione, la classe GameScene esegue l’istruzione seguente:

self.gameScene.didDie()

Ciò comunica il completamento della sequenza di action. A questo punto, GameScene potrà visualizzare il testo Game Over.

Ovviamente, se proviamo a compilare il progetto adesso, otterremo un errore. Ciò è dovuto al fatto che non abbiamo ancora definito il metodo didDie. Vedremo questa parte nella prossime lezione.


Ti consigliamo anche