Pisanje igre memorijskih kartica u Swiftu

Pisanje igre memorijskih kartica u Swiftu

Ovaj članak opisuje proces stvaranja jednostavne igre za vježbanje pamćenja u kojoj stvarno uživam. Osim što je sam po sebi dobar, usput ćete naučiti nešto više o Swift klasama i protokolima. Ali prije nego što počnemo, shvatimo samu igru.

Podsjećamo: za sve čitatelje "Habra" - popust od 10 000 rubalja pri upisu na bilo koji tečaj Skillbox koristeći promotivni kod "Habr".

Skillbox preporučuje: Edukativni online tečaj "Profesija Java developer".

Kako igrati Memory Card

Igra počinje demonstracijom seta karata. Leže licem prema dolje (odnosno, slikom prema dolje). Kada kliknete na bilo koji, slika se otvara na nekoliko sekundi.

Zadatak igrača je pronaći sve karte s istim slikama. Ako nakon otvaranja prve karte okrenete drugu i slike se poklope, obje kartice ostaju otvorene. Ako se ne podudaraju, karte se ponovno zatvaraju. Cilj je otvoriti sve.

Struktura projekta

Za izradu jednostavne verzije ove igre potrebne su vam sljedeće komponente:

  • Jedan kontroler: GameController.swift.
  • Jedan pogled: CardCell.swift.
  • Dva modela: MemoryGame.swift i Card.swift.
  • Main.storyboard kako bi se osiguralo da je cijeli skup komponenti dostupan.

Počinjemo s najjednostavnijom komponentom igre, kartama.

Card.swift

Model kartice imat će tri svojstva: ID za identifikaciju svake od njih, Booleovu varijablu prikazanu za određivanje statusa kartice (skrivena ili otvorena) i artworkURL za slike na karticama.

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

Također ćete trebati ove metode za kontrolu interakcije korisnika s kartama:

Metoda prikazivanja slike na kartici. Ovdje vraćamo sva svojstva na zadane. Za id, generiramo nasumični id pozivanjem NSUUIS().uuidString.

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

Metoda usporedbe osobnih iskaznica.

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

Metoda za stvaranje kopije svake kartice - kako bi se dobio veći broj istih. Ova metoda će vratiti karticu sa sličnim vrijednostima.

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

I još jedna metoda je potrebna za miješanje karata u startu. Napravit ćemo to proširenje klase Array.

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

I ovdje je implementacija koda za Card model sa svim svojstvima i metodama.

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

Samo naprijed.

Drugi model je MemoryGame, ovdje smo postavili mrežu 4*4. Model će imati svojstva kao što su kartice (niz karata na mreži), polje cardsShown s već otvorenim kartama i booleovu varijablu isPlaying za praćenje statusa igre.

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

Također moramo razviti metode za kontrolu interakcije korisnika s mrežom.

Metoda koja miješa karte u mreži.

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

Metoda za stvaranje nove igre. Ovdje pozivamo prvu metodu za pokretanje početnog izgleda i inicijaliziranje varijable isPlaying na true.

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

Ako želimo ponovno pokrenuti igru, tada postavljamo varijablu isPlaying na false i uklanjamo početni raspored karata.

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

Metoda provjere kliknutih kartica. Više o njemu kasnije.

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

Metoda koja vraća položaj određene kartice.

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

Provjera usklađenosti odabrane kartice sa standardom.

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

Ova metoda čita posljednji element u nizu **cardsShown** i vraća karticu koja se ne podudara.

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

Main.storyboard izgleda otprilike ovako:

Pisanje igre memorijskih kartica u Swiftu

U početku morate novu igru ​​postaviti kao viewDidLoad u kontroleru, uključujući slike za rešetku. U igri će sve to predstavljati 4*4 collectionView. Ako još niste upoznati s CollectionViewom, evo ga možete dobiti informacije koje su vam potrebne.

Konfigurirat ćemo GameController kao korijenski kontroler aplikacije. GameController će imati collectionView koji ćemo spominjati kao IBOutlet. Još jedna referenca je gumb IBAction onStartGame(), ovo je UIButton, možete ga vidjeti u ploči scenarija pod nazivom PLAY.

Malo o implementaciji kontrolera:

  • Prvo inicijaliziramo dva glavna objekta - rešetku: game = MemoryGame() i skup karata: cards = [Card]().
  • Postavili smo početne varijable kao viewDidLoad, ovo je prva metoda koja se poziva dok igra radi.
  • collectionView je postavljen na skriven jer su sve kartice skrivene dok korisnik ne pritisne PLAY.
  • Čim pritisnemo PLAY, pokreće se odjeljak onStartGame IBAction, a svojstvo collectionView isHidden postavljamo na false kako bi kartice postale vidljive.
  • Svaki put kada korisnik odabere karticu, poziva se metoda didSelectItemAt. U metodi koju pozivamo didSelectCard za implementaciju glavne logike igre.

Evo konačne implementacije GameControllera:

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

Razgovarajmo sada malo o važnim protokolima.

protokoli

Rad s protokolima srž je Swift programiranja. Protokoli pružaju mogućnost definiranja pravila za klasu, strukturu ili enumeraciju. Ovaj princip vam omogućuje pisanje modularnog i proširivog koda. Zapravo, ovo je uzorak koji već implementiramo za collectionView u GameControlleru. Sada napravimo vlastitu verziju. Sintaksa će izgledati ovako:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

Znamo da nam protokol omogućuje definiranje pravila ili uputa za implementaciju klase, pa razmislimo o tome što bi one trebale biti. Trebate ih ukupno četiri.

  • Početak igre: memoryGameDidStart.
  • Morate okrenuti karticu licem prema dolje: memoryGameShowCards.
  • Morate okrenuti karticu licem prema dolje: memoryGameHideCards.
  • Kraj igre: memoryGameDidEnd.

Sve četiri metode mogu se implementirati za glavnu klasu, a to je GameController.

memoryGameDidStart

Kada se ova metoda pokrene, igra bi trebala započeti (korisnik pritisne PLAY). Ovdje ćemo jednostavno ponovno učitati sadržaj pozivom collectionView.reloadData(), koji će izmiješati karte.

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

memoryGameShowCards

Ovu metodu pozivamo iz collectionSDViewSelectItemAt. Prvo prikazuje odabranu karticu. Zatim provjerava postoji li neuparena karta u nizu cardsShown (ako je broj cardsShown neparan). Ako postoji, odabrana karta se uspoređuje s njom. Ako su slike iste, obje karte se dodaju na prikazane kartice i ostaju licem prema gore. Ako je drugačija, karta ostavlja prikazane karte i obje su okrenute licem prema dolje.

memoryGameHideCards

Ako se kartice ne podudaraju, poziva se ova metoda i slike kartica se skrivaju.

prikazano = lažno.

memoryGameDidEnd

Kada se ova metoda pozove, to znači da su sve karte već otkrivene i nalaze se na listi cardsShown: cardsShown.count = cards.count, tako da je igra gotova. Metoda se posebno poziva nakon što smo pozvali endGame() da postavimo isPlaying var na false, nakon čega se prikazuje poruka o kraju igre. AlertController se također koristi kao indikator za kontroler. viewDidDisappear se poziva i igra se resetira.

Evo kako to sve izgleda u GameControlleru:

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 memorijskih kartica u Swiftu
Zapravo, to je sve. Možete koristiti ovaj projekt za izradu vlastite verzije igre.

Sretno kodiranje!

Skillbox preporučuje:

Izvor: www.habr.com

Dodajte komentar