Swift'de Hafıza Kartları Oyunu Yazmak

Swift'de Hafıza Kartları Oyunu Yazmak

Bu makale, gerçekten sevdiğim basit bir hafıza eğitimi oyunu oluşturma sürecini anlatıyor. Kendi içinde iyi olmanın yanı sıra, ilerledikçe Swift sınıfları ve protokolleri hakkında biraz daha fazlasını öğreneceksiniz. Ancak başlamadan önce oyunun kendisini anlayalım.

Hatırlatıyoruz: tüm "Habr" okuyucuları için - "Habr" promosyon kodunu kullanarak herhangi bir Skillbox kursuna kayıt olurken 10 ruble indirim.

Skillbox şunları önerir: Eğitici çevrimiçi kurs "Meslek Java geliştiricisi".

Hafıza Kartı nasıl oynanır?

Oyun bir dizi kartın gösterilmesiyle başlar. Yüzüstü uzanırlar (sırasıyla görüntü aşağı doğru). Herhangi birine tıkladığınızda resim birkaç saniyeliğine açılır.

Oyuncunun görevi aynı resimlere sahip tüm kartları bulmaktır. İlk kartı açtıktan sonra ikinciyi açarsanız ve resimler eşleşirse her iki kart da açık kalır. Eşleşmiyorlarsa kartlar tekrar kapatılır. Amaç her şeyi açmaktır.

Proje yapısı

Bu oyunun basit bir versiyonunu oluşturmak için aşağıdaki bileşenlere ihtiyacınız var:

  • Bir Denetleyici: GameController.swift.
  • Tek Görünüm: CardCell.swift.
  • İki Model: MemoryGame.swift ve Card.swift.
  • Main.storyboard, tüm bileşen setinin mevcut olduğundan emin olmak için.

Oyunun en basit bileşeni olan kartlarla başlıyoruz.

Kart.hızlı

Kart modelinin üç özelliği olacaktır: her birini tanımlayan kimlik, kartın durumunu (gizli veya açık) belirtmek için gösterilen bir boole değişkeni ve kartlardaki resimler için resim URL'si.

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

Haritalarla kullanıcı etkileşimini kontrol etmek için şu yöntemlere de ihtiyacınız olacak:

Bir görüntüyü kartta görüntüleme yöntemi. Burada tüm özellikleri varsayılana sıfırlıyoruz. Kimlik için NSUUIS().uuidString'i çağırarak rastgele bir kimlik oluştururuz.

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

Kimlik kartlarını karşılaştırma yöntemi.

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

Her kartın bir kopyasını oluşturma yöntemi - daha fazla sayıda özdeş olanı elde etmek için. Bu yöntem, benzer değerlere sahip kartı döndürecektir.

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

Ve başlangıçta kartları karıştırmak için bir yönteme daha ihtiyaç var. Bunu Array sınıfının bir uzantısı yapacağız.

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

Ve işte Kart modelinin kodunun tüm özellikleri ve yöntemleriyle uygulanması.

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

Devam et.

İkinci model ise MemoryGame, burada 4*4'lük bir grid ayarladık. Model, kartlar (bir ızgara üzerinde kart dizisi), zaten açık olan kartların bulunduğu birwardsShown dizisi ve oyunun durumunu izlemek için bir boolean isPlaying değişkeni gibi özelliklere sahip olacaktır.

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

Ayrıca kullanıcının grid ile etkileşimini kontrol edecek yöntemler geliştirmemiz gerekiyor.

Kartları bir ızgarada karıştıran bir yöntem.

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

Yeni bir oyun oluşturma yöntemi. Burada ilk düzeni başlatmak ve isPlaying değişkenini true olarak başlatmak için ilk yöntemi çağırıyoruz.

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

Oyunu yeniden başlatmak istersek, daha sonra isPlaying değişkenini false olarak ayarlıyoruz ve kartların ilk düzenini kaldırıyoruz.

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

Tıklanan kartları doğrulama yöntemi. Onun hakkında daha sonra daha fazla bilgi vereceğiz.

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

Belirli bir kartın konumunu döndüren bir yöntem.

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

Seçilen kartın standarda uygunluğu kontrol ediliyor.

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

Bu yöntem **cardsShown** dizisindeki son öğeyi okur ve eşleşmeyen kartı döndürür.

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

Main.storyboard şuna benzer:

Swift'de Hafıza Kartları Oyunu Yazmak

Başlangıçta, yeni oyunu, ızgara görüntüleri de dahil olmak üzere denetleyicide viewDidLoad olarak ayarlamanız gerekir. Oyunda tüm bunlar 4*4 koleksiyon Görünümü ile temsil edilecek. CollectionView'a henüz aşina değilseniz, işte burada ihtiyacınız olan bilgiyi alabilirsiniz.

GameController'ı uygulamanın kök denetleyicisi olarak yapılandıracağız. GameController, IBOutlet olarak başvuracağımız bir koleksiyon Görünümüne sahip olacaktır. Başka bir referans IBAction onStartGame() düğmesidir, bu bir UIButton'dur, bunu PLAY adlı storyboard'da görebilirsiniz.

Denetleyicilerin uygulanması hakkında biraz:

  • Öncelikle iki ana nesneyi başlatıyoruz: ızgara: oyun = MemoryGame() ve bir kart seti: kartlar = [Card]().
  • Başlangıç ​​değişkenlerini viewDidLoad olarak ayarladık, oyun çalışırken çağrılan ilk yöntem budur.
  • CollectionView gizli olarak ayarlanmıştır çünkü kullanıcı PLAY'e basana kadar tüm kartlar gizlidir.
  • PLAY tuşuna bastığımız anda onStartGame IBAction bölümü başlıyor ve kartların görünür olabilmesi için CollectionView isHidden özelliğini false olarak ayarlıyoruz.
  • Kullanıcı her kart seçtiğinde didSelectItemAt yöntemi çağrılır. Ana oyun mantığını uygulamak için didSelectCard dediğimiz yöntemde.

İşte son GameController uygulaması:

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

Şimdi biraz önemli protokollerden bahsedelim.

protokolleri

Protokollerle çalışmak Swift programlamanın temelidir. Protokoller bir sınıf, yapı veya numaralandırma için kuralları tanımlama yeteneği sağlar. Bu prensip modüler ve genişletilebilir kod yazmanıza olanak tanır. Aslında bu, GameController'da CollectionView için zaten uyguladığımız bir kalıptır. Şimdi kendi versiyonumuzu yapalım. Sözdizimi şöyle görünecek:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

Bir protokolün, bir sınıfı uygulamaya yönelik kuralları veya talimatları tanımlamamıza izin verdiğini biliyoruz, o halde bunların ne olması gerektiğini düşünelim. Toplamda dörde ihtiyacınız var.

  • Oyunun başlangıcı: MemoryGameDidStart.
  • Kartı ters çevirmeniz gerekiyor: MemoryGameShowCards.
  • Kartı ters çevirmeniz gerekiyor: MemoryGameHideCards.
  • Oyunun sonu: MemoryGameDidEnd.

Dört yöntemin tümü GameController olan ana sınıf için uygulanabilir.

hafızaOyunDidBaşlat

Bu yöntem çalıştırıldığında oyun başlamalıdır (kullanıcı PLAY'e basar). Burada, kartları karıştıracak olan CollectionView.reloadData()'yı çağırarak içeriği yeniden yükleyeceğiz.

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

hafızaOyunGösterKartlar

Bu yöntemi CollectionSDViewSelectItemAt'tan çağırıyoruz. İlk önce seçilen kartı gösterir. Daha sonra,wardsShown dizisinde eşleşmeyen bir kart olup olmadığını kontrol eder (eğercardShown sayısı tek ise). Varsa seçilen kart onunla karşılaştırılır. Resimler aynıysa, her iki kart da gösterilen kartlara eklenir ve açık olarak kalır. Farklıysa, kartta kartlar gösterilir ve her ikisi de ters çevrilir.

hafızaOyunGizleKartlar

Kartların eşleşmemesi durumunda bu yöntem çağrılır ve kart görselleri gizlenir.

gösterilen = yanlış.

hafızaOyunDidEnd

Bu yöntem çağrıldığında, tüm kartların zaten açık olduğu ve kartların Gösterildiği listede olduğu anlamına gelir: kartlarShown.count = kartlar.sayısı, yani oyun biter. Bu yöntem özellikle isPlaying var'ı false olarak ayarlamak için endGame() çağrıldıktan sonra çağrılır ve ardından oyun sonu mesajı gösterilir. Ayrıca warningController denetleyici için bir gösterge olarak kullanılır. viewDidDisappear çağrılır ve oyun sıfırlanır.

GameController'da her şey şöyle görünüyor:

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

Swift'de Hafıza Kartları Oyunu Yazmak
Aslında hepsi bu. Oyunun kendi versiyonunu oluşturmak için bu projeyi kullanabilirsiniz.

Mutlu kodlama!

Skillbox şunları önerir:

Kaynak: habr.com

Yorum ekle