Episode 38 — 選擇題 App: 猜歌遊戲
完整操作
設定題目
新增一個file
在新的 file 裡面建立一個 structure
struct Question {var song: String //題目的歌曲
var answerIndex: Int //題目的答案
var options: [String] //題目的選項
var image: String //答案的圖片
}
設定題目
var chineseSongs = [
Question(song: "Without You", answerIndex: 2, options: ["Runaway", "Why You Gonna Lie", "Without You"], image: "withoutYou"),
Question(song: "帥到分手", answerIndex: 1, options: ["需要你的美","帥到分手","你是我的菜"], image: "帥到分手"),
Question(song: "刻在我心底的名字", answerIndex: 0, options: ["刻在我心底的名字", "魚仔", "早安! 晨之美"], image: "刻在我心底的名字"),
Question(song: "想見你想見你想見你", answerIndex: 2, options: ["東區東區", "最好的結局", "想見你想見你想見你"], image: "想見你"),
Question(song: "地球上最浪漫的一首歌", answerIndex: 0, options: ["地球上最浪漫的一首歌", "不屑", "愛的進行曲"], image: "地球上最浪漫的一首歌"),
Question(song: "還是會", answerIndex: 1, options: ["有人在等我", "還是會", "心醉心碎"], image: "還是會"),
Question(song: "怎麼了", answerIndex: 1, options: ["Room for You", "怎麼了", "終於了解自由"], image: "怎麼了"),
Question(song: "路過人間", answerIndex: 2, options: ["微加幸福", "指望", "路過人間"], image: "路過人間"),
Question(song: "光之海", answerIndex: 0, options: ["光之海", "有一種悲傷", "幸福了 然後呢"], image: "光之海"),
Question(song: "Easy Come Easy Go", answerIndex: 1, options: ["紅色高跟鞋", "Easy Come Easy Go", "達爾文"], image: "easyComeEasyGo")
]
在建立 structure 的外面設定題目,每一個 quesiton 都要有題目歌曲、答案、題目選項跟音樂圖片
製作 storyboard
到時候適時的用 isHidden 來隱藏及顯示元件
建立 IBAction
@IBOutlet weak var gameView: UIView!
@IBOutlet weak var nextButton: UIButton! //下一題
@IBOutlet weak var playButton: UIButton! //播放鍵
@IBOutlet weak var keepPlayingLabel: UILabel! //繼續播放提示
@IBOutlet weak var checkResultButton: UIButton! //看結果鍵
@IBOutlet weak var pauseButton: UIButton! //暫停鍵
@IBOutlet weak var nextLabel: UILabel! //下一首提示
@IBOutlet var optionButtons: [UIButton]! //選擇題鍵
@IBOutlet weak var resultLabel: UILabel! //答題後提示
@IBOutlet weak var songImageView: UIImageView! //答案圖片
回到原本的 controller 建立
選擇題鍵有三個,先隨便連一個按鈕建立 outlet collection 形成 array。再將其他兩個按鈕連進來。
var index = 0
先宣告一個題目數字
func setTheGame(songType: [Question], totalSongs: Int, options: [String], image: String){
music(type: songType, index: index)
for i in 0...2 {
optionButtons[i].setTitle(options[i], for: .normal)
}
setTime()
displayTime()
}
再宣告了一個顯示每一題題目的方法,先使用 music function 來呼叫歌曲,再用 for 迴圈來設定該題目歌曲的選項到三個按鈕上。利用 setTitle 的方式幫按鈕上名字。
音樂播放
import AVFoundation
輸入一個能播放音樂的資料庫
let player = AVPlayer()
建立一個播放音樂的 player
加入 mp3 檔
Copy items if needed 記得勾選
func music(type: [Question], index: Int) {
let fileURL = Bundle.main.url(forResource: type[index].song, withExtension: "mp3")
let playerItem = AVPlayerItem(url: fileURL!)
player.replaceCurrentItem(with: playerItem)
}
宣告一個儲存待播音樂的方法,這邊有一個參數傳入 [Question],可以在 forResource 那邊呼叫歌曲。
@IBAction func play(_ sender: UIButton) {
switch sender {
case pauseButton:
player.pause() //暫停播放
pauseButton.isHidden = true
playButton.isHidden = false
default:
player.play() //開始播放
playButton.isHidden = true
pauseButton.isHidden = false
}
setTime()
displayTime()
}
將播放鍵即暫停鍵連成同一個 IBAction,然後使用 switch 來判斷按的是播放鍵還是暫停鍵。
顯示下一題
@IBAction func next(_ sender: UIButton) {
nextButton.isHidden = true
nextLabel.isHidden = true
keepPlayingLabel.isHidden = true
index += 1
if isChinese {
setTheGame(songType: chineseSongs, totalSongs: chineseSongs.count, options: chineseSongs[index].options, image: chineseSongs[index].image)
} else if isEnglish {
setTheGame(songType: engilshSongs, totalSongs: engilshSongs.count, options: engilshSongs[index].options, image: engilshSongs[index].image)
}
pauseButton.isHidden = false
playButton.isHidden = true
songImageView.isHidden = true
resultLabel.text = ""
player.play()
}
按下下一題的按鈕後,index 除了加一以外,還要利用加一後的 index 來取得下一首歌的值。
歌曲時間顯示
@objc func displayTime() {
let remainTime = String(format: "%.0f", (Double(180)-player.currentTime().seconds))
remainMinuteText = String(Int(remainTime)!/60)
remainSecondText = String( Int(remainTime)! % 60)
if Int(remainSecondText)! < 10 { remainTimeLabel.text = "0\(remainMinuteText):0\(remainSecondText)"
} else { remainTimeLabel.text = "0\(remainMinuteText):\(remainSecondText)"
}
}
利用播放器目前的時間來顯示到數。因為我的歌都只有三分鐘所以我直接用 180 秒去剪掉播放器目前讀到的秒數。用 除以 60 獲得分鐘數、用 60 求餘數得到秒數。我這邊有歌缺點是有時候用 ! 去硬拆會是 nil ,可能還要再研究一下更好的用法。
func setTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(displayTime) , userInfo: nil, repeats: true)
}
設定到數計時器,並選擇剛剛建立的時間顯示方法去執行。
控制音量
@IBAction func controlVolume(_ sender: UISlider) {
player.volume = sender.value
}
創建一個 slider 的 IBAction,把 slider 的值給 player 的音量即可
建立主頁面及改變題目順序
建立 storyboard
這個是玩遊戲前出現的選擇畫面,所以選擇題的 view 在他下面。
var isChinese = false
var isEnglish = false
我宣告兩個變數來判斷是中文歌曲還是英文歌曲
@IBAction func shuffleSongs(_ sender: UIButton) {
playButton.isHidden = false
switch sender {
case chineseSongsButton:
chineseSongs.shuffle() //當選到中文歌曲就開始洗牌題目
isChinese = true
setTheGame(songType: chineseSongs, totalSongs: chineseSongs.count, options: chineseSongs[index].options, image: chineseSongs[index].image)
case engilshSongsButton:
engilshSongs.shuffle() //當選到英文歌曲就開始洗牌題目
isEnglish = true
setTheGame(songType: engilshSongs, totalSongs: engilshSongs.count, options: engilshSongs[index].options, image: engilshSongs[index].image)
default:
index += 0
}
player.play()
displayTime()
gameView.isHidden = false
songTypeView.isHidden = true
pauseButton.isHidden = false
}
利用 .shuffle() 可以將矩陣裡的值洗牌
答對或答錯
var correctCount = 0
var wrongCount = 0
宣告對錯數量的變數
func addCount(answerIndex: Int, songName: String, buttonIndex: Int, imageName: String) {
if answerIndex == buttonIndex {
resultLabel.text = "Congradulations! 你答對了!"
correctCount += 1
} else {
resultLabel.text = "Oops! 這首歌是 \(songName)"
wrongCount += 1
}
songImageView.image = UIImage(named: imageName )
}
宣告一個增加對錯數目的方法。參數中,answerIndex 要取得題目的正確答案。songName 要取得播放音樂。buttonIndex 要取得選擇按鈕的代號。imageName 要取得該首歌的圖片。
@IBAction func checkAnswer(_ sender: UIButton) {
songImageView.isHidden = false
switch sender {
case optionButtons[0]:
if isChinese {
addCount(answerIndex: chineseSongs[index].answerIndex, songName: chineseSongs[index].song, buttonIndex: 0, imageName: chineseSongs[index].image)
} else if isEnglish {
addCount(answerIndex: engilshSongs[index].answerIndex, songName: engilshSongs[index].song, buttonIndex: 0, imageName: engilshSongs[index].image)
}
case optionButtons[1]:
// 依此類推
default:
//依此類推
}
.....
將三個選擇按鈕連成同一個IBAction,用選擇項目的按鈕來判斷是否正確。當按到第一個按鈕,先分辨是中文還是英文歌曲,再將該歌曲的相關屬性丟進 addCount 當參數去增加對錯數量及顯示答案圖片。
遊戲結果
製作storyboard
這個是遊戲結果頁面,我把它擺最下面。
建立 IBOutlet
@IBOutlet weak var scoreView: UIView! //顯示結果的頁面@IBOutlet weak var scoreLabel: UILabel! //顯示對錯題數@IBOutlet weak var trophyImageView: UIImageView! //顯示獲得的獎項@IBOutlet weak var descriptionLabel: UILabel! //顯示描述內容@IBOutlet weak var playAgainButton: UIButton! //再玩一次的按鈕
建立IBAction
@IBAction func checkResult(_ sender: Any) {
scoreView.isHidden = false
gameView.isHidden = true
scoreLabel.text = "答對 \(correctCount) 題, 答錯 \(wrongCount) 題"
if correctCount < 3 {
descriptionLabel.text = "你需要多聽歌!!"
} else if correctCount < 6 {
descriptionLabel.text = "還可以 有在聽歌\n 獲得銅色音符"
trophyImageView.image = UIImage(named: copperImages.randomElement()!)
addImage(image: copperImages, collection: copperCollection)
} else if correctCount < 9 {
descriptionLabel.text = "不錯嘛 很常聽歌\n 獲得銀色音符"
trophyImageView.image = UIImage(named: silverImages.randomElement()!)
addImage(image: silverImages, collection: silverCollection)
} else {
descriptionLabel.text = "猜歌大神\n 獲得金色音符"
trophyImageView.image = UIImage(named: goldenImages.randomElement()!)
addImage(image: goldenImages, collection: goldenCollection)
}
}
選擇題玩到最後一題會跳出結果按鈕,點擊按鈕即顯示結果。利用 correctCount 跟 wrongCount 來描述對錯幾題。用 if else 來判斷對的數量及顯示內容。
加入徽章
var copperImages = ["copperNote1", "copperNote2"]var goldenImages = ["goldenNote1", "goldenNote2", "goldenNote3", "goldenNote4"]var silverImages = ["silverNote1", "silverNote2", "silverNote3", "silverNote4"]
分別儲存金銀銅色音符的照片
製作 storyboard
每次玩完遊戲會發一個顏色的音符徽章,該徽章會顯示在這個頁面。
建立 IBOutlet
@IBOutlet weak var trophyView: UIView! //獎章搜集頁面@IBOutlet var goldenCollection: [UIImageView]! //金色音符框@IBOutlet var silverCollection: [UIImageView]! //銀色音符框@IBOutlet var copperCollection: [UIImageView]! //銅色音符框@IBOutlet weak var backToTypeButton: UIButton! //反會按鈕func addImage(image: [String], collection: [UIImageView]){
var num = 3
if collection == copperCollection { num = 1
}
for index in 0...num {
if trophyImageView.image == UIImage(named: image[index]) {
collection[index].image = UIImage(named: image[index]) }
}
}
宣告一個把徽章加入圖庫的方法,參數 image 取得照片名稱的 array,collection 要取得圖框的 array。使用 for 迴圈並用 if else 判斷抽到的徽章是否等於該圖框的徽章。
完整程式碼
<view controller>
<GameMode>