Pagsusulat ng Memory Card Game sa Swift

Pagsusulat ng Memory Card Game sa Swift

Inilalarawan ng artikulong ito ang proseso ng paglikha ng isang simpleng laro ng pagsasanay sa memorya na talagang kinagigiliwan ko. Bukod sa pagiging mahusay sa sarili, matututo ka pa ng kaunti tungkol sa mga klase at protocol ng Swift habang nagpapatuloy ka. Ngunit bago tayo magsimula, unawain natin ang laro mismo.

Pinapaalala namin sa iyo: para sa lahat ng mga mambabasa ng "Habr" - isang diskwento na 10 rubles kapag nag-enroll sa anumang kurso sa Skillbox gamit ang code na pang-promosyon ng "Habr".

Inirerekomenda ng Skillbox ang: Online na kursong pang-edukasyon "Propesyon Java Developer".

Paano laruin ang Memory Card

Ang laro ay nagsisimula sa isang pagpapakita ng isang hanay ng mga baraha. Sila ay nakahiga nang nakaharap (ayon sa pagkakabanggit, imahe sa ibaba). Kapag nag-click ka sa alinman, magbubukas ang larawan nang ilang segundo.

Ang gawain ng manlalaro ay hanapin ang lahat ng card na may parehong mga larawan. Kung, pagkatapos buksan ang unang card, ibabalik mo ang pangalawa at magkatugma ang mga larawan, mananatiling bukas ang parehong card. Kung hindi sila magkatugma, ang mga card ay sarado muli. Ang layunin ay buksan ang lahat.

Istraktura ng proyekto

Upang lumikha ng isang simpleng bersyon ng larong ito kailangan mo ang mga sumusunod na bahagi:

  • Isang Controller: GameController.swift.
  • Isang View: CardCell.swift.
  • Dalawang Modelo: MemoryGame.swift at Card.swift.
  • Main.storyboard upang matiyak na ang buong hanay ng mga bahagi ay magagamit.

Magsisimula tayo sa pinakasimpleng bahagi ng laro, ang mga card.

Card.mabilis

Ang modelo ng card ay magkakaroon ng tatlong katangian: id para matukoy ang bawat isa, boolean variable na ipinapakita para tukuyin ang status ng card (nakatago o bukas), at artworkURL para sa mga larawan sa mga card.

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

Kakailanganin mo rin ang mga pamamaraang ito para makontrol ang pakikipag-ugnayan ng user sa mga mapa:

Paraan para sa pagpapakita ng isang imahe sa isang card. Dito, ni-reset namin ang lahat ng property sa default. Para sa id, bumubuo kami ng random na id sa pamamagitan ng pagtawag sa NSUUIS().uuidString.

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

Paraan para sa paghahambing ng mga ID card.

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

Paraan ng paggawa ng kopya ng bawat card - upang makakuha ng mas malaking bilang ng magkakapareho. Ibabalik ng pamamaraang ito ang card na may mga katulad na halaga.

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

At isa pang paraan ang kailangan para i-shuffle ang mga card sa simula. Gagawin namin itong extension ng klase ng Array.

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

At narito ang pagpapatupad ng code para sa modelo ng Card kasama ang lahat ng mga katangian at pamamaraan.

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

Sige.

Ang pangalawang modelo ay MemoryGame, dito nagtakda kami ng 4*4 grid. Ang modelo ay magkakaroon ng mga katangian tulad ng mga card (isang array ng mga card sa isang grid), isang cardShown array na may mga card na nakabukas na, at isang boolean variable na Naglalaro upang subaybayan ang status ng laro.

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

Kailangan din nating bumuo ng mga pamamaraan para makontrol ang pakikipag-ugnayan ng user sa grid.

Isang paraan na nag-shuffle ng mga card sa isang grid.

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

Paraan para sa paglikha ng isang bagong laro. Dito namin tinatawag ang unang paraan upang simulan ang paunang layout at simulan ang isPlaying variable sa true.

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

Kung gusto nating i-restart ang laro, pagkatapos ay itinakda namin ang isPlaying variable sa false at alisin ang unang layout ng mga card.

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

Paraan para sa pag-verify ng mga na-click na card. Higit pa tungkol sa kanya mamaya.

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

Isang paraan na nagbabalik ng posisyon ng isang partikular na card.

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

Sinusuri ang pagsunod ng napiling card sa pamantayan.

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

Binabasa ng paraang ito ang huling elemento sa **cardsShown** array at ibinabalik ang hindi tumutugmang card.

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

Mukhang ganito ang Main.storyboard:

Pagsusulat ng Memory Card Game sa Swift

Sa una, kailangan mong itakda ang bagong laro bilang viewDidLoad sa controller, kasama ang mga larawan para sa grid. Sa laro, ang lahat ng ito ay kakatawanin ng 4*4 collectionView. Kung hindi ka pa pamilyar sa collectionView, narito na makukuha mo ang impormasyong kailangan mo.

Iko-configure namin ang GameController bilang root controller ng application. Ang GameController ay magkakaroon ng collectionView na aming sanggunian bilang isang IBOutlet. Ang isa pang sanggunian ay ang IBAction onStartGame() na buton, ito ay isang UIButton, makikita mo ito sa storyboard na tinatawag na PLAY.

Kaunti tungkol sa pagpapatupad ng mga controllers:

  • Una, sinisimulan namin ang dalawang pangunahing bagay - ang grid: laro = MemoryGame(), at isang set ng mga card: card = [Card]().
  • Itinakda namin ang mga paunang variable bilang viewDidLoad, ito ang unang paraan na tinatawag habang tumatakbo ang laro.
  • Nakatakda ang collectionView sa hidden dahil nakatago ang lahat ng card hanggang sa pinindot ng user ang PLAY.
  • Sa sandaling pinindot namin ang PLAY, magsisimula ang onStartGame IBAction na seksyon, at itinakda namin ang collectionView isHidden property sa false para makita ang mga card.
  • Sa tuwing pipili ang user ng card, tinatawag ang didSelectItemAt method. Sa pamamaraang tinatawag naming didSelectCard upang ipatupad ang pangunahing lohika ng laro.

Narito ang panghuling pagpapatupad ng 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 {
                // 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)
    }
}

Ngayon ay pag-usapan natin ang tungkol sa mahahalagang protocol.

Mga protocol

Ang pagtatrabaho sa mga protocol ay ang core ng Swift programming. Ang mga protocol ay nagbibigay ng kakayahang tumukoy ng mga panuntunan para sa isang klase, istraktura, o enumeration. Ang prinsipyong ito ay nagpapahintulot sa iyo na magsulat ng modular at extensible code. Sa katunayan, ito ay isang pattern na ipinapatupad na namin para sa collectionView sa GameController. Ngayon gumawa tayo ng sarili nating bersyon. Magiging ganito ang syntax:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

Alam namin na ang isang protocol ay nagpapahintulot sa amin na tukuyin ang mga panuntunan o mga tagubilin para sa pagpapatupad ng isang klase, kaya isipin natin kung ano ang dapat na mga ito. Kailangan mo ng apat sa kabuuan.

  • Pagsisimula ng laro: memoryGameDidStart.
  • Kailangan mong ibababa ang card: memoryGameShowCards.
  • Kailangan mong ibababa ang card: memoryGameHideCards.
  • Pagtatapos ng laro: memoryGameDidEnd.

Ang lahat ng apat na pamamaraan ay maaaring ipatupad para sa pangunahing klase, na GameController.

memoryGameDidStart

Kapag ang pamamaraang ito ay pinatakbo, ang laro ay dapat magsimula (ang user ay pinindot ang PLAY). Dito, ire-reload lang namin ang content sa pamamagitan ng pagtawag sa collectionView.reloadData(), na mag-shuffle sa mga card.

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

memoryGameShowCards

Tinatawag namin ang paraang ito mula sa collectionSDViewSelectItemAt. Una ay ipinapakita nito ang napiling card. Pagkatapos ay suriin upang makita kung mayroong isang walang kapantay na card sa mga cardShown array (kung ang bilang ng mga cardShown ay kakaiba). Kung mayroon man, ang napiling card ay inihambing dito. Kung ang mga larawan ay pareho, ang parehong mga card ay idaragdag sa mga card na ipinapakita at mananatiling nakaharap. Kung iba, ang card ay nag-iiwan ng mga cardIpinakita at pareho ay nakaharap pababa.

memoryGameHideCard

Kung ang mga card ay hindi tumutugma, ang pamamaraang ito ay tinatawag at ang mga larawan ng card ay nakatago.

ipinapakita = mali.

memoryGameDidEnd

Kapag tinawag ang pamamaraang ito, nangangahulugan ito na ang lahat ng mga card ay naihayag na at nasa listahan ng mga cardShown: cardsShown.count = cards.count, kaya tapos na ang laro. Ang pamamaraan ay partikular na tinawag pagkatapos naming tawagan ang endGame() upang itakda ang isPlaying var sa false, pagkatapos ay ipapakita ang mensahe ng pagtatapos ng laro. Ginagamit din ang alertController bilang indicator para sa controller. Ang viewDidDisappear ay tinawag at ang laro ay na-reset.

Narito kung ano ang hitsura ng lahat sa 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()
    }
}

Pagsusulat ng Memory Card Game sa Swift
Actually, yun lang. Maaari mong gamitin ang proyektong ito upang lumikha ng iyong sariling bersyon ng laro.

Maligayang coding!

Inirerekomenda ng Skillbox ang:

Pinagmulan: www.habr.com

Magdagdag ng komento