
Este artigo descreve o processo de criação de um jogo simples de treinamento de memória que eu realmente gosto. Além de ser bom por si só, você aprenderá um pouco mais sobre as classes e protocolos Swift durante o trabalho. Mas antes de começar, vamos descobrir o jogo em si.
Lembramos que: para todos os leitores de "Habr" - um desconto de 10.000 rublos ao se inscrever em qualquer curso Skillbox usando o código promocional "Habr".
A Skillbox recomenda: O curso educacional on-line "Profissão Java-developer" .
Como jogar Cartão de Memória
O jogo começa com uma demonstração de um conjunto de cartas. Eles ficam de “camisa” para cima (respectivamente, voltados para baixo). Quando você clica em qualquer, uma imagem é aberta por alguns segundos.
A tarefa do jogador é encontrar todos os cartões com as mesmas fotos. Se, depois de abrir o primeiro cartão, você virar o segundo cartão e as imagens coincidirem, os dois cartões permanecerão abertos. Se não corresponderem, os cartões serão fechados novamente. A tarefa é abrir tudo.
Estrutura do projeto
Para criar uma versão simples deste jogo, você precisa dos seguintes componentes:
- Um controlador: GameController.swift.
- Uma visão: CardCell.swift.
- Dois modelos: MemoryGame.swift e Card.swift.
- Main.storyboard para que todo o conjunto de componentes esteja disponível.
Começamos com o componente mais simples do jogo, o cartão.
Card.swiftO modelo do cartão terá três propriedades: id para identificar cada uma, uma variável lógica mostrada para esclarecer o status do cartão (oculto ou aberto) e o artworkURL para fotos nos cartões.
class Card { var id: String var shown: Bool = false var artworkURL: UIImage! }
Você também precisará destes métodos para controlar a interação do usuário com os cartões:
Método para exibir uma imagem em um cartão. Aqui redefinimos todas as propriedades para o padrão. Para identificação, gere uma identificação aleatória chamando NSUUIS (). UuidString.
init(image: UIImage) { self.id = NSUUID().uuidString self.shown = false self.artworkURL = image }
Método para comparar cartões de identificação. func equals(_ card: Card) -> Bool { return (card.id == id) }
O método para criar uma cópia de cada cartão é obter um número maior de
cartões idênticos. Este método retornará o cartão com valores semelhantes.
func copy() -> Card { return Card(card: self) } init(card: Card) { self.id = card.id self.shown = card.shown self.artworkURL = card.artworkURL }
E é necessário outro método para misturar as cartas no início. Vamos torná-lo uma extensão da classe Array.
extension Array { mutating func shuffle() { for _ in 0...self.count { sort { (_,_) in arc4random() < arc4random() } } } }
E aqui está a implementação de código para o modelo de cartão com todas as propriedades e métodos.
class Card { var id: String var shown: Bool = false var artworkURL: UIImage! static var allCards = [Card]() init(card: Card) { self.id = card.id self.shown = card.shown self.artworkURL = card.artworkURL } init(image: UIImage) { self.id = NSUUID().uuidString self.shown = false self.artworkURL = image } func equals(_ card: Card) -> Bool { return (card.id == id) } func copy() -> Card { return Card(card: self) } } extension Array { mutating func shuffle() { for _ in 0...self.count { sort { (_,_) in arc4random() < arc4random() } } } }
Vá em frente.
O segundo modelo é MemoryGame, aqui configuramos a grade 4 * 4. O modelo terá propriedades como cartões (uma matriz de cartas na grade), uma matriz cardsShown com cartas já abertas e o booleano isPlaying para rastrear o status do jogo.
class MemoryGame { var cards:[Card] = [Card]() var cardsShown:[Card] = [Card]() var isPlaying: Bool = false }
Também precisamos desenvolver métodos para controlar a interação do usuário com a grade.
Um método que embaralha as cartas em uma grade. func shuffleCards(cards:[Card]) -> [Card] { var randomCards = cards randomCards.shuffle() return randomCards }
Método para criar um novo jogo. Aqui chamamos o primeiro método para iniciar o layout inicial e inicializar a variável isPlaying como true.
func newGame(cardsArray:[Card]) -> [Card] { cards = shuffleCards(cards: cardsArray) isPlaying = true return cards }
Se queremos reiniciar o jogo, defina a variável isPlaying como false e remova o layout inicial dos cartões.
func restartGame() { isPlaying = false cards.removeAll() cardsShown.removeAll() }
Método para verificação de cartões prensados. Mais sobre isso mais tarde.
func cardAtIndex(_ index: Int) -> Card? { if cards.count > index { return cards[index] } else { return nil } }
Um método que retorna a posição de um cartão específico. func indexForCard(_ card: Card) -> Int? { for index in 0...cards.count-1 { if card === cards[index] { return index } } return nil }
Verificar a conformidade do cartão selecionado com o padrão
func unmatchedCardShown() -> Bool { return cardsShown.count % 2 != 0 }
Este método lê o último elemento na matriz ** cardsShown ** e retorna um cartão inapropriado.
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 e GameController.swift
Main.storyboard é algo parecido com isto:

Inicialmente, no controlador, você precisa instalar um novo jogo como viewDidLoad, incluindo imagens para a grade. No jogo, tudo isso será representado por 4 * 4 collectionView. Se você não estiver familiarizado com o collectionView, aqui
poderá obter as informações necessárias .
Vamos configurar o GameController como o controlador raiz do aplicativo. Haverá um collectionView no GameController, que iremos referenciar como um IBOutlet. Outro link é para o botão IBAction onStartGame (), este é o UIButton, você pode vê-lo no storyboard chamado PLAY.
Um pouco sobre a implementação de controladores:
- Primeiro, inicializamos os dois objetos principais - a grade (jogo): game = MemoryGame (), e no conjunto de cartas: cards = [Card] ().
- Defina as variáveis iniciais como viewDidLoad, este é o primeiro método chamado durante o jogo.
- defina collectionView como oculto, pois todos os mapas ficam ocultos até o usuário pressionar PLAY.
- Assim que pressionamos PLAY, a seção onStartGame IBAction é iniciada e definimos a propriedade collectionView isHidden como false para que os cartões possam ficar visíveis.
- Sempre que um usuário seleciona um cartão, o método didSelectItemAt é chamado. No método, chamamos didSelectCard para implementar a lógica básica do jogo.
Aqui está a implementação final do GameController:
class GameController: UIViewController { @IBOutlet weak var collectionView: UICollectionView! let game = MemoryGame() var cards = [Card]() override func viewDidLoad() { super.viewDidLoad() game.delegate = self collectionView.dataSource = self collectionView.delegate = self collectionView.isHidden = true APIClient.shared.getCardImages { (cardsArray, error) in if let _ = error {
Agora vamos nos debruçar sobre alguns protocolos importantes.
Protocolos
Trabalhar com protocolos é a base da programação Swift. Os protocolos fornecem a capacidade de definir regras para uma classe, estrutura ou enumeração. Este princípio permite escrever código modular e extensível. Na verdade, este é um modelo que já estamos implementando para collectionView no GameController. Agora vamos criar nossa própria versão. A sintaxe ficará assim:
protocol MemoryGameProtocol {
Sabemos que um protocolo permite que você defina regras ou instruções para implementar uma classe, então vamos pensar sobre o que elas devem ser. Apenas quatro são necessários.
- Início do jogo: memoryGameDidStart.
- Precisa virar o cartão com a face para baixo: memoryGameShowCards.
- Precisa virar o cartão com a face para baixo: memoryGameHideCards.
- Conclusão do jogo: memoryGameDidEnd.
Implementamos todos os quatro métodos para a classe principal, e este é o GameController.
memoryGameDidStart
Quando esse método é iniciado, o jogo deve começar (o usuário pressiona PLAY). Aqui, simplesmente recarregamos o conteúdo chamando collectionView.reloadData (), o que fará com que os mapas sejam embaralhados.
func memoryGameDidStart(_ game: MemoryGame) { collectionView.reloadData() }
memoryGameShowCards
Chame esse método de collectionSDViewSelectItemAt. Primeiro, mostra o mapa selecionado. Em seguida, verifica se há um cartão incomparável na matriz cardsShown (se o número de cardsShown for ímpar). Se houver um, o cartão selecionado é comparado a ele. Se as fotos forem iguais, os dois cartões serão adicionados ao cardsShown e permanecerão abertos. Se diferente, o cartão deixa cardsShown e os dois viram de cabeça para baixo.
memoryGameHideCards
Se os cartões não corresponderem, esse método será chamado e as imagens do cartão serão ocultadas.
mostrado = falso.
memoryGameDidEnd
Quando esse método é chamado, significa que todas as cartas já estão abertas e estão na lista cardsShown: cardsShown.count = cards.count, para que o jogo termine. O método é chamado especificamente depois que chamamos endGame () para definir isPlaying var como false, após o qual é exibida uma mensagem sobre a conclusão do jogo. O AlertController também é usado como um indicador para o controlador. O ViewDidDisappear é chamado e o jogo é reiniciado.
Veja como tudo fica no 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() } }

Só isso. Você pode usar este projeto para criar sua própria versão do jogo.
Boa codificação!
A Skillbox recomenda: