
Dieser Artikel beschreibt den Prozess der Erstellung eines einfachen Gedächtnistrainingsspiels, das mir wirklich gefällt. Abgesehen davon, dass Sie an und für sich gut sind, lernen Sie während der Arbeit ein bisschen mehr über Swift-Klassen und -Protokolle. Aber bevor Sie anfangen, lassen Sie uns das Spiel selbst herausfinden.
Wir erinnern Sie daran: Für alle Leser von „Habr“ - ein Rabatt von 10.000 Rubel bei der Anmeldung für einen Skillbox-Kurs mit dem Promo-Code „Habr“.
Skillbox empfiehlt: Der Online-Schulungskurs "Profession Java-Entwickler" .
Wie man eine Speicherkarte spielt
Das Spiel beginnt mit einem Kartensatz. Sie liegen "Hemd" nach oben (bzw. mit dem Gesicht nach unten). Wenn Sie auf eines klicken, wird ein Bild für einige Sekunden geöffnet.
Die Aufgabe des Spielers besteht darin, alle Karten mit denselben Bildern zu finden. Wenn Sie nach dem Öffnen der ersten Karte die zweite umdrehen und die Bilder übereinstimmen, bleiben beide Karten offen. Wenn sie nicht übereinstimmen, werden die Karten wieder geschlossen. Die Aufgabe ist es, alles zu öffnen.
Projektstruktur
Um eine einfache Version dieses Spiels zu erstellen, benötigen Sie die folgenden Komponenten:
- Ein Controller: GameController.swift.
- Eine Ansicht: CardCell.swift.
- Zwei Modelle: MemoryGame.swift und Card.swift.
- Main.storyboard, damit der gesamte Komponentensatz verfügbar ist.
Wir beginnen mit der einfachsten Komponente des Spiels, der Karte.
Card.swiftDas Kartenmodell verfügt über drei Eigenschaften: ID zur Identifizierung, eine logische Variable zur Verdeutlichung des Status der Karte (versteckt oder offen) und ArtworkURL für Bilder auf den Karten.
class Card { var id: String var shown: Bool = false var artworkURL: UIImage! }
Sie benötigen außerdem die folgenden Methoden, um die Benutzerinteraktion mit Karten zu steuern:
Methode zum Anzeigen eines Bildes auf einer Karte. Hier setzen wir alle Eigenschaften auf den Standard zurück. Generieren Sie für id eine zufällige ID, indem Sie NSUUIS (). UuidString aufrufen.
init(image: UIImage) { self.id = NSUUID().uuidString self.shown = false self.artworkURL = image }
Methode zum Vergleichen von ID-Karten. func equals(_ card: Card) -> Bool { return (card.id == id) }
Die Methode zum Erstellen einer Kopie jeder Karte besteht darin, eine größere Anzahl identischer
Karten zu erhalten. Diese Methode gibt eine Karte mit ähnlichen Werten zurück.
func copy() -> Card { return Card(card: self) } init(card: Card) { self.id = card.id self.shown = card.shown self.artworkURL = card.artworkURL }
Und eine andere Methode ist erforderlich, um Karten zu Beginn zu mischen. Wir werden es zu einer Erweiterung der Array-Klasse machen.
extension Array { mutating func shuffle() { for _ in 0...self.count { sort { (_,_) in arc4random() < arc4random() } } } }
Und hier ist die Code-Implementierung für das Kartenmodell mit allen Eigenschaften und Methoden.
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() } } } }
Mach weiter.
Das zweite Modell ist MemoryGame, hier setzen wir das 4 * 4-Raster. Das Modell verfügt über Eigenschaften wie Karten (ein Array von Karten im Raster), ein CardsShown-Array mit bereits geöffneten Karten und den Booleschen Wert isPlaying, um den Status des Spiels zu verfolgen.
class MemoryGame { var cards:[Card] = [Card]() var cardsShown:[Card] = [Card]() var isPlaying: Bool = false }
Wir müssen auch Methoden entwickeln, um die Benutzerinteraktion mit dem Grid zu steuern.
Eine Methode, mit der Karten in einem Raster gemischt werden. func shuffleCards(cards:[Card]) -> [Card] { var randomCards = cards randomCards.shuffle() return randomCards }
Methode zum Erstellen eines neuen Spiels. Hier rufen wir die erste Methode auf, um das anfängliche Layout zu starten und die Variable isPlaying als true zu initialisieren.
func newGame(cardsArray:[Card]) -> [Card] { cards = shuffleCards(cards: cardsArray) isPlaying = true return cards }
Wenn wir das Spiel neu starten möchten, setzen Sie die Variable isPlaying auf false und entfernen Sie das ursprüngliche Layout der Karten.
func restartGame() { isPlaying = false cards.removeAll() cardsShown.removeAll() }
Methode zur Überprüfung von gepressten Karten. Dazu später mehr.
func cardAtIndex(_ index: Int) -> Card? { if cards.count > index { return cards[index] } else { return nil } }
Eine Methode, die die Position einer bestimmten Karte zurückgibt. func indexForCard(_ card: Card) -> Int? { for index in 0...cards.count-1 { if card === cards[index] { return index } } return nil }
Überprüfen der Übereinstimmung der ausgewählten Karte mit dem Standard.
func unmatchedCardShown() -> Bool { return cardsShown.count % 2 != 0 }
Diese Methode liest das letzte Element im Array ** cardShown ** und gibt eine unangemessene Karte zurück.
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 und GameController.swift
Main.storyboard sieht ungefähr so aus:

Zunächst müssen Sie im Controller ein neues Spiel als viewDidLoad installieren, einschließlich Bilder für das Raster. Im Spiel wird dies alles durch 4 * 4 collectionView dargestellt. Wenn Sie mit collectionView nicht vertraut sind, erhalten
Sie hier
die erforderlichen Informationen .
Wir werden GameController als Root-Controller der Anwendung konfigurieren. In GameController wird es eine collectionView geben, die wir als IBOutlet bezeichnen werden. Ein weiterer Link führt zur Schaltfläche IBAction onStartGame (). Dies ist UIButton. Sie können ihn im Storyboard mit dem Namen PLAY sehen.
Ein bisschen über die Implementierung von Controllern:
- Zuerst initialisieren wir die beiden Hauptobjekte - das Gitter (Spiel): Spiel = MemoryGame () und auf dem Kartensatz: Karten = [Karte] ().
- Legen Sie die Anfangsvariablen als viewDidLoad fest. Dies ist die erste Methode, die während des Spiels aufgerufen wird.
- Setzen Sie collectionView als ausgeblendet, da alle Karten ausgeblendet sind, bis der Benutzer PLAY drückt.
- Sobald wir PLAY drücken, wird der Abschnitt onStartGame IBAction gestartet und die Eigenschaft collectionView isHidden auf false gesetzt, damit die Karten sichtbar werden.
- Jedes Mal, wenn ein Benutzer eine Karte auswählt, wird die didSelectItemAt-Methode aufgerufen. In der Methode rufen wir didSelectCard auf, um die grundlegende Logik des Spiels zu implementieren.
Hier ist die endgültige Implementierung von 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 {
Kommen wir nun zu einigen wichtigen Protokollen.
Protokolle
Die Arbeit mit Protokollen ist die Grundlage der Swift-Programmierung. Protokolle bieten die Möglichkeit, Regeln für eine Klasse, Struktur oder Aufzählung festzulegen. Mit diesem Prinzip können Sie modularen und erweiterbaren Code schreiben. Dies ist eine Vorlage, die wir bereits für collectionView in GameController implementieren. Jetzt machen wir unsere eigene Version. Die Syntax sieht folgendermaßen aus:
protocol MemoryGameProtocol {
Wir wissen, dass Sie mit einem Protokoll Regeln oder Anweisungen für die Implementierung einer Klasse definieren können. Überlegen wir uns also, wie diese aussehen sollen. Es werden nur vier benötigt.
- Spielstart: memoryGameDidStart.
- Die Karte muss verdeckt umgedreht werden: memoryGameShowCards.
- Die Karte muss verdeckt umgedreht werden: memoryGameHideCards.
- Spielabschluss: memoryGameDidEnd.
Wir implementieren alle vier Methoden für die Hauptklasse, und dies ist GameController.
memoryGameDidStart
Wenn diese Methode gestartet wird, sollte das Spiel beginnen (Benutzer drückt PLAY). Hier laden wir den Inhalt einfach neu, indem wir collectionView.reloadData () aufrufen, wodurch die Karten gemischt werden.
func memoryGameDidStart(_ game: MemoryGame) { collectionView.reloadData() }
memoryGameShowCards
Rufen Sie diese Methode von collectionSDViewSelectItemAt auf. Zunächst wird die ausgewählte Karte angezeigt. Anschließend wird überprüft, ob das Feld "Karten angezeigt" eine nicht übereinstimmende Karte enthält (wenn die Anzahl der angezeigten Karten ungerade ist). Wenn es eine gibt, wird die ausgewählte Karte damit verglichen. Wenn die Bilder gleich sind, werden beide Karten zu den gezeigten Karten hinzugefügt und bleiben offen. Wenn anders, verlässt die Karte die angezeigten Karten und beide drehen sich auf den Kopf.
memoryGameHideCards
Wenn die Karten nicht übereinstimmen, wird diese Methode aufgerufen und die Kartenbilder werden ausgeblendet.
gezeigt = falsch.
memoryGameDidEnd
Wenn diese Methode aufgerufen wird, bedeutet dies, dass alle Karten bereits geöffnet sind und sich in der Liste "Karten zeigen" befinden: cardShown.count = maps.count, sodass das Spiel beendet ist. Die Methode wird speziell aufgerufen, nachdem wir endGame () aufgerufen haben, um isPlaying var auf false zu setzen. Danach wird eine Meldung über den Abschluss des Spiels angezeigt. AlertController wird auch als Indikator für den Controller verwendet. ViewDidDisappear wird aufgerufen und das Spiel zurückgesetzt.
So sieht alles in GameController aus:
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() } }

Das ist alles Mit diesem Projekt können Sie Ihre eigene Version des Spiels erstellen.
Gute Codierung!
Skillbox empfiehlt: