#18 撞球計分板(Scoreboard)

Lou
彼得潘的 Swift iOS / Flutter App 開發教室
15 min readMar 22, 2023

製作撞球14-1規則的計分板

SetViewController 資料傳遞到計分板畫面

可設定名稱 獲勝分數
import UIKit

class SetViewController: UIViewController {

@IBOutlet weak var playerATextField: UITextField!
@IBOutlet weak var playerBTextField: UITextField!
@IBOutlet weak var gameScoreTextField: UITextField!

override func viewDidLoad() {
super.viewDidLoad()

}



//點選空白處收鍵盤
@IBAction func closeKeyboard(_ sender: Any) {
view.endEditing(true)
}

//鍵盤按下return鍵收鍵盤
@IBAction func returnCloseKeyboard(_ sender: Any) {
}


//segue傳遞資料到計分板頁面
@IBSegueAction func showResult(_ coder: NSCoder) -> ScoreViewController? {

let setPlayerAName = String(playerATextField.text!)
let setPlayerBName = String(playerBTextField.text!)
let setGameScore = Int(gameScoreTextField.text!)

let controller = ScoreViewController(coder: coder)

controller?.playerAName = setPlayerAName
controller?.playerBName = setPlayerBName
controller?.gameScore = setGameScore


return controller
}

ScoreViewController 計分板介面

計分板介面

計時功能

var timer: Timer?
var counter = 0

override func viewDidLoad() {
super.viewDidLoad()

timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)

}

@objc func updateTime(){
counter += 1
timeLabel.text = formatedTime(counter)
}

//顯示時間格式
func formatedTime(_ seconds: Int) -> String {
var hourSting = String()
var minString = String()
var secString = String()

if seconds / 3600 == 0 {
hourSting = "00"
} else if (seconds / 3600 > 0 && seconds / 3600 < 10 ){
hourSting = "0\(seconds / 3600)"
} else {
hourSting = "\(seconds / 3600)"
}

if seconds / 60 == 0 {
minString = "00"
} else if (seconds / 60 > 0 && seconds / 60 < 10){
minString = "0\(seconds / 60)"
} else {
minString = "\(seconds / 60)"
}

if seconds % 60 == 0 {
secString = "00"
} else if (seconds % 60 < 10){
secString = "0\(seconds % 60)"
} else {
secString = "\(seconds % 60)"
}

return "比賽時間:\n\(hourSting):\(minString):\(secString)"

}

計分功能

進球加分 犯規扣分 下一局統計總分
    //A隊加分
@IBAction func addScoreA(_ sender: Any) {

//單局雙方合計未達13分直接計分
if scoreA + scoreB < 13 {
scoreA += 1
playerABallView.frame.origin.y = CGFloat(60 + 40 * scoreA)
//Array紀錄得分扣分
getScoreArray.append(playerAScore)
} else if scoreA + scoreB < 14 {
//單局合計14分 計算雙方總分並顯示
scoreA += 1
playerABallView.frame.origin.y = CGFloat(60 + 40 * scoreA)
roundScoreA = roundScoreA + scoreA - foulScoreA
roundScoreB = roundScoreB + scoreB - foulScoreB
scoreALabel.text = roundScoreA.formatted()
scoreBLabel.text = roundScoreB.formatted()
getScoreArray.append(playerAScore)
}
//有設定獲勝分數 達標後會有獲勝提示
if gameScore != nil && gameScore > 0{
if scoreA + scoreB < 14 && (roundScoreA + scoreA - foulScoreA) == gameScore {
showWinner()
} else if scoreA + scoreB == 14 && roundScoreA == gameScore {
showWinner()
}


}

//B隊加分
@IBAction func addScoreB(_ sender: Any) {

if scoreA + scoreB < 13 {
scoreB += 1
playerBBallView.frame.origin.y = CGFloat(60 + 40 * scoreB)
getScoreArray.append(playerBScore)
} else if scoreA + scoreB < 14 {
scoreB += 1
playerBBallView.frame.origin.y = CGFloat(60 + 40 * scoreB)
roundScoreA = roundScoreA + scoreA - foulScoreA
roundScoreB = roundScoreB + scoreB - foulScoreB
scoreALabel.text = roundScoreA.formatted()
scoreBLabel.text = roundScoreB.formatted()
getScoreArray.append(playerBScore)
}

if gameScore != nil && gameScore > 0{
if scoreA + scoreB < 14 && (roundScoreB + scoreB - foulScoreB) == gameScore {
showWinner()
} else if scoreA + scoreB == 14 && roundScoreB == gameScore {
showWinner()
}
}
}

//A隊犯規扣分
@IBAction func reduceScoreA(_ sender: Any) {

if scoreA + scoreB != 14 {
foulScoreA += 1
foulALabel.text = foulScoreA.formatted()
getScoreArray.append(playerAFoul)
}
}


//B隊犯規扣分
@IBAction func reduceScoreB(_ sender: Any) {

if scoreA + scoreB != 14 {
foulScoreB += 1
foulBLabel.text = foulScoreB.formatted()
getScoreArray.append(playerBFoul)
}
}

下一局按鍵

14-1打完球檯上14顆球 重新排球計分進入下一局

    @IBAction func changeNextGame(_ sender: Any) {

//達到雙方進球總和14分 重置單局分數及得分圖示初始位置
if scoreA + scoreB == 14 {
scoreA = 0
scoreB = 0
foulScoreA = 0
foulScoreB = 0
rounds += 1
foulALabel.text = foulScoreA.formatted()
foulBLabel.text = foulScoreB.formatted()
playerABallView.frame.origin.y = CGFloat(60)
playerBBallView.frame.origin.y = CGFloat(60)
totalScoreAArray.append(roundScoreA)
totalScoreBArray.append(roundScoreB)
getScoreArray.removeAll()
}
}

恢復上一步功能

計分錯誤 可以恢復上ㄧ步
@IBAction func rewind(_ sender: Any) {
//利用Array.popLast找出最後一步的操作
let lastScore = getScoreArray.popLast()
//判斷條件後 恢復上一步
switch lastScore {

case playerAScore:
if scoreA + scoreB == 14 {
scoreA -= 1
playerABallView.frame.origin.y = CGFloat(60 + 40 * scoreA)
roundScoreA = totalScoreAArray[rounds]
roundScoreB = totalScoreBArray[rounds]
scoreALabel.text = roundScoreA.formatted()
scoreBLabel.text = roundScoreB.formatted()

} else {
scoreA -= 1
playerABallView.frame.origin.y = CGFloat(60 + 40 * scoreA)
}

case playerBScore:
if scoreA + scoreB == 14 {
scoreB -= 1
playerBBallView.frame.origin.y = CGFloat(60 + 40 * scoreB)
roundScoreA = totalScoreAArray[rounds]
roundScoreB = totalScoreBArray[rounds]
scoreALabel.text = roundScoreA.formatted()
scoreBLabel.text = roundScoreB.formatted()

} else {
scoreB -= 1
playerBBallView.frame.origin.y = CGFloat(60 + 40 * scoreB)
}

case playerAFoul:
foulScoreA -= 1
foulALabel.text = foulScoreA.formatted()

case playerBFoul:
foulScoreB -= 1
foulBLabel.text = foulScoreB.formatted()

default:
break
}

獲勝提示

分數達標 獲勝提示
func showWinner(){
var winnerName = String()
//判斷雙方總分
if (roundScoreA + scoreA - foulScoreA) > (roundScoreB + scoreB - foulScoreB) {
winnerName = playerALabel.text!
} else {
winnerName = playerBLabel.text!
}

//使用UIAlert
let controller = UIAlertController(title: "恭喜\(winnerName)獲勝", message: "🏆🏆🏆🏆🏆🏆", preferredStyle: .alert)
let okAction = UIAlertAction(title: "重置比賽", style: .default){
_ in
//重置比賽
self.resetGame()
}
controller.addAction(okAction)
present(controller, animated: true)

}

重置比賽功能

重置比賽
func initialUI(){

//SetViewController傳遞的資料如果空白 設為預設值
if playerAName != "" {
playerALabel.text = playerAName
} else {
playerALabel.text = "A(先攻)"
}

if playerBName != ""{
playerBLabel.text = playerBName
} else {
playerBLabel.text = "B(後攻)"
}

if gameScore != nil {
gameScoreLabel.text = "獲勝總分:\( gameScore!.formatted())"
} else {
gameScoreLabel.text = "單局計分"
}

}


//重置功能
func resetGame(){

initialUI()
rounds = 0
scoreA = 0
scoreB = 0
foulScoreA = 0
foulScoreB = 0
roundScoreA = 0
roundScoreB = 0
totalScoreAArray = [0]
totalScoreBArray = [0]
scoreALabel.text = roundScoreA.formatted()
scoreBLabel.text = roundScoreB.formatted()
foulALabel.text = foulScoreA.formatted()
foulBLabel.text = foulScoreB.formatted()
playerABallView.frame.origin.y = CGFloat(60)
playerBBallView.frame.origin.y = CGFloat(60)
getScoreArray.removeAll()

}


//建立重置UIAlert提示
@IBAction func reset(_ sender: Any) {

let controller = UIAlertController(title: "重置比賽", message: "確定要重置比賽", preferredStyle: .alert)
let okAction = UIAlertAction(title: "確定", style: .default){ _ in
self.resetGame()
}
controller.addAction(okAction)
let cancelAction = UIAlertAction(title: "取消", style: .cancel)
controller.addAction(cancelAction)
present(controller, animated: true)

}

作品程式碼

--

--