Skriuw in Memory Cards Game yn Swift

Skriuw in Memory Cards Game yn Swift

Dit artikel beskriuwt it proses fan it meitsjen fan in ienfâldich spultsje foar ûnthâldtraining dat ik echt leuk fyn. Neist it op himsels goed te wêzen, sille jo wat mear leare oer Swift-klassen en protokollen as jo gean. Mar foardat wy begjinne, litte wy it spultsje sels begripe.

Wy herinnerje: foar alle lêzers fan "Habr" - in koarting fan 10 roebel by it ynskriuwen fan in Skillbox-kursus mei de promoasjekoade "Habr".

Skillbox advisearret: Edukative online kursus "Berop Java Developer".

Hoe te spyljen Memory Card

It spul begjint mei in demonstraasje fan in set fan kaarten. Se lizze gesicht del (resp. ôfbylding del). As jo ​​op ien klikke, iepenet de ôfbylding foar in pear sekonden.

De taak fan de spiler is om alle kaarten te finen mei deselde foto's. As jo, nei it iepenjen fan de earste kaart, de twadde omdraaie en de foto's oerienkomme, bliuwe beide kaarten iepen. As se net oerienkomme, wurde de kaarten wer sletten. It doel is om alles te iepenjen.

Projektstruktuer

Om in ienfâldige ferzje fan dit spultsje te meitsjen hawwe jo de folgjende komponinten nedich:

  • Ien Controller: GameController.swift.
  • Ien werjefte: CardCell.swift.
  • Twa modellen: MemoryGame.swift en Card.swift.
  • Main.storyboard om te soargjen dat de hiele set fan komponinten beskikber is.

Wy begjinne mei de ienfâldichste komponint fan it spul, de kaarten.

Card.swift

It kaartmodel sil trije eigenskippen hawwe: id om elk te identifisearjen, in Booleaanske fariabele werjûn om de status fan 'e kaart te spesifisearjen (ferburgen as iepen), en artworkURL foar de ôfbyldings op' e kaarten.

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

Jo sille ek dizze metoaden nedich wêze om brûkersynteraksje mei kaarten te kontrolearjen:

Metoade foar it werjaan fan in ôfbylding op in kaart. Hjir sette wy alle eigenskippen werom nei standert. Foar id generearje wy in willekeurige id troch in oprop NSUUIS ().uuidString.

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

Metoade foar it fergelykjen fan ID-kaarten.

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

Metoade om in kopy fan elke kaart te meitsjen - om in grutter oantal identike te krijen. Dizze metoade sil werom kaart mei ferlykbere wearden.

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

En noch ien metoade is nedich om te skodzjen de kaarten oan it begjin. Wy meitsje it in útwreiding fan 'e Array-klasse.

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

En hjir is de ymplemintaasje fan 'e koade foar it Card-model mei alle eigenskippen en metoaden.

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

Gean foarút.

It twadde model is MemoryGame, hjir sette wy in 4 * 4 raster yn. It model sil eigenskippen hawwe lykas kaarten (in array fan kaarten op in raster), in cardsShown-array mei kaarten dy't al iepen binne, en in Booleaanske fariabele is Playing om de status fan it spul te folgjen.

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

Wy moatte ek metoaden ûntwikkelje om ynteraksje fan brûkers mei it raster te kontrolearjen.

In metoade dy't shuffles kaarten yn in raster.

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

Metoade foar it meitsjen fan in nij spultsje. Hjir neame wy de earste metoade om de earste yndieling te begjinnen en de isPlaying-fariabele te initialisearjen nei wier.

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

As wy it spultsje opnij wolle begjinne, dan sette wy de fariabele isPlaying op falsk en ferwiderje de earste yndieling fan 'e kaarten.

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

Metoade foar it ferifiearjen fan oanklikte kaarten. Letter mear oer him.

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

In metoade dy't jout de posysje fan in spesifike card.

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

Kontrolearje it neilibjen fan de selektearre kaart mei de standert.

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

Dizze metoade lêst it lêste elemint yn de ** cardsShown ** rige en jout de net-oerienkommende 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 en GameController.swift

Main.storyboard sjocht der sa út:

Skriuw in Memory Cards Game yn Swift

Yn it earstoan moatte jo it nije spultsje ynstelle as viewDidLoad yn 'e controller, ynklusyf de ôfbyldings foar it raster. Yn it spultsje sil dit alles wurde fertsjintwurdige troch 4 * 4 collectionView. As jo ​​​​noch net bekend binne mei collectionView, hjir is it kinne jo krije de ynformaasje dy't jo nedich hawwe.

Wy sille de GameController konfigurearje as de root-controller fan 'e applikaasje. De GameController sil in samlingView hawwe dy't wy sille ferwize as in IBOutlet. In oare ferwizing is nei de IBAction onStartGame () knop, dit is in UIButton, kinne jo sjen it yn it storyboard neamd PLAY.

In bytsje oer de ymplemintaasje fan controllers:

  • Earst, wy inisjalisearje twa wichtichste objekten - it roaster: game = MemoryGame (), en in set fan kaarten: cards = [Card] ().
  • Wy sette de earste fariabelen as viewDidLoad, dit is de earste metoade dy't neamd wurdt wylst it spultsje rint.
  • collectionView is ynsteld op ferburgen omdat alle kaarten binne ferburgen oant de brûker drukt op PLAY.
  • Sadree't wy op PLAY drukke, begjint de onStartGame IBAction-seksje, en wy sette de collectionView isHidden-eigenskip op falsk, sadat de kaarten sichtber wurde kinne.
  • Elke kear as de brûker in kaart kiest, wurdt de metoade didSelectItemAt neamd. Yn 'e metoade dy't wy neame didSelectCard foar it útfieren fan de logika fan it haadspultsje.

Hjir is de lêste ymplemintaasje fan 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)
    }
}

Litte wy no in bytsje prate oer de wichtige protokollen.

Protokollen

Wurkje mei protokollen is de kearn fan Swift-programmearring. Protokollen jouwe de mooglikheid om regels te definiearjen foar in klasse, struktuer of opsomming. Dit prinsipe lit jo modulêre en útwreide koade skriuwe. Yn feite is dit in patroan dat wy al implementearje foar collectionView yn GameController. Litte wy no ús eigen ferzje meitsje. De syntaksis sil der sa útsjen:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

Wy witte dat in protokol ús regels of ynstruksjes kin definiearje foar it útfieren fan in klasse, dus litte wy tinke oer wat se moatte wêze. Jo moatte fjouwer yn totaal.

  • Game start: memoryGameDidStart.
  • Jo moatte keare de kaart face down: memoryGameShowCards.
  • Jo moatte keare de kaart face down: memoryGameHideCards.
  • Game ein: memoryGameDidEnd.

Alle fjouwer metoaden kinne wurde ymplemintearre foar de haadklasse, dat is GameController.

memoryGameDidStart

As dizze metoade wurdt útfierd, moat it spultsje begjinne (de brûker drukt op PLAY). Hjir sille wy gewoan opnij laden de ynhâld troch in oprop collectionView.reloadData (), dat sil skodzje de kaarten.

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

memoryGameShowCards

Wy neame dizze metoade út collectionSDViewSelectItemAt. Earst toant it de selektearre kaart. Dan kontrolearret om te sjen oft der in unmatched kaart yn de cardsShown array (as it oantal cardsShown is ûneven). As der ien is, wurdt de selektearre kaart dêrmei fergelike. As de foto's binne itselde, beide kaarten wurde tafoege oan cardsShown en bliuwe gesicht omheech. As oars, de kaart lit cardsShown en beide wurde draaid face down.

memoryGameHideCards

As de kaarten net oerienkomme, wurdt dizze metoade neamd en de kaartôfbyldings binne ferburgen.

werjûn = falsk.

memoryGameDidEnd

As dizze metoade wurdt neamd, betsjut dat alle kaarten binne al iepenbiere en binne yn de cardsShown list: cardsShown.count = cards.count, sadat it spul is foarby. De metoade wurdt neamd spesifyk neidat wy hawwe neamd endGame () foar in set de isPlaying var nei falsk, wêrnei't it spultsje ein berjocht wurdt werjûn. Ek alertController wurdt brûkt as in yndikator foar de controller. viewDidDisappear wurdt neamd en it spultsje is weromsette.

Hjir is hoe't it der allegear útsjocht yn 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()
    }
}

Skriuw in Memory Cards Game yn Swift
Eins is dat alles. Jo kinne dit projekt brûke om jo eigen ferzje fan it spultsje te meitsjen.

Lokkige kodearring!

Skillbox advisearret:

Boarne: www.habr.com

Add a comment