#Task-05 Badminton Scoreboard 羽球計分板 (上)

仿照 Tournament software 羽球比賽的 LiveScore 畫面來製作,這篇介紹比賽規則、功能需球、實際做出的介面及部分功能

在 Storyboard 製作,主要使用到的元件跟方法:

  • Label、View
  • Segmented Control
  • Button
  • 頁面間的資料傳遞
  • If/else 迴圈判斷
  • Array 陣列( append、popLast、removeAll )
  • Alert 彈窗

一樣先來看看較完整的操作畫面

前情提要:羽球比賽規則

  1. 比賽開始時雙方以擲硬幣來決定誰可以優先有選發球或場地的選擇權;另一方則行使剩餘的選擇權(如果優先方選擇發球,另一方則可選擇場地要哪一邊;如果優先方選擇場地,另一方可以選擇先發球或後發球)
    註:非正式比賽可用「向上拋球,看落地後球頭指向哪一方」或「猜拳」等方式決定球權
  2. 新制羽球比賽採「落地得分制」。當球落在對手場地的有效區域內(未出界),則我方得分。反之,當球落在我方場地的有效區域內,則對手得分
  3. 發球方若得分,依得分後的分數決定發球位置並繼續發球;若接球方的分,則接球方得到一分並同時取得發球權,一樣依得分後的分數決定發球位置。分數為奇數,站左邊發球區發球;分數為偶數,站右邊發球區發球
  4. 比賽採三戰兩勝制,每局21分
  5. 若雙方在將近終局時,同時拿到20–20的分數,則進入Deuce規則:任一方需取得兩分領先,才算得勝,例如:22–20、23–21。若比賽雙方持續Deuce至29–29時,則先取得第30分者獲勝
  6. 第一局比賽結束後,雙方應交換場地,勝者在次局比賽中為先發球方。若前兩局雙方各取得一勝一負,則進入決勝局(第三局)
    決勝局中,任一方先得到11分時,雙方需互換場地,發球者繼續發球

⭐️ 羽球計分版App功能需求
在畫面構想出來之後,覺得應該具有的功能如下:

  1. 可自行輸入名字,顯示比賽選手名稱
  2. 顯示選手站哪一邊(藍色圓形標誌)及發球方向
  3. 可選擇優先發球方
  4. 點選 change side 換邊(分數、藍色標誌及發球方向)
  5. 計時功能
  6. 目前發球方顯示標記 (黃色圓點)
  7. 左右邊選手的加分按鈕
  8. Deuce 判斷機制
  9. 比數、贏得局數顯示
  10. 點選 reset 歸 0,回到初始狀態
  11. 點選 rewind,回到上一步
  12. 點選 new match,回到輸入名字畫面
  13. 獲勝者顯示彈窗

⭐️ 羽球計分板介面

大家有看過 Tournament software 羽球比賽的 LiveScore 畫面嗎?如下左圖,右圖則是我這次做的計分板畫面

(因為想做出這樣子的畫面跟上面提到的功能,真是折騰死我了T_T,不過做完也是滿有成就感的啦)

這個作業一共用到 3 個 View Controller(自訂類別):

  • FirstpageViewController 用來輸入選手們的名字
  • MatchViewController 是計分板主要介面,待會再個別介紹功能跟程式的部分
  • AlertViewController 是用來提示誰是贏家的 Alert

⭐️ 羽球計分板功能介紹(上)

💡 輸入選手名字,資料傳遞到計分版畫面

在 FirstpageViewController 先拉 Outlet 定義輸入選手名字的 TextField 名稱

@IBOutlet weak var player1TextField: UITextField!@IBOutlet weak var player2TextField: UITextField!

Start 按鈕拉 Segue 到 MatchViewController,然後用這個 Segue 再拉IBSegueAction,撰寫資料傳遞的程式碼

//FirstpageViewController@IBSegueAction func showResult(_ coder: NSCoder) -> MatchViewController? {let player1Name = String(player1TextField.text!)let player2Name = String(player2TextField.text!)let controller = MatchViewController(coder: coder) //目的地Controllercontroller?.player1Name = player1Name 
//前面的player1Name是目的地controller中設定的變數,後面的player1Name是這裡儲存字串的常數
controller?.player2Name = player2Namereturn controller}

MatchViewController 中也要宣告儲存第一頁輸入的選手名稱的變數,才能透過 property 儲存傳送的資料

class MatchViewController: UIViewController {var player1Name: String!var player2Name: String!

開始撰寫設定初始畫面的 function initialUI

此時如果直接使用上方宣告的變數,在執行時會閃退,然後 swift 會告訴你他發現 nil,因為要等到 viewDidLoad 時 outlet 才有值,因此應該另外宣告 property 儲存接收到的資料,然後在 viewDidLoad 讀取 property 更新畫面

func initialUI(){//player1、player2儲存接收到的名稱
//系統覺得player1Name跟player2Name可能會出現空值,因此需給初始值才能執行
let player1 = player1Name ?? "Player1" let player2 = player2Name ?? "Player2"

將 initialUI() 放在 viewDidLoad 裡,才會更新畫面

override func viewDidLoad() {    super.viewDidLoad()    initialUI()}

💡 function initialUI

  • 計分板跟 Segmented Control 顯示 Player1 跟 Player2 的名字
player1NameCard.text = player1
player2NameCard.text = player2
firstServeSegment.setTitle(player1, forSegmentAt: 0)
firstServeSegment.setTitle(player2, forSegmentAt: 1)
  • Segmented Control 預設選擇的發球者為 Player1,代表著發球的黃色圓點顯示在 Player1 後方
  • 雙方分數初始值皆為 0,贏的局數的初始值也為 0
  • 抓取 Player1 跟 Player2 的名字第一個字,作為場地上藍色圓形標誌顯示的字
//將字串轉成陣列個別儲存字元let player1Array = Array(player1)
let player2Array = Array(player2)
//抓出名字首字let player1FirstWord = String(player1Array[0])
let player2FirstWord = String(player2Array[0])
//設定場上圓形標誌顯示的字,初始狀態左邊是player1,右邊是player2leftUpLable.text = player1FirstWord
leftLowerLable.text = player1FirstWord
rightUpLable.text = player2FirstWord
rightLowerLable.text = player2FirstWord
//另外儲存初始狀態圓形標誌顯示的字,用來判斷後續左右邊是哪個選手leftPlayerWord = leftLowerLable.text!
rightPlayerWord = rightUpLable.text!
  • 預設 Player1 站在左方場地,Player2 站在右方場地,因為預設發球者為 Player1,所以黃色圓點顯示在中間計分版左方,發球方向是從左下到右上(偶數位),顯示左下及右上的藍色圓形標誌
leftUpVIew.isHidden = true
leftLowerView.isHidden = false
rightUpView.isHidden = false
rightLowerView.isHidden = true
//初始狀態為左方選手發球,雙數位發球方向是左下到右上serveDirectionImageView.image = UIImage(named: "leftEven")
  • 預設顯示 Start 按鈕,隱藏 Stop 按鈕及加分按鈕

💡 Segmented Control 選擇先發球者

  • 變換黃色發球圓點及發球方向圖片
//如果選擇player1先發球,黃色點點在player1名字後方顯示if firstServeSegment.selectedSegmentIndex == 0{
player1Serve.isHidden = false
player2Serve.isHidden = true
//如果左方選手名字首字跟左邊場地上圓形標誌的字相同,代表左方選手是player1,黃色點點顯示在左方,發球方向是左下到右上;如果不同,代表左方選手是player2,黃色點點應顯示在右方,發球方向是右上到左下if leftPlayerWord == leftLowerLable.text{
leftPlayerServe.isHidden = false
rightPlayerServe.isHidden = true
serveDirectionImageView.image = UIImage(named: "leftEven")
}else if leftPlayerWord != leftLowerLable.text{
leftPlayerServe.isHidden = true
rightPlayerServe.isHidden = false
serveDirectionImageView.image = UIImage(named: "rightEven")
}//選擇player2發球同理

這篇先介紹到這裡,不然內容太多了QQ

下篇待續!!

附上 Github 作業連結

參考資料:

--

--