Este artículo describe el proceso de creación de un sencillo juego de entrenamiento de la memoria que realmente me gusta. Además de ser bueno en sí mismo, aprenderá un poco más sobre las clases y protocolos de Swift a medida que avance. Pero antes de comenzar, comprendamos el juego en sí.
Recordamos:para todos los lectores de "Habr": un descuento de 10 rublos al inscribirse en cualquier curso de Skillbox utilizando el código promocional "Habr".
El juego comienza con una demostración de un juego de cartas. Se acuestan boca abajo (respectivamente, imagen abajo). Cuando haces clic en cualquiera, la imagen se abre durante unos segundos.
La tarea del jugador es encontrar todas las cartas con las mismas imágenes. Si después de abrir la primera carta, le das la vuelta a la segunda y las imágenes coinciden, ambas cartas permanecen abiertas. Si no coinciden, las tarjetas se vuelven a cerrar. El objetivo es abrirlo todo.
Estructura del proyecto
Para crear una versión simple de este juego necesitas los siguientes componentes:
Un controlador: GameController.swift.
Una vista: CardCell.swift.
Dos modelos: MemoryGame.swift y Card.swift.
Main.storyboard para garantizar que todo el conjunto de componentes esté disponible.
Empezamos con el componente más sencillo del juego, las cartas.
Tarjeta.swift
El modelo de tarjeta tendrá tres propiedades: id para identificar cada una, una variable booleana que se muestra para especificar el estado de la tarjeta (oculta o abierta) y ArtworkURL para las imágenes de las tarjetas.
class Card {
var id: String
var shown: Bool = false
var artworkURL: UIImage!
}
También necesitará estos métodos para controlar la interacción del usuario con los mapas:
Método para mostrar una imagen en una tarjeta. Aquí restablecemos todas las propiedades a los valores predeterminados. Para la identificación, generamos una identificación aleatoria llamando a NSUUIS().uuidString.
El segundo modelo es MemoryGame, aquí configuramos una cuadrícula de 4*4. El modelo tendrá propiedades como cards (una matriz de cartas en una cuadrícula), una matriz cardsShown con cartas ya abiertas y una variable booleana isPlaying para rastrear el estado del juego.
class MemoryGame {
var cards:[Card] = [Card]()
var cardsShown:[Card] = [Card]()
var isPlaying: Bool = false
}
También necesitamos desarrollar métodos para controlar la interacción del usuario con la red.
Este método lee el último elemento de la matriz **cardsShown** y devuelve la tarjeta que no coincide.
func didSelectCard(_ card: Card?) {
guard let card = card else { return }
if unmatchedCardShown() {
let unmatched = unmatchedCard()!
if card.equals(unmatched) {
cardsShown.append(card)
} else {
let secondCard = cardsShown.removeLast()
}
} else {
cardsShown.append(card)
}
if cardsShown.count == cards.count {
endGame()
}
}
Main.storyboard y GameController.swift
Main.storyboard se parece a esto:
Inicialmente, debes configurar el nuevo juego como viewDidLoad en el controlador, incluidas las imágenes de la cuadrícula. En el juego, todo esto estará representado por 4*4 collectionView. Si aún no estás familiarizado con collectionView, aquí lo tienes puedes obtener la información que necesitas.
Configuraremos GameController como controlador raíz de la aplicación. GameController tendrá un collectionView al que nos referiremos como IBOutlet. Otra referencia es al botón IBAction onStartGame(), este es un UIButton, puedes verlo en el guión gráfico llamado PLAY.
Un poco sobre la implementación de controladores:
Primero, inicializamos dos objetos principales: la cuadrícula: juego = MemoryGame() y un conjunto de cartas: cards = [Card]().
Configuramos las variables iniciales como viewDidLoad, este es el primer método que se llama mientras se ejecuta el juego.
collectionView está configurado como oculto porque todas las tarjetas están ocultas hasta que el usuario presiona PLAY.
Tan pronto como presionamos PLAY, se inicia la sección onStartGame IBAction y configuramos la propiedad collectionView isHidden en falso para que las tarjetas puedan volverse visibles.
Cada vez que el usuario selecciona una tarjeta, se llama al método didSelectItemAt. En el método llamamos didSelectCard para implementar la lógica principal del juego.
Aquí está la implementación final de GameController:
Ahora hablemos un poco de los protocolos importantes.
Protocolos
Trabajar con protocolos es el núcleo de la programación Swift. Los protocolos brindan la capacidad de definir reglas para una clase, estructura o enumeración. Este principio le permite escribir código modular y extensible. De hecho, este es un patrón que ya estamos implementando para collectionView en GameController. Ahora hagamos nuestra propia versión. La sintaxis se verá así:
protocol MemoryGameProtocol {
//protocol definition goes here
}
Sabemos que un protocolo nos permite definir reglas o instrucciones para implementar una clase, así que pensemos en cuáles deberían ser. Necesitas cuatro en total.
Inicio del juego: MemoryGameDidStart.
Debes poner la tarjeta boca abajo: MemoryGameShowCards.
Debes poner la tarjeta boca abajo: MemoryGameHideCards.
Fin del juego: MemoryGameDidEnd.
Los cuatro métodos se pueden implementar para la clase principal, que es GameController.
memoriaGameDidStart
Cuando se ejecuta este método, el juego debería comenzar (el usuario presiona PLAY). Aquí simplemente recargaremos el contenido llamando a collectionView.reloadData(), que barajará las cartas.
Llamamos a este método desde collectionSDViewSelectItemAt. Primero muestra la tarjeta seleccionada. Luego verifica si hay una tarjeta no coincidente en la matriz cardsShown (si el número de cardsShown es impar). Si la hay, se compara la tarjeta seleccionada con ella. Si las imágenes son iguales, ambas cartas se agregan a las cartas que se muestran y permanecen boca arriba. Si es diferente, la carta deja cardsShown y ambas se ponen boca abajo.
memoriaJuegoOcultarCartas
Si las tarjetas no coinciden, se llama a este método y las imágenes de las tarjetas se ocultan.
mostrado = falso.
memoriaGameDidEnd
Cuando se llama a este método, significa que todas las cartas ya están reveladas y están en la lista cardsShown: cardsShown.count = cards.count, por lo que el juego ha terminado. El método se llama específicamente después de haber llamado a endGame() para establecer la var isPlaying en falso, después de lo cual se muestra el mensaje de finalización del juego. Además, alertController se utiliza como indicador para el controlador. Se llama a viewDidDisappear y el juego se reinicia.
Así es como se ve todo en GameController:
extension GameController: MemoryGameProtocol {
func memoryGameDidStart(_ game: MemoryGame) {
collectionView.reloadData()
}
func memoryGame(_ game: MemoryGame, showCards cards: [Card]) {
for card in cards {
guard let index = game.indexForCard(card)
else { continue
}
let cell = collectionView.cellForItem(
at: IndexPath(item: index, section:0)
) as! CardCell
cell.showCard(true, animted: true)
}
}
func memoryGame(_ game: MemoryGame, hideCards cards: [Card]) {
for card in cards {
guard let index = game.indexForCard(card)
else { continue
}
let cell = collectionView.cellForItem(
at: IndexPath(item: index, section:0)
) as! CardCell
cell.showCard(false, animted: true)
}
}
func memoryGameDidEnd(_ game: MemoryGame) {
let alertController = UIAlertController(
title: defaultAlertTitle,
message: defaultAlertMessage,
preferredStyle: .alert
)
let cancelAction = UIAlertAction(
title: "Nah", style: .cancel) {
[weak self] (action) in
self?.collectionView.isHidden = true
}
let playAgainAction = UIAlertAction(
title: "Dale!", style: .default) {
[weak self] (action) in
self?.collectionView.isHidden = true
self?.resetGame()
}
alertController.addAction(cancelAction)
alertController.addAction(playAgainAction)
self.present(alertController, animated: true) { }
resetGame()
}
}
En realidad, eso es todo. Puedes utilizar este proyecto para crear tu propia versión del juego.