At skrive et hukommelseskortspil i Swift

At skrive et hukommelseskortspil i Swift

Denne artikel beskriver processen med at skabe et simpelt hukommelsestræningsspil, som jeg virkelig nyder. Udover at være god i sig selv, lærer du lidt mere om Swift-klasser og -protokoller, mens du går. Men før vi starter, lad os forstå selve spillet.

Påmindelse: for alle læsere af "Habr" - en rabat på 10 rubler ved tilmelding til ethvert Skillbox-kursus ved hjælp af "Habr"-kampagnekoden.

Skillbox anbefaler: Pædagogisk online kursus "Profession Java Developer".

Sådan spiller du Memory Card

Spillet begynder med en demonstration af et sæt kort. De ligger med forsiden nedad (henholdsvis billedet nedad). Når du klikker på en, åbnes billedet i et par sekunder.

Spillerens opgave er at finde alle kort med de samme billeder. Hvis du efter at have åbnet det første kort vender det andet og billederne matcher, forbliver begge kort åbne. Hvis de ikke matcher, lukkes kortene igen. Målet er at åbne alt.

Projektets struktur

For at lave en simpel version af dette spil skal du bruge følgende komponenter:

  • Én controller: GameController.swift.
  • One View: CardCell.swift.
  • To modeller: MemoryGame.swift og Card.swift.
  • Main.storyboard for at sikre, at hele sættet af komponenter er tilgængeligt.

Vi starter med den enkleste komponent i spillet, kortene.

Card.swift

Kortmodellen vil have tre egenskaber: id til at identificere hver enkelt, en boolesk variabel vist for at angive kortets status (skjult eller åben) og artworkURL for billederne på kortene.

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

Du skal også bruge disse metoder til at kontrollere brugerinteraktion med kort:

Metode til at vise et billede på et kort. Her nulstiller vi alle egenskaber til standard. For id genererer vi et tilfældigt id ved at kalde NSUUIS().uuidString.

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

Metode til at sammenligne ID-kort.

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

Metode til at oprette en kopi af hvert kort - for at få et større antal identiske. Denne metode returnerer kort med lignende værdier.

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

Og en metode mere er nødvendig for at blande kortene i starten. Vi vil gøre det til en udvidelse af Array-klassen.

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

Og her er implementeringen af ​​koden til kortmodellen med alle egenskaber og metoder.

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

Gå fremad.

Den anden model er MemoryGame, her sætter vi et 4*4 gitter. Modellen vil have egenskaber såsom kort (en række kort på et gitter), et kort vist array med kort, der allerede er åbne, og en boolsk variabel isPlaying for at spore spillets status.

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

Vi skal også udvikle metoder til at kontrollere brugerinteraktion med nettet.

En metode, der blander kort i et gitter.

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

Metode til at skabe et nyt spil. Her kalder vi den første metode til at starte det indledende layout og initialisere isPlaying-variablen til sand.

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

Hvis vi vil genstarte spillet, så indstiller vi isPlaying-variablen til false og fjerner det oprindelige layout af kortene.

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

Metode til at verificere klikkede kort. Mere om ham senere.

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

En metode, der returnerer positionen af ​​et bestemt kort.

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

Kontrol af det valgte korts overensstemmelse med standarden.

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

Denne metode læser det sidste element i **cardsShown**-arrayet og returnerer det ikke-matchende kort.

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

Main.storyboard ser sådan ud:

At skrive et hukommelseskortspil i Swift

I første omgang skal du indstille det nye spil som viewDidLoad i controlleren, inklusive billederne til gitteret. I spillet vil alt dette blive repræsenteret af 4*4 collectionView. Hvis du endnu ikke er bekendt med collectionView, så er den her du kan få de oplysninger, du har brug for.

Vi vil konfigurere GameController som applikationens root-controller. GameControlleren vil have en samlingsvisning, som vi vil referere til som en IBoutlet. En anden reference er IBAction onStartGame() knappen, dette er en UIButton, du kan se den i storyboardet kaldet PLAY.

Lidt om implementeringen af ​​controllere:

  • Først initialiserer vi to hovedobjekter - gitteret: spil = MemoryGame(), og et sæt kort: kort = [Card]().
  • Vi indstiller de indledende variabler som viewDidLoad, dette er den første metode, der kaldes, mens spillet kører.
  • collectionView er sat til skjult, fordi alle kort er skjult, indtil brugeren trykker på PLAY.
  • Så snart vi trykker på PLAY, starter onStartGame IBAction-sektionen, og vi sætter egenskaben collectionView isHidden til false, så kortene kan blive synlige.
  • Hver gang brugeren vælger et kort, kaldes didSelectItemAt-metoden. I metoden kalder vi didSelectCard for at implementere hovedspillets logik.

Her er den endelige GameController-implementering:

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

Lad os nu tale lidt om de vigtige protokoller.

protokoller

At arbejde med protokoller er kernen i Swift-programmering. Protokoller giver mulighed for at definere regler for en klasse, struktur eller opregning. Dette princip giver dig mulighed for at skrive modulær og udvidelig kode. Faktisk er dette et mønster, som vi allerede implementerer for collectionView i GameController. Lad os nu lave vores egen version. Syntaksen vil se således ud:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

Vi ved, at en protokol giver os mulighed for at definere regler eller instruktioner til implementering af en klasse, så lad os tænke over, hvad de skal være. Du skal bruge fire i alt.

  • Spilstart: memoryGameDidStart.
  • Du skal vende kortet med forsiden nedad: memoryGameShowCards.
  • Du skal vende kortet med forsiden nedad: memoryGameHideCards.
  • Spilslut: memoryGameDidEnd.

Alle fire metoder kan implementeres for hovedklassen, som er GameController.

memoryGameDidStart

Når denne metode køres, bør spillet starte (brugeren trykker på PLAY). Her genindlæser vi simpelthen indholdet ved at kalde collectionView.reloadData(), som vil blande kortene.

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

memoryGameShowCards

Vi kalder denne metode fra collectionSDViewSelectItemAt. Først viser det det valgte kort. Kontrollerer derefter, om der er et umatchet kort i arrayet med de viste kort (hvis antallet af de viste kort er ulige). Hvis der er et, sammenlignes det valgte kort med det. Hvis billederne er ens, føjes begge kort til de viste kort og forbliver med billedsiden opad. Hvis det er forskelligt, efterlader kortet de viste kort, og begge vendes med forsiden nedad.

memoryGameHideCards

Hvis kortene ikke stemmer overens, kaldes denne metode, og kortbillederne er skjult.

vist = falsk.

memoryGameDidEnd

Når denne metode kaldes, betyder det, at alle kort allerede er afsløret og er på listen over de viste kort: cardsShown.count = cards.count, så spillet er slut. Metoden kaldes specifikt efter at vi har kaldt endGame() for at sætte isPlaying var til false, hvorefter meddelelsen om spilslut vises. Også alertController bruges som indikator for controlleren. viewDidDisappear kaldes, og spillet nulstilles.

Sådan ser det hele ud i 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()
    }
}

At skrive et hukommelseskortspil i Swift
Faktisk er det alt. Du kan bruge dette projekt til at skabe din egen version af spillet.

God kodning!

Skillbox anbefaler:

Kilde: www.habr.com

Tilføj en kommentar