Dit artikel beschrijft het proces van het maken van een eenvoudig geheugentrainingspel waar ik erg van geniet. Behalve dat het op zichzelf goed is, leer je gaandeweg wat meer over Swift-klassen en -protocollen. Maar voordat we beginnen, laten we het spel zelf begrijpen.
Herinnering:voor alle lezers van "Habr" - een korting van 10 roebel bij inschrijving voor een Skillbox-cursus met behulp van de promotiecode "Habr".
Het spel begint met een demonstratie van een set kaarten. Ze liggen met de voorkant naar beneden (respectievelijk afbeelding naar beneden). Wanneer u op iemand klikt, wordt de afbeelding enkele seconden geopend.
De taak van de speler is om alle kaarten met dezelfde afbeeldingen te vinden. Als je na het openen van de eerste kaart de tweede omdraait en de afbeeldingen overeenkomen, blijven beide kaarten open. Als ze niet overeenkomen, worden de kaarten weer gesloten. Het doel is om alles te openen.
Projectstructuur
Om een eenvoudige versie van dit spel te maken, heb je de volgende componenten nodig:
Eén controller: GameController.swift.
Eén weergave: CardCell.swift.
Twee modellen: MemoryGame.swift en Card.swift.
Main.storyboard om ervoor te zorgen dat de volledige set componenten beschikbaar is.
We beginnen met het eenvoudigste onderdeel van het spel: de kaarten.
Kaart.snel
Het kaartmodel heeft drie eigenschappen: id om ze allemaal te identificeren, een Booleaanse variabele die wordt weergegeven om de status van de kaart (verborgen of open) te specificeren, en artworkURL voor de afbeeldingen op de kaarten.
class Card {
var id: String
var shown: Bool = false
var artworkURL: UIImage!
}
U hebt deze methoden ook nodig om de gebruikersinteractie met kaarten te beheren:
Methode voor het weergeven van een afbeelding op een kaart. Hier zetten we alle eigenschappen terug naar de standaardwaarden. Voor ID genereren we een willekeurige ID door NSUUIS().uuidString aan te roepen.
Methode om van elke kaart een kopie te maken - om een groter aantal identieke exemplaren te krijgen. Deze methode retourneert een kaart met vergelijkbare waarden.
Het tweede model is MemoryGame, hier stellen we een 4*4-raster in. Het model heeft eigenschappen zoals kaarten (een reeks kaarten op een raster), een array cardsShown met kaarten die al open zijn, en een Booleaanse variabele isPlaying om de status van het spel bij te houden.
class MemoryGame {
var cards:[Card] = [Card]()
var cardsShown:[Card] = [Card]()
var isPlaying: Bool = false
}
We moeten ook methoden ontwikkelen om de interactie van gebruikers met het elektriciteitsnet te controleren.
Een methode waarbij kaarten in een raster worden geschud.
Methode voor het maken van een nieuw spel. Hier noemen we de eerste methode om de initiële lay-out te starten en de isPlaying-variabele te initialiseren naar true.
Als we het spel opnieuw willen starten, vervolgens stellen we de isPlaying-variabele in op false en verwijderen we de oorspronkelijke lay-out van de kaarten.
Deze methode leest het laatste element in de array **cardsShown** en retourneert de niet-overeenkomende kaart.
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 en GameController.swift
Main.storyboard ziet er ongeveer zo uit:
In eerste instantie moet je het nieuwe spel instellen als viewDidLoad in de controller, inclusief de afbeeldingen voor het raster. In het spel wordt dit allemaal weergegeven door 4*4 collectionView. Als u nog niet bekend bent met collectionView, hier is het dan u kunt de informatie krijgen die u nodig heeft.
We zullen de GameController configureren als de rootcontroller van de applicatie. De GameController zal een collectionView hebben waarnaar we zullen verwijzen als een IBOutlet. Een andere verwijzing is naar de IBAction onStartGame()-knop, dit is een UIButton, je kunt deze zien in het storyboard genaamd PLAY.
Iets over de implementatie van controllers:
Eerst initialiseren we twee hoofdobjecten: het raster: game = MemoryGame(), en een set kaarten: kaarten = [Card]().
We stellen de initiële variabelen in als viewDidLoad, dit is de eerste methode die wordt aangeroepen terwijl het spel draait.
collectionView is ingesteld op verborgen omdat alle kaarten verborgen zijn totdat de gebruiker op PLAY drukt.
Zodra we op PLAY drukken, start de onStartGame IBAction-sectie en stellen we de eigenschap collectionView isHidden in op false, zodat de kaarten zichtbaar kunnen worden.
Elke keer dat de gebruiker een kaart selecteert, wordt de methode didSelectItemAt aangeroepen. In de methode die we didSelectCard noemen, wordt de hoofdspellogica geïmplementeerd.
Hier is de uiteindelijke GameController-implementatie:
Laten we het nu even hebben over de belangrijke protocollen.
Protocollen
Het werken met protocollen is de kern van Swift-programmeren. Protocollen bieden de mogelijkheid om regels te definiëren voor een klasse, structuur of opsomming. Met dit principe kunt u modulaire en uitbreidbare code schrijven. In feite is dit een patroon dat we al implementeren voor collectionView in GameController. Laten we nu onze eigen versie maken. De syntaxis ziet er als volgt uit:
protocol MemoryGameProtocol {
//protocol definition goes here
}
We weten dat we met een protocol regels of instructies kunnen definiëren voor het implementeren van een klasse, dus laten we eens nadenken over wat die zouden moeten zijn. Je hebt er in totaal vier nodig.
Begin van het spel: memoryGameDidStart.
Je moet de kaart met de afbeelding naar beneden omdraaien: memoryGameShowCards.
Je moet de kaart met de afbeelding naar beneden omdraaien: memoryGameHideCards.
Einde van het spel: memoryGameDidEnd.
Alle vier de methoden kunnen worden geïmplementeerd voor de hoofdklasse, namelijk GameController.
geheugenGameDidStart
Wanneer deze methode wordt uitgevoerd, zou het spel moeten starten (de gebruiker drukt op PLAY). Hier laden we eenvoudigweg de inhoud opnieuw door collectionView.reloadData() aan te roepen, waardoor de kaarten worden geschud.
We noemen deze methode vanuit collectionSDViewSelectItemAt. Eerst wordt de geselecteerde kaart weergegeven. Vervolgens wordt gecontroleerd of er een niet-overeenkomende kaart in de array cardsShown zit (als het aantal cardsShown oneven is). Als die er is, wordt de geselecteerde kaart ermee vergeleken. Als de afbeeldingen hetzelfde zijn, worden beide kaarten toegevoegd aan cardsShown en blijven ze open. Als ze verschillend zijn, laat de kaart kaarten getoond en worden beide met de beeldzijde naar beneden omgedraaid.
geheugenSpelVerbergkaarten
Als de kaarten niet overeenkomen, wordt deze methode aangeroepen en worden de kaartafbeeldingen verborgen.
getoond = onwaar.
geheugenGameDidEnd
Wanneer deze methode wordt aangeroepen, betekent dit dat alle kaarten al zijn onthuld en in de cardsShown-lijst staan: cardsShown.count = cards.count, dus het spel is afgelopen. De methode wordt specifiek aangeroepen nadat we endGame() hebben aangeroepen om de isPlaying var op false in te stellen, waarna het bericht over het einde van het spel wordt weergegeven. Ook alertController wordt gebruikt als indicator voor de controller. viewDidDisappear wordt aangeroepen en het spel wordt gereset.
Zo ziet het er allemaal uit in 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()
}
}
Eigenlijk is dat alles. Je kunt dit project gebruiken om je eigen versie van het spel te maken.