本文描述了創造我真正喜歡的簡單記憶訓練遊戲的過程。 除了本身的優勢之外,您還將在學習過程中了解更多有關 Swift 類別和協議的知識。 但在開始之前,讓我們先了解遊戲本身。
提醒: 對於“Habr”的所有讀者 - 使用“Habr”促銷代碼註冊任何 Skillbox 課程可享受 10 盧布的折扣。
技能箱推薦: 教育在線課程
《職業Java開發人員》 .
如何玩記憶卡
遊戲從展示一組紙牌開始。 他們面朝下躺著(分別是圖像朝下)。 當您單擊任何一個時,圖像會打開幾秒鐘。
玩家的任務是找到所有具有相同圖片的卡片。 如果打開第一張卡片後,翻開第二張卡片並且圖片匹配,則兩張卡片都保持打開狀態。 如果不匹配,卡片將再次關閉。 目標是打開一切。
項目結構
要創建該遊戲的簡單版本,您需要以下組件:
- 一個控制器:GameController.swift。
- 一種視圖:CardCell.swift。
- 兩個模型:MemoryGame.swift 和 Card.swift。
- Main.storyboard 確保整套組件可用。
我們從遊戲中最簡單的組成部分——紙牌開始。
swift卡
卡片模型將具有三個屬性:用於標識每張卡片的 id、用於指定卡片狀態(隱藏或開啟)的布林變數 shown 以及用於卡片上圖像的artworkURL。
class Card {
var id: String
var shown: Bool = false
var artworkURL: UIImage!
}
您還需要這些方法來控制使用者與地圖的互動:
在卡片上顯示圖像的方法。 這裡我們將所有屬性重設為預設值。 對於id,我們透過呼叫NSUUIS().uuidString來產生一個隨機id。
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的網格。 該模型將具有諸如卡片(網格上的卡片數組)、包含已開啟的卡片的 cardsShown 數組以及用於追蹤遊戲狀態的布林變數 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() 按鈕,這是一個 UIButton,您可以在名為 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)
}
}
現在我們來談談重要的協議。
通訊協定
使用協定是 Swift 程式設計的核心。 協議提供了為類別、結構或枚舉定義規則的能力。 這項原則允許您編寫模組化和可擴展的程式碼。 事實上,這是我們已經在 GameController 中為 collectionView 實作的模式。 現在讓我們來製作自己的版本。 文法如下:
protocol MemoryGameProtocol {
//protocol definition goes here
}
我們知道協議允許我們定義用於實現類別的規則或指令,所以讓我們考慮一下它們應該是什麼。 你總共需要四個。
- 遊戲開始:memoryGameDidStart。
- 您需要將卡片面朝下:memoryGameShowCards。
- 您需要將卡片面朝下:memoryGameHideCards。
- 遊戲結束:記憶體GameDidEnd。
所有四個方法都可以在主類別 GameController 中實作。
記憶體遊戲已開始
執行此方法時,遊戲應該開始(使用者按下“PLAY”)。 在這裡,我們只需呼叫 collectionView.reloadData() 來重新載入內容,這將會洗牌。
func memoryGameDidStart(_ game: MemoryGame) {
collectionView.reloadData()
}
記憶遊戲秀卡
我們從 collectionSDViewSelectItemAt 呼叫此方法。 首先它顯示所選的卡片。 然後檢查cardsShown數組中是否有不匹配的卡片(如果cardShown的數量是奇數)。 如果有,則將所選卡與其進行比較。 如果圖片相同,則兩張卡片都會添加到 cardsShown 中並保持面朝上。 如果不同,則該卡將保持顯示狀態,並將兩者都翻面朝下。
記憶遊戲隱藏卡
如果卡片不匹配,則呼叫此方法並隱藏卡片圖像。
顯示=假。
記憶遊戲結束
當這個方法被呼叫時,意味著所有的牌都已經被揭開並且在cardsShown列表中:cardsShown.count = cards.count,所以遊戲結束。 方法在我們調用 endGame() 將 isPlaying 變數設定為 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()
}
}
事實上,僅此而已。 您可以使用此項目來創建您自己的遊戲版本。
快樂編碼!
技能箱推薦:
- 實踐課程
“移動開發者專業版” .- 應用在線課程
《Python資料分析師》 .- 兩年實踐課程
“我是專業網頁開發人員” .
來源: www.habr.com