Menulis Permainan Kad Memori dalam Swift

Menulis Permainan Kad Memori dalam Swift

Artikel ini menerangkan proses mencipta permainan latihan memori mudah yang saya sangat suka. Selain menjadi baik dengan sendirinya, anda akan mempelajari lebih sedikit tentang kelas dan protokol Swift semasa anda pergi. Tetapi sebelum kita mula, mari kita fahami permainan itu sendiri.

Kami mengingatkan: untuk semua pembaca "Habr" - diskaun sebanyak 10 rubel apabila mendaftar dalam mana-mana kursus Skillbox menggunakan kod promosi "Habr".

Skillbox mengesyorkan: Kursus dalam talian pendidikan "Pemaju Java Profesion".

Cara bermain Kad Memori

Permainan dimulakan dengan demonstrasi set kad. Mereka berbaring menghadap ke bawah (masing-masing, imej ke bawah). Apabila anda mengklik pada mana-mana satu, imej terbuka selama beberapa saat.

Tugas pemain adalah untuk mencari semua kad dengan gambar yang sama. Jika, selepas membuka kad pertama, anda membelek yang kedua dan gambar sepadan, kedua-dua kad kekal terbuka. Jika mereka tidak sepadan, kad ditutup semula. Matlamatnya adalah untuk membuka segala-galanya.

Struktur projek

Untuk mencipta versi ringkas permainan ini, anda memerlukan komponen berikut:

  • Satu Pengawal: GameController.swift.
  • Satu Pandangan: CardCell.swift.
  • Dua Model: MemoryGame.swift dan Card.swift.
  • Main.storyboard untuk memastikan keseluruhan set komponen tersedia.

Kita mulakan dengan komponen permainan yang paling mudah, kad.

Card.swift

Model kad akan mempunyai tiga sifat: id untuk mengenal pasti setiap satu, pembolehubah boolean yang ditunjukkan untuk menentukan status kad (tersembunyi atau terbuka), dan artworkURL untuk imej pada kad.

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

Anda juga memerlukan kaedah ini untuk mengawal interaksi pengguna dengan peta:

Kaedah untuk memaparkan imej pada kad. Di sini kami menetapkan semula semua sifat kepada lalai. Untuk id, kami menjana id rawak dengan memanggil NSUUIS().uuidString.

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

Kaedah untuk membandingkan kad pengenalan.

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

Kaedah untuk membuat salinan setiap kad - untuk mendapatkan lebih banyak bilangan yang sama. Kaedah ini akan mengembalikan kad dengan nilai yang sama.

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

Dan satu lagi kaedah diperlukan untuk mengocok kad pada permulaan. Kami akan menjadikannya lanjutan daripada kelas Array.

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

Dan inilah pelaksanaan kod untuk model Kad dengan semua sifat dan kaedah.

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

Teruskan.

Model kedua ialah MemoryGame, di sini kami menetapkan grid 4*4. Model ini akan mempunyai sifat seperti kad (susunan kad pada grid), tatasusunan cardShown dengan kad telah dibuka dan pembolehubah boolean isPlaying untuk menjejaki status permainan.

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

Kami juga perlu membangunkan kaedah untuk mengawal interaksi pengguna dengan grid.

Kaedah yang mengocok kad dalam grid.

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

Kaedah untuk mencipta permainan baharu. Di sini kami memanggil kaedah pertama untuk memulakan susun atur awal dan memulakan pembolehubah isPlaying kepada benar.

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

Jika kita ingin memulakan semula permainan, kemudian kami menetapkan pembolehubah isPlaying kepada palsu dan mengalih keluar susun atur awal kad.

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

Kaedah untuk mengesahkan kad yang diklik. Lebih lanjut mengenai dia nanti.

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

Kaedah yang mengembalikan kedudukan kad tertentu.

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

Menyemak pematuhan kad yang dipilih dengan standard.

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

Kaedah ini membaca elemen terakhir dalam tatasusunan **cardsShown** dan mengembalikan kad yang tidak sepadan.

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

Main.storyboard kelihatan seperti ini:

Menulis Permainan Kad Memori dalam Swift

Pada mulanya, anda perlu menetapkan permainan baharu sebagai viewDidLoad dalam pengawal, termasuk imej untuk grid. Dalam permainan, semua ini akan diwakili oleh 4*4 collectionView. Jika anda belum biasa dengan collectionView, inilah dia anda boleh mendapatkan maklumat yang anda perlukan.

Kami akan mengkonfigurasi GameController sebagai pengawal akar aplikasi. GameController akan mempunyai collectionView yang akan kami rujuk sebagai IBOutlet. Rujukan lain ialah pada butang IBAction onStartGame(), ini adalah UIButton, anda boleh melihatnya dalam papan cerita yang dipanggil PLAY.

Sedikit mengenai pelaksanaan pengawal:

  • Mula-mula, kami memulakan dua objek utama - grid: permainan = MemoryGame(), dan satu set kad: kad = [Kad]().
  • Kami menetapkan pembolehubah awal sebagai viewDidLoad, ini adalah kaedah pertama yang dipanggil semasa permainan sedang berjalan.
  • collectionView ditetapkan kepada tersembunyi kerana semua kad disembunyikan sehingga pengguna menekan PLAY.
  • Sebaik sahaja kami menekan PLAY, bahagian onStartGame IBAction bermula dan kami menetapkan sifat collectionView isHidden kepada palsu supaya kad boleh kelihatan.
  • Setiap kali pengguna memilih kad, kaedah didSelectItemAt dipanggil. Dalam kaedah yang kami panggil didSelectCard untuk melaksanakan logik permainan utama.

Berikut ialah pelaksanaan GameController terakhir:

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

Sekarang mari kita bercakap sedikit tentang protokol penting.

Protokol

Bekerja dengan protokol adalah teras pengaturcaraan Swift. Protokol menyediakan keupayaan untuk menentukan peraturan untuk kelas, struktur atau penghitungan. Prinsip ini membolehkan anda menulis kod modular dan boleh diperluaskan. Malah, ini ialah corak yang kami sudah laksanakan untuk collectionView dalam GameController. Sekarang mari kita buat versi kita sendiri. Sintaks akan kelihatan seperti ini:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

Kami tahu bahawa protokol membolehkan kami mentakrifkan peraturan atau arahan untuk melaksanakan kelas, jadi mari kita fikirkan apa yang sepatutnya. Anda memerlukan empat secara keseluruhan.

  • Permulaan permainan: memoryGameDidStart.
  • Anda perlu membalikkan kad ke bawah: memoryGameShowCards.
  • Anda perlu membalikkan kad ke bawah: memoryGameHideCards.
  • Tamat permainan: memoryGameDidEnd.

Keempat-empat kaedah boleh dilaksanakan untuk kelas utama, iaitu GameController.

memoryGameDidStart

Apabila kaedah ini dijalankan, permainan harus dimulakan (pengguna menekan PLAY). Di sini kami hanya akan memuatkan semula kandungan dengan memanggil collectionView.reloadData(), yang akan mengocok kad.

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

memoryGameShowKad

Kami memanggil kaedah ini daripada collectionSDViewSelectItemAt. Mula-mula ia menunjukkan kad yang dipilih. Kemudian semak untuk melihat sama ada terdapat kad yang tidak sepadan dalam tatasusunan cardsShown (jika bilangan kadShown ganjil). Jika ada, kad yang dipilih akan dibandingkan dengannya. Jika gambar adalah sama, kedua-dua kad ditambahkan pada kad yang Ditunjukkan dan kekal menghadap ke atas. Jika berbeza, kad itu meninggalkan kad Ditunjukkan dan kedua-duanya menghadap ke bawah.

memoryGameHideKad

Jika kad tidak sepadan, kaedah ini dipanggil dan imej kad disembunyikan.

ditunjukkan = palsu.

memoryGameDidEnd

Apabila kaedah ini dipanggil, ini bermakna semua kad telah didedahkan dan berada dalam senarai kadShown: cardsShown.count = cards.count, jadi permainan tamat. Kaedah ini dipanggil secara khusus selepas kami memanggil endGame() untuk menetapkan isPlaying var kepada false, selepas itu mesej tamat permainan ditunjukkan. Juga alertController digunakan sebagai penunjuk untuk pengawal. viewDidDisappear dipanggil dan permainan ditetapkan semula.

Inilah rupa semuanya dalam 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()
    }
}

Menulis Permainan Kad Memori dalam Swift
Sebenarnya, itu sahaja. Anda boleh menggunakan projek ini untuk mencipta versi permainan anda sendiri.

Selamat mengekod!

Skillbox mengesyorkan:

Sumber: www.habr.com

Tambah komen