Peter’s 100 task #4 誰是臥底
一群朋友相聚可共同遊玩的小遊戲
平時與三五好友同聚,室內活動就以桌遊為主,上次露營時用網站玩了這款遊戲,心想自己也試著做出有趣的小遊戲,玩著自己做的遊戲,格外開心呢
構想
遊戲基本設定(人數、身份、題目)
可選擇玩家人數、臥底人數、是否加入白板(無題目)
隨機分配玩家身份與題目
玩家一一傳閱確認題目,平民題目與臥底題目依前頁設定之數量分配(玩家不知自己是平民還是臥底)
描述身份詞與投票規則說明
玩家依序描述自己的身份詞,描述完畢將投票決定最像臥底的人使其出局
投票環節
選擇大家最後決定的人選,顯示其身份,並判斷遊戲是否結束(臥底全出局則平民獲勝,所剩平民人數與臥底相同則臥底獲勝)
草擬畫面
使用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的文章
目前已可以正常遊戲,但題庫尚屬不足,未來希望能直接從網路上抓題庫。
之後再新增自訂暱稱及忘記題目功能
參考資料
-Github