Pisanje igre Memory Cards v Swiftu

Pisanje igre Memory Cards v Swiftu

Ta članek opisuje postopek ustvarjanja preproste igre za urjenje spomina, v kateri resnično uživam. Poleg tega, da je sam po sebi dober, se boste sproti naučili nekaj več o razredih in protokolih Swift. Toda preden začnemo, razumejmo samo igro.

Spomnimo: za vse bralce "Habr" - popust v višini 10 rubljev ob vpisu v kateri koli tečaj Skillbox s promocijsko kodo "Habr".

Skillbox priporoča: Izobraževalni spletni tečaj "Poklic Java razvijalec".

Kako igrati Memory Card

Igra se začne s predstavitvijo kompleta kart. Ležijo z obrazom navzdol (oziroma s sliko navzdol). Ko kliknete katero koli, se slika odpre za nekaj sekund.

Igralčeva naloga je najti vse karte z enakimi slikami. Če po odprtju prve karte obrnete drugo in se sliki ujemata, ostaneta obe karti odprti. Če se ne ujemajo, se karte ponovno zaprejo. Cilj je odpreti vse.

Struktura projekta

Za ustvarjanje preproste različice te igre potrebujete naslednje komponente:

  • En krmilnik: GameController.swift.
  • En pogled: CardCell.swift.
  • Dva modela: MemoryGame.swift in Card.swift.
  • Main.storyboard, da zagotovite, da je na voljo celoten nabor komponent.

Začnemo z najpreprostejšim delom igre, kartami.

Card.swift

Model kartice bo imel tri lastnosti: ID za identifikacijo vsake, logično spremenljivko, prikazano za določitev statusa kartice (skrita ali odprta), in artworkURL za slike na karticah.

class Card {        
    var id: String    
    var shown: Bool = false    
    var artworkURL: UIImage!
}

Te metode boste potrebovali tudi za nadzor interakcije uporabnika z zemljevidi:

Metoda za prikaz slike na kartici. Tukaj ponastavimo vse lastnosti na privzete. Za id ustvarimo naključni id s klicem NSUUIS().uuidString.

init(image: UIImage) {        
    self.id = NSUUID().uuidString        
    self.shown = false        
    self.artworkURL = image    
}

Metoda primerjave osebnih izkaznic.

func equals(_ card: Card) -> Bool {
    return (card.id == id)    
}

Metoda za ustvarjanje kopije vsake kartice - da bi dobili večje število enakih. Ta metoda bo vrnila kartico s podobnimi vrednostmi.

func copy() -> Card {        
    return Card(card: self)    
}
 
init(card: Card) {        
    self.id = card.id        
    self.shown = card.shown        
    self.artworkURL = card.artworkURL    
}

In še ena metoda je potrebna za mešanje kart na začetku. Naredili ga bomo kot razširitev razreda Array.

extension Array {    
    mutating func shuffle() {        
        for _ in 0...self.count {            
            sort { (_,_) in arc4random() < arc4random() }        
        }   
    }
}

In tukaj je implementacija kode za model Card z vsemi lastnostmi in metodami.

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() }
        }
    }
}

Pojdi naprej.

Drugi model je MemoryGame, tukaj smo postavili mrežo 4*4. Model bo imel lastnosti, kot so kartice (matrika kart na mreži), matrika cardsShown z že odprtimi kartami in logična spremenljivka isPlaying za sledenje statusu igre.

class MemoryGame {        
    var cards:[Card] = [Card]()    
    var cardsShown:[Card] = [Card]()    
    var isPlaying: Bool = false
}

Prav tako moramo razviti metode za nadzor interakcije uporabnikov z omrežjem.

Metoda, ki meša karte v mrežo.

func shuffleCards(cards:[Card]) -> [Card] {       
    var randomCards = cards        
    randomCards.shuffle()                
 
    return randomCards    
}

Metoda za ustvarjanje nove igre. Tukaj pokličemo prvo metodo za zagon začetne postavitve in inicializacijo spremenljivke isPlaying na true.

func newGame(cardsArray:[Card]) -> [Card] {       
    cards = shuffleCards(cards: cardsArray)        
    isPlaying = true            
 
    return cards    
}

Če želimo znova zagnati igro, potem nastavimo spremenljivko isPlaying na false in odstranimo začetno postavitev kart.

func restartGame() {        
    isPlaying = false                
    cards.removeAll()        
    cardsShown.removeAll()    
}

Metoda za preverjanje klikanih kartic. Več o njem kasneje.

func cardAtIndex(_ index: Int) -> Card? {        
    if cards.count > index {            
        return cards[index]        
    } else {            
        return nil        
    }    
}

Metoda, ki vrne položaj določene kartice.

func indexForCard(_ card: Card) -> Int? {        
    for index in 0...cards.count-1 {            
        if card === cards[index] {                
            return index            
        }      
    }
        
    return nil    
}

Preverjanje skladnosti izbrane kartice s standardom.

func unmatchedCardShown() -> Bool {
    return cardsShown.count % 2 != 0
}

Ta metoda prebere zadnji element v matriki **cardsShown** in vrne neujemajočo se kartico.

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 in GameController.swift

Main.storyboard izgleda nekako takole:

Pisanje igre Memory Cards v Swiftu

Na začetku morate novo igro nastaviti kot viewDidLoad v krmilniku, vključno s slikami za mrežo. V igri bo vse to predstavljal 4*4 collectionView. Če še niste seznanjeni s CollectionView, je tukaj lahko dobite informacije, ki jih potrebujete.

GameController bomo konfigurirali kot korenski krmilnik aplikacije. GameController bo imel collectionView, ki ga bomo sklicevali kot IBOutlet. Druga referenca je gumb IBAction onStartGame(), to je UIButton, lahko ga vidite v snemalni knjigi z imenom PLAY.

Nekaj ​​o izvedbi krmilnikov:

  • Najprej inicializiramo dva glavna objekta - mrežo: game = MemoryGame() in niz kart: cards = [Card]().
  • Začetne spremenljivke smo nastavili kot viewDidLoad, to je prva metoda, ki se kliče, ko se igra izvaja.
  • collectionView je nastavljen na skrito, ker so vse kartice skrite, dokler uporabnik ne pritisne PLAY.
  • Takoj ko pritisnemo PLAY, se zažene razdelek onStartGame IBAction, lastnost collectionView isHidden pa nastavimo na false, tako da lahko kartice postanejo vidne.
  • Vsakič, ko uporabnik izbere kartico, se pokliče metoda didSelectItemAt. V metodi, ki jo pokličemo didSelectCard za implementacijo glavne logike igre.

Tukaj je končna implementacija GameControllerja:

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 {
                // show alert
            }
            
            self.cards = cardsArray!
            self.setupNewGame()
        }
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        
        if game.isPlaying {
            resetGame()
        }
    }
    
    func setupNewGame() {
        cards = game.newGame(cardsArray: self.cards)
        collectionView.reloadData()
    }
    
    func resetGame() {
        game.restartGame()
        setupNewGame()
    }
    
    @IBAction func onStartGame(_ sender: Any) {
        collectionView.isHidden = false
    }
}
 
// MARK: - CollectionView Delegate Methods
extension GameController: UICollectionViewDelegate, UICollectionViewDataSource {
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return cards.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CardCell", for: indexPath) as! CardCell
        cell.showCard(false, animted: false)
        
        guard let card = game.cardAtIndex(indexPath.item) else { return cell }
        cell.card = card
        
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let cell = collectionView.cellForItem(at: indexPath) as! CardCell
        
        if cell.shown { return }
        game.didSelectCard(cell.card)
        
        collectionView.deselectItem(at: indexPath, animated:true)
    }
}

Zdaj pa se pogovorimo malo o pomembnih protokolih.

Protokoli

Delo s protokoli je jedro programiranja Swift. Protokoli nudijo možnost definiranja pravil za razred, strukturo ali oštevilčenje. To načelo vam omogoča pisanje modularne in razširljive kode. Pravzaprav je to vzorec, ki ga že izvajamo za collectionView v GameControllerju. Zdaj pa naredimo svojo različico. Sintaksa bo videti takole:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

Vemo, da nam protokol omogoča definiranje pravil ali navodil za implementacijo razreda, zato pomislimo, kakšna bi morala biti. Skupaj potrebujete štiri.

  • Začetek igre: memoryGameDidStart.
  • Kartico morate obrniti z licem navzdol: memoryGameShowCards.
  • Kartico morate obrniti z licem navzdol: memoryGameHideCards.
  • Konec igre: memoryGameDidEnd.

Vse štiri metode je mogoče implementirati za glavni razred, ki je GameController.

memoryGameDidStart

Ko je ta metoda zagnana, se mora začeti igra (uporabnik pritisne PLAY). Tukaj bomo preprosto znova naložili vsebino s klicem collectionView.reloadData(), ki bo premešal karte.

func memoryGameDidStart(_ game: MemoryGame) {
    collectionView.reloadData()
}

memoryGameShowCards

To metodo kličemo iz zbirkeSDViewSelectItemAt. Najprej prikaže izbrano kartico. Nato preveri, ali je v matriki cardsShown karta, ki se ne ujema (če je število cardsShown liho). Če obstaja, se izbrana karta primerja z njo. Če sta sliki enaki, sta obe kartici dodani na cardsShown in ostaneta obrnjeni navzgor. Če je drugačna, karta ostane prikazana in obe sta obrnjeni z licem navzdol.

memoryGameHideCards

Če se karti ne ujemata, se ta metoda pokliče in slike kartic se skrijejo.

prikazano = napačno.

memoryGameDidEnd

Ko je ta metoda poklicana, to pomeni, da so vse karte že razkrite in so na seznamu cardsShown: cardsShown.count = cards.count, torej je igre konec. Metoda je poklicana posebej potem, ko smo poklicali endGame(), da nastavimo isPlaying var na false, po tem pa se prikaže sporočilo o koncu igre. AlertController se uporablja tudi kot indikator za krmilnik. viewDidDisappear se pokliče in igra se ponastavi.

Tako je videti vse skupaj v GameControllerju:

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()
    }
}

Pisanje igre Memory Cards v Swiftu
Pravzaprav je to vse. Ta projekt lahko uporabite za ustvarjanje lastne različice igre.

Veselo kodiranje!

Skillbox priporoča:

Vir: www.habr.com

Dodaj komentar