Muistikorttipelin kirjoittaminen Swiftissä

Muistikorttipelin kirjoittaminen Swiftissä

Tässä artikkelissa kuvataan prosessi, jolla luodaan yksinkertainen muistiharjoituspeli, josta pidän todella. Sen lisäksi, että olet hyvä sinänsä, opit hieman enemmän Swift-luokista ja protokollista. Mutta ennen kuin aloitamme, ymmärrämme itse pelin.

Muistutamme sinua: kaikille "Habrin" lukijoille - 10 000 ruplan alennus ilmoittautuessaan mille tahansa Skillbox-kurssille "Habr" -tarjouskoodilla.

Skillbox suosittelee: Kouluttava verkkokurssi "Ammatti Java-kehittäjä".

Kuinka pelata Memory Cardia

Peli alkaa korttisarjan esittelyllä. He makaavat alaspäin (kuva alaspäin). Kun napsautat mitä tahansa, kuva avautuu muutamaksi sekunniksi.

Pelaajan tehtävänä on löytää kaikki kortit, joissa on samat kuvat. Jos ensimmäisen kortin avaamisen jälkeen käännät toisen ja kuvat täsmäävät, molemmat kortit pysyvät auki. Jos ne eivät täsmää, kortit suljetaan uudelleen. Tavoitteena on avata kaikki.

Hankkeen rakenne

Pelistä yksinkertaisen version luomiseksi tarvitset seuraavat komponentit:

  • Yksi ohjain: GameController.swift.
  • Yksi näkymä: CardCell.swift.
  • Kaksi mallia: MemoryGame.swift ja Card.swift.
  • Main.storyboard varmistaaksesi, että koko komponenttisarja on saatavilla.

Aloitamme pelin yksinkertaisimmalla osalla, korteilla.

Card.swift

Korttimallilla on kolme ominaisuutta: id jokaisen tunnistamiseen, looginen muuttuja, joka määrittää kortin tilan (piilotettu tai avoin) ja artworkURL korttien kuville.

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

Tarvitset myös näitä menetelmiä hallitaksesi käyttäjien vuorovaikutusta karttojen kanssa:

Menetelmä kuvan näyttämiseksi kortilla. Tässä palautamme kaikki ominaisuudet oletusasetuksiin. ID:lle luomme satunnaisen tunnuksen kutsumalla NSUUIS().uuidString.

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

Menetelmä henkilökorttien vertailuun.

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

Tapa luoda kopio jokaisesta kortista - jotta saadaan suurempi määrä samanlaisia. Tämä menetelmä palauttaa kortin, jolla on samanlaiset arvot.

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

Ja vielä yksi menetelmä tarvitaan korttien sekoittamiseen alussa. Teemme siitä Array-luokan laajennuksen.

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

Ja tässä on korttimallin koodin toteutus kaikilla ominaisuuksilla ja menetelmillä.

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

Menkää.

Toinen malli on MemoryGame, tässä asetamme 4*4-ruudukon. Mallissa on ominaisuuksia, kuten kortit (korttijoukko ruudukossa), cardsShown-taulukko, jossa kortit ovat jo auki, ja boolen muuttuja isPlaying pelin tilan seuraamiseksi.

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

Meidän on myös kehitettävä menetelmiä käyttäjien vuorovaikutuksen hallitsemiseksi verkon kanssa.

Menetelmä, joka sekoittaa kortit ruudukossa.

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

Menetelmä uuden pelin luomiseen. Tässä kutsumme ensimmäistä menetelmää alkuperäisen asettelun aloittamiseksi ja isPlaying-muuttujan alustamiseksi true.

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

Jos haluamme käynnistää pelin uudelleen, sitten asetamme isPlaying-muuttujan arvoon false ja poistamme korttien alkuperäisen asettelun.

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

Tapa klikattujen korttien vahvistamiseksi. Hänestä lisää myöhemmin.

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

Menetelmä, joka palauttaa tietyn kortin sijainnin.

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

Valitun kortin standardinmukaisuuden tarkistaminen.

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

Tämä menetelmä lukee **cardsShown**-taulukon viimeisen elementin ja palauttaa yhteensopimattoman kortin.

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

Main.soryboard näyttää suunnilleen tältä:

Muistikorttipelin kirjoittaminen Swiftissä

Aluksi sinun on asetettava uudelle pelille viewDidLoad ohjaimessa, mukaan lukien ruudukon kuvat. Pelissä kaikkea tätä edustaa 4*4 collectionView. Jos et ole vielä tutustunut collectionViewiin, tässä se on saat tarvitsemasi tiedot.

Määritämme GameControllerin sovelluksen pääohjaimeksi. GameControllerilla on kokoelmanäkymä, jota kutsumme IBO-outletiksi. Toinen viittaus on IBAction onStartGame() -painike, tämä on UIB-painike, näet sen kuvakäsikirjoituksessa nimeltä PLAY.

Hieman ohjaimien käyttöönotosta:

  • Ensin alustetaan kaksi pääobjektia - ruudukko: peli = MemoryGame() ja joukko korttia: kortit = [Card]().
  • Asetamme alkumuuttujat viewDidLoadiksi, tämä on ensimmäinen menetelmä, jota kutsutaan pelin ollessa käynnissä.
  • collectionView on piilotettu, koska kaikki kortit ovat piilossa, kunnes käyttäjä painaa PLAY.
  • Heti kun painamme PLAY, onStartGame IBAction -osio alkaa ja asetamme collectionView isHidden -ominaisuuden arvoksi false, jotta kortit tulevat näkyviin.
  • Joka kerta, kun käyttäjä valitsee kortin, didSelectItemAt-menetelmä kutsutaan. Menetelmässä kutsumme didSelectCard toteuttamaan pelin päälogiikan.

Tässä on viimeinen GameController-toteutus:

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

Puhutaanpa nyt vähän tärkeistä protokollista.

Pöytäkirjat

Työskentely protokollien kanssa on Swift-ohjelmoinnin ydin. Protokollat ​​tarjoavat mahdollisuuden määritellä sääntöjä luokalle, rakenteelle tai luettelolle. Tämä periaate mahdollistaa modulaarisen ja laajennettavan koodin kirjoittamisen. Itse asiassa tämä on malli, jota otamme jo käyttöön GameController-kokoelmassa. Tehdään nyt oma versio. Syntaksi näyttää tältä:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

Tiedämme, että protokollan avulla voimme määritellä sääntöjä tai ohjeita luokan toteuttamiseen, joten mietitään mitä niiden pitäisi olla. Tarvitset yhteensä neljä.

  • Pelin aloitus: memoryGameDidStart.
  • Sinun on käännettävä kortti kuvapuoli alaspäin: memoryGameShowCards.
  • Sinun on käännettävä kortti kuvapuoli alaspäin: memoryGameHideCards.
  • Pelin loppu: memoryGameDidEnd.

Kaikki neljä menetelmää voidaan toteuttaa pääluokkaan, joka on GameController.

memoryGameDidStart

Kun tämä menetelmä suoritetaan, pelin pitäisi alkaa (käyttäjä painaa PLAY). Täällä lataamme sisällön uudelleen kutsumalla collectionView.reloadData(), joka sekoittaa kortit.

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

memoryGameShowCards

Kutsumme tätä menetelmää kokoelmastaSDViewSelectItemAt. Ensin se näyttää valitun kortin. Tarkistaa sitten, onko cardsShown-taulukossa vertaamaton kortti (jos korttien määrä on pariton). Jos sellainen on, valittua korttia verrataan siihen. Jos kuvat ovat samat, molemmat kortit lisätään kortteihinNäytetty ja pysyvät kuvapuoli ylöspäin. Jos erilainen, kortti jättää kortitNäytettynä ja molemmat käännetään kuvapuoli alaspäin.

memoryGameHideCards

Jos kortit eivät täsmää, tätä menetelmää kutsutaan ja korttien kuvat piilotetaan.

näytetään = false.

memoryGameDidEnd

Kun tätä menetelmää kutsutaan, se tarkoittaa, että kaikki kortit on jo paljastettu ja ovat kortitNäytetyt-luettelossa: kortitNäytetyt.määrä = kortit.määrä, joten peli on ohi. Menetelmää kutsutaan nimenomaan sen jälkeen, kun olemme kutsuneet endGame() asettamalla isPlaying var arvoon false, minkä jälkeen näytetään pelin lopetusviesti. Myös alertController toimii ohjaimen indikaattorina. viewDidDisappear kutsutaan ja peli nollataan.

Tältä kaikki näyttää GameControllerissa:

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

Muistikorttipelin kirjoittaminen Swiftissä
Itse asiassa siinä kaikki. Voit käyttää tätä projektia luodaksesi oman version pelistä.

Hyvää koodausta!

Skillbox suosittelee:

Lähde: will.com

Lisää kommentti