نوشتن یک بازی کارت حافظه در سوئیفت

نوشتن یک بازی کارت حافظه در سوئیفت

این مقاله روند ایجاد یک بازی آموزش حافظه ساده را شرح می دهد که من واقعاً از آن لذت می برم. علاوه بر این که به خودی خود خوب است، در حین حرکت کمی بیشتر در مورد کلاس ها و پروتکل های سوئیفت خواهید آموخت. اما قبل از شروع، بیایید خود بازی را درک کنیم.

یادآوری می کنیم: برای همه خوانندگان "Habr" - تخفیف 10 روبل هنگام ثبت نام در هر دوره Skillbox با استفاده از کد تبلیغاتی "Habr".

Skillbox توصیه می کند: دوره آموزشی آنلاین "توسعه دهنده حرفه ای جاوا".

نحوه بازی کارت حافظه

بازی با نمایش مجموعه ای از کارت ها آغاز می شود. آنها رو به پایین دراز می کشند (به ترتیب، تصویر پایین). وقتی روی هر کدام کلیک می کنید، تصویر برای چند ثانیه باز می شود.

وظیفه بازیکن این است که همه کارت ها را با تصاویر مشابه پیدا کند. اگر پس از باز کردن کارت اول، کارت دوم را برگردانید و تصاویر مطابقت داشتند، هر دو کارت باز می مانند. اگر مطابقت نداشتند، کارت ها دوباره بسته می شوند. هدف باز کردن همه چیز است.

ساختار پروژه

برای ایجاد یک نسخه ساده از این بازی به اجزای زیر نیاز دارید:

  • یک کنترلر: GameController.swift.
  • One View: CardCell.swift.
  • دو مدل: MemoryGame.swift و Card.swift.
  • Main.storyboard برای اطمینان از در دسترس بودن کل مجموعه اجزا.

ما با ساده ترین جزء بازی، کارت ها شروع می کنیم.

کارت.سوئیفت

مدل کارت دارای سه ویژگی است: id برای شناسایی هر یک، یک متغیر بولی نشان داده شده برای مشخص کردن وضعیت کارت (مخفی یا باز)، و artworkURL برای تصاویر روی کارت‌ها.

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

و در اینجا پیاده سازی کد مدل Card با تمام ویژگی ها و روش ها است.

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 تنظیم می کنیم. این مدل دارای ویژگی‌هایی مانند کارت‌ها (آرایه‌ای از کارت‌ها در یک شبکه)، آرایه کارت‌های نشان داده شده با کارت‌هایی که قبلاً باز شده‌اند، و یک متغیر بولی isPlaying برای ردیابی وضعیت بازی خواهد داشت.

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 را روی false قرار می دهیم و طرح اولیه کارت ها را حذف می کنیم.

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 نشان داده می شود. اگر هنوز با collectionView آشنا نیستید، اینجاست می توانید اطلاعات مورد نیاز خود را دریافت کنید.

ما GameController را به عنوان کنترلر اصلی برنامه پیکربندی می کنیم. GameController یک collectionView خواهد داشت که به عنوان یک IBOutlet به آن ارجاع می دهیم. مرجع دیگر دکمه IBAction onStartGame () است، این یک دکمه UIB است، می توانید آن را در استوری بورد به نام PLAY ببینید.

کمی در مورد پیاده سازی کنترلرها:

  • ابتدا، دو شی اصلی را مقداردهی می کنیم - شبکه: game = MemoryGame()، و مجموعه ای از کارت ها: cards = [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)
    }
}

حالا بیایید کمی در مورد پروتکل های مهم صحبت کنیم.

پروتکل ها

کار با پروتکل ها هسته اصلی برنامه نویسی سوئیفت است. پروتکل ها توانایی تعریف قوانین برای یک کلاس، ساختار یا شمارش را فراهم می کنند. این اصل به شما امکان می دهد کدهای ماژولار و قابل توسعه بنویسید. در واقع این الگویی است که ما در حال پیاده سازی آن برای collectionView در GameController هستیم. حالا بیایید نسخه خودمان را بسازیم. سینتکس به شکل زیر خواهد بود:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

ما می دانیم که یک پروتکل به ما اجازه می دهد تا قوانین یا دستورالعمل هایی را برای پیاده سازی یک کلاس تعریف کنیم، بنابراین بیایید در مورد آنها فکر کنیم. در کل به چهار مورد نیاز دارید.

  • شروع بازی: memoryGameDidStart.
  • باید کارت را رو به پایین بچرخانید: memoryGameShowCards.
  • باید کارت را رو به پایین بچرخانید: memoryGameHideCards.
  • پایان بازی: memoryGameDidEnd.

هر چهار متد را می توان برای کلاس اصلی که GameController است پیاده سازی کرد.

memoryGameDidStart

هنگامی که این روش اجرا می شود، بازی باید شروع شود (کاربر PLAY را فشار می دهد). در اینجا ما به سادگی محتوا را با فراخوانی () collectionView.reloadData بارگیری می کنیم، که کارت ها را به هم می زند.

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

MemoryGameShowCards

ما این متد را از collectionSDViewSelectItemAt می نامیم. ابتدا کارت انتخاب شده را نشان می دهد. سپس بررسی می کند که آیا کارت بی همتا در آرایه cardsShown وجود دارد (اگر تعداد کارت های نمایش داده شده فرد باشد). در صورت وجود، کارت انتخاب شده با آن مقایسه می شود. اگر تصاویر یکسان باشند، هر دو کارت به cardsShown اضافه می شوند و رو به بالا باقی می مانند. اگر متفاوت باشد، کارت cardsShown را می‌گذارد و هر دو رو به پایین می‌چرخند.

MemoryGameHideCards

اگر کارت ها مطابقت نداشته باشند، این روش فراخوانی می شود و تصاویر کارت پنهان می شوند.

نشان داده شده = نادرست

memoryGameDidEnd

هنگامی که این روش فراخوانی می شود، به این معنی است که همه کارت ها قبلاً آشکار شده اند و در لیست cardsShown قرار دارند: cardsShown.count = cards.count، بنابراین بازی تمام شده است. متد به طور خاص پس از فراخوانی endGame() فراخوانی می شود تا isPlaying var را روی false قرار دهیم و پس از آن پیام پایان بازی نشان داده می شود. همچنین alertController به عنوان نشانگر کنترلر استفاده می شود. 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

اضافه کردن نظر