كتابة لعبة بطاقات الذاكرة في سويفت

كتابة لعبة بطاقات الذاكرة في سويفت

توضح هذه المقالة عملية إنشاء لعبة بسيطة لتدريب الذاكرة والتي أحبها حقًا. إلى جانب كونها جيدة في حد ذاتها، ستتعلم المزيد عن فئات وبروتوكولات Swift أثناء تقدمك. ولكن قبل أن نبدأ، دعونا نفهم اللعبة نفسها.

نذكر: لجميع قراء "Habr" - خصم 10 روبل عند التسجيل في أي دورة Skillbox باستخدام رمز "Habr" الترويجي.

يوصي Skillbox بما يلي: دورة تعليمية عبر الإنترنت "مهنة مطور جافا".

كيفية تشغيل بطاقة الذاكرة

تبدأ اللعبة بعرض مجموعة من البطاقات. إنهم يرقدون ووجههم لأسفل (على التوالي، الصورة لأسفل). عند النقر على أي واحد، يتم فتح الصورة لبضع ثوان.

مهمة اللاعب هي العثور على جميع البطاقات التي تحتوي على نفس الصور. إذا قمت، بعد فتح البطاقة الأولى، بقلب الثانية وتطابقت الصور، تظل كلتا البطاقتين مفتوحتين. إذا لم تكن متطابقة، يتم إغلاق البطاقات مرة أخرى. الهدف هو فتح كل شيء.

هيكل المشروع

لإنشاء نسخة بسيطة من هذه اللعبة، تحتاج إلى المكونات التالية:

  • وحدة تحكم واحدة: GameController.swift.
  • عرض واحد: CardCell.swift.
  • نموذجان: MemoryGame.swift وCard.swift.
  • Main.storyboard للتأكد من توفر مجموعة المكونات بأكملها.

نبدأ بأبسط مكونات اللعبة، وهي البطاقات.

بطاقة. سويفت

سيحتوي نموذج البطاقة على ثلاث خصائص: معرف لتعريف كل واحدة، ومتغير منطقي يظهر لتحديد حالة البطاقة (مخفية أو مفتوحة)، وعنوان URL للصور الموجودة على البطاقات.

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

ستحتاج أيضًا إلى هذه الطرق للتحكم في تفاعل المستخدم مع الخرائط:

طريقة عرض الصورة على البطاقة. هنا نقوم بإعادة تعيين جميع الخصائص إلى الوضع الافتراضي. بالنسبة للمعرف، نقوم بإنشاء معرف عشوائي عن طريق استدعاء NSUUIS().uuidString.

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)    
}
 
init(card: Card) {        
    self.id = card.id        
    self.shown = card.shown        
    self.artworkURL = card.artworkURL    
}

وهناك حاجة إلى طريقة أخرى لخلط الأوراق في البداية. سنجعله امتدادًا لفئة Array.

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

وهنا يتم تنفيذ الكود الخاص بنموذج البطاقة بجميع الخصائص والأساليب.

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

المضي قدما.

النموذج الثاني هو MemoryGame، وهنا قمنا بتعيين شبكة 4*4. سيحتوي النموذج على خصائص مثل البطاقات (مصفوفة من البطاقات على الشبكة)، ومصفوفة البطاقات المعروضة مع البطاقات المفتوحة بالفعل، والمتغير المنطقي قيد التشغيل لتتبع حالة اللعبة.

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

نحتاج أيضًا إلى تطوير طرق للتحكم في تفاعل المستخدم مع الشبكة.

طريقة خلط البطاقات في الشبكة.

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

طريقة صنع لعبة جديدة. هنا نسمي الطريقة الأولى لبدء التخطيط الأولي وتهيئة متغير isPlaying إلى true.

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

إذا أردنا إعادة تشغيل اللعبة، ثم قمنا بتعيين متغير isPlaying على خطأ وإزالة التخطيط الأولي للبطاقات.

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

طريقة التحقق من البطاقات التي تم النقر عليها. المزيد عنه لاحقا.

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

طريقة تقوم بإرجاع موضع بطاقة معينة.

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

التحقق من امتثال البطاقة المحددة للمعيار.

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

تقرأ هذه الطريقة العنصر الأخير في مصفوفة **cardsShown** وترجع البطاقة غير المطابقة.

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

يبدو Main.storyboard شيئًا مثل هذا:

كتابة لعبة بطاقات الذاكرة في سويفت

في البداية، تحتاج إلى تعيين اللعبة الجديدة كـ viewDidLoad في وحدة التحكم، بما في ذلك الصور الخاصة بالشبكة. في اللعبة، سيتم تمثيل كل هذا من خلال عرض مجموعة 4*4. إذا لم تكن على دراية بـ CollectionView بعد، فها هو يمكنك الحصول على المعلومات التي تحتاجها.

سنقوم بتكوين وحدة التحكم GameController لتكون وحدة التحكم الجذرية للتطبيق. سيكون لدى GameController عرض مجموعة سنشير إليه باعتباره IBOutlet. مرجع آخر هو زر IBAction onStartGame()، وهو زر UIButton، يمكنك رؤيته في لوحة العمل التي تسمى PLAY.

قليلا عن تنفيذ وحدات التحكم:

  • أولاً، نقوم بتهيئة كائنين رئيسيين - الشبكة: اللعبة = MemoryGame()، ومجموعة من البطاقات: البطاقات = [Card]().
  • قمنا بتعيين المتغيرات الأولية على أنها viewDidLoad، وهذه هي الطريقة الأولى التي يتم استدعاؤها أثناء تشغيل اللعبة.
  • تم تعيين CollectionView على مخفي لأن جميع البطاقات مخفية حتى يضغط المستخدم على PLAY.
  • بمجرد الضغط على PLAY، يبدأ قسم onStartGame IBAction، وقمنا بتعيين خاصية CollectionView isHidden على false حتى تصبح البطاقات مرئية.
  • في كل مرة يقوم المستخدم بتحديد بطاقة، يتم استدعاء الأسلوب didSelectItemAt. في الطريقة نسميها didSelectCard لتنفيذ منطق اللعبة الرئيسي.

إليك التطبيق النهائي لـ 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)
    }
}

الآن دعونا نتحدث قليلا عن البروتوكولات الهامة.

بروتوكولات

العمل مع البروتوكولات هو جوهر برمجة Swift. توفر البروتوكولات القدرة على تحديد قواعد للفئة أو البنية أو التعداد. يتيح لك هذا المبدأ كتابة تعليمات برمجية معيارية وقابلة للتوسيع. في الواقع، هذا هو النمط الذي قمنا بتنفيذه بالفعل لـ CollectionView في GameController. الآن دعونا نصنع نسختنا الخاصة. سيبدو بناء الجملة كما يلي:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

نحن نعلم أن البروتوكول يسمح لنا بتحديد القواعد أو التعليمات لتنفيذ الفصل الدراسي، لذلك دعونا نفكر فيما ينبغي أن تكون عليه. أنت بحاجة إلى أربعة في المجموع.

  • بداية اللعبة: MemoryGameDidStart.
  • تحتاج إلى قلب البطاقة ووجهها لأسفل: MemoryGameShowCards.
  • تحتاج إلى قلب البطاقة ووجهها لأسفل: MemoryGameHideCards.
  • نهاية اللعبة: MemoryGameDidEnd.

يمكن تنفيذ جميع الطرق الأربع للفئة الرئيسية، وهي GameController.

MemoryGameDidStart

عند تشغيل هذه الطريقة، يجب أن تبدأ اللعبة (يضغط المستخدم على PLAY). هنا سنقوم ببساطة بإعادة تحميل المحتوى عن طريق استدعاء CollectionView.reloadData()، والذي سيعمل على خلط البطاقات.

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

MemoryGameShowCards

نحن نسمي هذه الطريقة من المجموعةSDViewSelectItemAt. أولا يظهر البطاقة المحددة. ثم يتحقق لمعرفة ما إذا كانت هناك بطاقة غير متطابقة في مصفوفة البطاقات المعروضة (إذا كان عدد البطاقات المعروضة فرديًا). إذا كان هناك واحدة، تتم مقارنة البطاقة المحددة معها. إذا كانت الصور هي نفسها، تتم إضافة كلا البطاقتين إلى البطاقات المعروضة وتبقى مكشوفة. إذا كانت مختلفة، فستترك البطاقة البطاقات المعروضة ويتم قلب كليهما لأسفل.

MemoryGameHideCards

إذا كانت البطاقات غير متطابقة، يتم استدعاء هذه الطريقة ويتم إخفاء صور البطاقة.

مبين = خطأ.

MemoryGameDidEnd

عندما يتم استدعاء هذه الطريقة، فهذا يعني أن جميع البطاقات قد تم الكشف عنها بالفعل وهي موجودة في قائمة البطاقات المعروضة: CardShown.count =cards.count، وبالتالي تنتهي اللعبة. يتم استدعاء الطريقة على وجه التحديد بعد أن قمنا باستدعاء endGame()‎ لتعيين قيمة isPlaying var إلى false، وبعد ذلك تظهر رسالة نهاية اللعبة. كما يتم استخدام تنبيه وحدة التحكم كمؤشر لوحدة التحكم. يتم استدعاء viewDidDisappear ويتم إعادة ضبط اللعبة.

إليك ما يبدو عليه كل شيء في 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()
    }
}

كتابة لعبة بطاقات الذاكرة في سويفت
في الواقع، هذا كل شيء. يمكنك استخدام هذا المشروع لإنشاء نسختك الخاصة من اللعبة.

ترميز سعيد!

يوصي Skillbox بما يلي:

المصدر: www.habr.com

إضافة تعليق