Peter’s 100 task #4 誰是臥底

一群朋友相聚可共同遊玩的小遊戲

Tommy
彼得潘的 Swift iOS / Flutter App 開發教室
10 min readDec 10, 2020

--

平時與三五好友同聚,室內活動就以桌遊為主,上次露營時用網站玩了這款遊戲,心想自己也試著做出有趣的小遊戲,玩著自己做的遊戲,格外開心呢

構想

遊戲基本設定(人數、身份、題目)

可選擇玩家人數、臥底人數、是否加入白板(無題目)

隨機分配玩家身份與題目

玩家一一傳閱確認題目,平民題目與臥底題目依前頁設定之數量分配(玩家不知自己是平民還是臥底)

描述身份詞與投票規則說明

玩家依序描述自己的身份詞,描述完畢將投票決定最像臥底的人使其出局

投票環節

選擇大家最後決定的人選,顯示其身份,並判斷遊戲是否結束(臥底全出局則平民獲勝,所剩平民人數與臥底相同則臥底獲勝)

草擬畫面

使用figma勾勒腦中畫面,不過實際畫面有稍作修改。

APP實作

storyboard layout

設定各元件大小、位置、顏色, 設定autolayout、stackView

基本設定頁面

使用 textField+pickerView、switch、button

說明 textField+pickerView做人數的選擇

step1. 繼承UIPickerViewDelegate, UIPickerViewDataSource

採用UIPickerViewDataSource protocol 用於顯示pickerView的資料

採用UIPickerViewDelegate 用於選擇pickerView

step2. 建立玩家與臥底之pickerView物件及人數資料陣列

var playerPicker = UIPickerView()
var spyPicker = UIPickerView()
let playerNumberForSelect = [4, 5, 6, 7, 8, 9, 10]
let spyNumberForSelect = [1, 2, 3]
// 預設人數
var playerNumber = 4
var spyNumber = 1
var blankNumber = 0

玩家可選擇人數為4~10人,臥底可選擇1~3人

step3. 於viewDidLoad將textField inputView設定為pickerView, 設定delegate與dataSource來源為此viewController, 加入手勢(endEditing)

// 設定delegate, dataSource來源
playerPicker.dataSource = self
playerPicker.delegate = self
spyPicker.dataSource = self
spyPicker.delegate = self

// 設定textField inputView為pickerView
playerPickerTextField.inputView = playerPicker
spyPickerTextField.inputView = spyPicker

// 設定手勢(endEditing)
let tap = UITapGestureRecognizer(target: self, action: #selector(closeKeyboard))
// 將手勢加入view
view.addGestureRecognizer(tap)

step4. 實作繼承之protocol方法

func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}

設定pickerView區塊數量為1(就是可以捲動的部分)

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView == playerPicker {
return playerNumberForSelect.count
} else {
return spyNumberForSelect.count
}
}

設定兩個pickerView的列數分別為playerNumberForSelect陣列及spyNumberForSelect陣列之元素數量

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView == playerPicker {
return String(playerNumberForSelect[row])+"人"
} else {
return String(spyNumberForSelect[row])+"人"
}
}

設定pickerView每列的顯示標題為使用者選擇的人數

此func分為兩區塊(玩家人數選擇及臥底人數選擇),將人數顯示在textField上,並且存至變數playerNumber、spyNumber

使用IBSegueAction將資料傳遞至下一頁

init?(coder: NSCoder, playerNumber: Int, spyNumber: Int, blankNumber: Int) {
self.playerNumber = playerNumber
self.spyNumber = spyNumber
self.blankNumber = blankNumber
super.init(coder: coder)
}

需先在QuestionViewController宣告init?()

@IBSegueAction func showQuestion(_ coder: NSCoder) -> QuestionViewController? {
// 程式只有一行時可省略return
QuestionViewController(coder: coder, playerNumber: playerNumber, spyNumber: spyNumber, blankNumber: blankNumber)
}

接著藉由SegueAction做資料傳遞

傳閱題目頁面

在QuestionViewController接受前頁傳遞人數,將每位玩家之身份與對應題目隨機分配,並由每個玩家傳閱確認題目

自訂玩家、身份、題目三種型別

投票頁面

stackView問題

  • 問題:button在只有奇數為玩家,位置會跑掉
  • 說明:stackView內之元件,若使用isHidden屬性將其隱藏,APP將忽略其位置
  • 解決方法:將alpha設為1(原本擔心就算看不到也點得到,但後來peter告知看不到即無法點擊)

使用UIAlertController做投票確認

step1. 建立UIAlertController物件

let controller = UIAlertController(title: "", message: "確定要讓\(name)出局嗎?", preferredStyle: .alert)

preferredStyle有兩種可以選擇

.alert 直接跳出提示訊息

.actionSheet 由下往上彈出訊息

step2. 建立UIAlertAction物件(提示訊息下方的選項)

let confirmAction = UIAlertAction(title: "滾吧!", style: .default) { (_) in
self
.votedPlayer(index: index)
}

其中closure部分為handler: ((UIAlertAction) -> Void)? = nil)當作參數,該參數為選擇該選項後要做的事情,UIAlertAction參數沒用到,因此用(_)代替

step3. 呼叫.addAction()將建立的Action加入UIAlertController

controller.addAction(confirmAction)

step4. 呼叫present()顯示UIAlertController

present(controller, animated: true, completion: nil)

多個元件outlet、action

此頁需產生多個button、label且每個button都有對應的動作

用outlet collection將多個元件放在array裡面,元件將按照拉進去的順序存取

將每個button拉到同一個IBAction,用switch比對button物件

詳細介紹可參考peter的文章

目前已可以正常遊戲,但題庫尚屬不足,未來希望能直接從網路上抓題庫。

之後再新增自訂暱稱忘記題目功能

--

--