> 原文:http://www.jianshu.com/p/1fdb34225bef
作者:[PMST](http://www.jianshu.com/users/596f2ba91ce9/latest_articles)
Lecture08課程結束,我們已經走過了90%,剩下的10%是對游戲體驗的改進罷了。就比如,剛啟動游戲,“Player”就出現在屏幕中Flap一下翅膀,然后還沒等用戶清楚這個游戲是什么情況的時候,“Player”已經墜地陣亡了。這種游戲體驗可謂是差到極致,試想一個用戶下載游戲并啟動,此時還對游戲沒有一絲認知,渴求先看看幫助說明或者玩法介紹之類吧!
因為本課程中,將剔除早前的直接進入游戲的弊端,通過添加主菜單供用戶選擇開始一次游戲亦或是查看游戲幫助說明等選項。如下:

前文已經給出了游戲狀態有如下幾種:
~~~
enum GameState{
case MainMenu
case Tutorial
case Play
case Falling
case ShowingScore
case GameOver
}
~~~
當我們開始一個游戲的時候,必須制定當前新游戲的狀態,比如是*MainMenu*顯示主菜單呢還是直接進入正題*Play*開始進行游戲。為此我們自定義一個構造函數`init(size: CGSize, gameState: GameState)`傳入gameState設置新游戲初識狀態。請添加如下兩個方法到*GameScene.swift*中的*GameScene*類中。
~~~
init(size: CGSize, gameState: GameState) {
self.gameState = gameState
super.init(size: size)
}
~~~
添加完畢之后,你會發現編譯器報錯,這也是情理之中,畢竟修改了構造方法導致早前的初始化方法都不能使用了。不急,慢慢修改。請定位到`switchToNewGame()`方法,要知道早前我們開始一個新游戲就是調用該函數,但是未指定新游戲的狀態,為此我們要大刀闊斧地小改一番...如下:
~~~
func switchToNewGame(gameState: GameState) { //改動1 添加了一個傳入參數
runAction(popAction)
let newScene = GameScene(size: size,gameState:gameState)//修改傳入參數
let transition = SKTransition.fadeWithColor(SKColor.blackColor(), duration: 0.5)
view?.presentScene(newScene, transition: transition)
}
~~~
wo ca!!這下早前所有調用`switchToNewGame()`方法的地方都報錯了。請不要著急,凡是循序漸進,首先找到`touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)`方法,這次真要大改一番了:
~~~
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//1
let touch = touches.first
let touchLocation = touch?.locationInNode(self)
switch gameState {
case .MainMenu:
//2
if touchLocation?.y < size.height * 0.15 {
//TODO: 之后添加
} else if touchLocation?.x < size.width * 0.6 {
//3
switchToNewGame(.Tutorial)
}
break
case .Tutorial:
//TODO: 之后添加
break
case .Play:
flapPlayer()
break
case .Falling:
break
case .ShowingScore:
break
case .GameOver:
//4
if touchLocation?.x < size.width * 0.6 {
switchToNewGame(.MainMenu)
}
break
}
}
~~~
改動還是蠻大的,起碼現在需要根據你點擊的位置來執行相應的點擊事件:
1. 獲得第一個點擊,然后得到在場景中的位置Position,自然就是點Point:包括x坐標值和y坐標值了。
2. 這里我們只是簡單判斷點擊位置的范圍,比如點擊位置下偏下方時,就裝作點擊了"Learn to make this game的按鈕"。
3. 倘若通過位置判斷,你點擊了*Play*按鈕,則新建一個初始游戲狀態為`.Tutorial`的新游戲,此時并不會立刻開始游戲,而是顯示一個教程畫面,只有當再次點擊時才會開始游戲。
4. 此時處于游戲結束狀態,通過點擊OK按鈕開啟一個新游戲,但是游戲狀態為.Menu。
此時還有個報錯來自于"GameViewController.switf文件",請找到`let scene = GameScene(size:CGSizeMake(320, 320 * aspectRatio))`這一行,改為我們定義的構造方法`let scene = GameScene(size:CGSizeMake(320, 320 * aspectRatio),gameState:.MainMenu)`即可。
點擊運行,我去!! 咋不靈了.....
貌似`didMoveToView()`方法中 我們并沒有根據游戲初始狀態來初始化游戲場景...請轉到`GameScene`類中,定位到`didMoveToView()`,將其中內容替換成如下內容:
~~~
override func didMoveToView(view: SKView) {
physicsWorld.gravity = CGVector(dx: 0, dy: 0)
physicsWorld.contactDelegate = self
addChild(worldNode)
// 以下為替換內容
if gameState == .MainMenu {
switchToMainMenu()
} else {
switchToTutorial()
}
}
//MARK: Game States
//添加剩余兩個場景切換方法
func switchToMainMenu() {
gameState = .MainMenu
setupBackground()
setupForeground()
setupPlayer()
setupSomebrero()
//TODO: 實現setupMainMenu()主界面布局 之后把注釋去掉
}
func switchToTutorial() {
gameState = .Tutorial
setupBackground()
setupForeground()
setupPlayer()
setupSomebrero()
setupLabel()
//TODO: 實現setupTutorial()教程界面布局 之后把注釋去掉
}
~~~
其中我們還未實現對主界面的布局,以及教程界面的布局,這也是接下來所要干的事了。
**實現主界面的布局:**
代碼貌似很長,但內容很熟悉不是嗎,當年你在配置ScoreCard界面的時候不也這么做過?先布局幾個button,然后執行幾個動畫罷了,請邊碼邊回憶是怎么對精靈位置放置,添加動作的。
~~~
func setupMainMenu() {
let logo = SKSpriteNode(imageNamed: "Logo")
logo.position = CGPoint(x: size.width/2, y: size.height * 0.8)
logo.zPosition = Layer.UI.rawValue
worldNode.addChild(logo)
// Play button
let playButton = SKSpriteNode(imageNamed: "Button")
playButton.position = CGPoint(x: size.width * 0.25, y: size.height * 0.25)
playButton.zPosition = Layer.UI.rawValue
worldNode.addChild(playButton)
let play = SKSpriteNode(imageNamed: "Play")
play.position = CGPoint.zero
playButton.addChild(play)
// Rate button
let rateButton = SKSpriteNode(imageNamed: "Button")
rateButton.position = CGPoint(x: size.width * 0.75, y: size.height * 0.25)
rateButton.zPosition = Layer.UI.rawValue
worldNode.addChild(rateButton)
let rate = SKSpriteNode(imageNamed: "Rate")
rate.position = CGPoint.zero
rateButton.addChild(rate)
// Learn button
let learn = SKSpriteNode(imageNamed: "button_learn")
learn.position = CGPoint(x: size.width * 0.5, y: learn.size.height/2 + kMargin)
learn.zPosition = Layer.UI.rawValue
worldNode.addChild(learn)
// Bounce button
let scaleUp = SKAction.scaleTo(1.02, duration: 0.75)
scaleUp.timingMode = .EaseInEaseOut
let scaleDown = SKAction.scaleTo(0.98, duration: 0.75)
scaleDown.timingMode = .EaseInEaseOut
learn.runAction(SKAction.repeatActionForever(SKAction.sequence([
scaleUp, scaleDown
])))
}
~~~
**實現教程界面設置:**
反觀這個教程界面就顯得簡單多了,只需要添加一章玩法幫助的圖就ok了,如下:
~~~
func setupTutorial() {
let tutorial = SKSpriteNode(imageNamed: "Tutorial")
tutorial.position = CGPoint(x: size.width * 0.5, y: playableHeight * 0.4 + playableStart)
tutorial.name = "Tutorial"
tutorial.zPosition = Layer.UI.rawValue
worldNode.addChild(tutorial)
let ready = SKSpriteNode(imageNamed: "Ready")
ready.position = CGPoint(x: size.width * 0.5, y: playableHeight * 0.7 + playableStart)
ready.name = "Tutorial"
ready.zPosition = Layer.UI.rawValue
worldNode.addChild(ready)
}
~~~
好了,定位到`switchToMainMenu()`和`switchToTutorial()`方法,把*TODO字樣的之后方法進行調用*。
點擊運行項目,恩...出來了,而且再次點擊Play會轉到教程界面。不過再點擊的話,貌似沒反應了,聰明的你肯定會轉到`touchesBegan()`方法,定位到.Tutorial狀態,你會發現此時名下啥都沒有,怎么可能開始愉快的玩耍呢???
為此在下方添加一個`switchToPlay()`方法并在.Tutorial下調用。
~~~
func switchToPlay() {
// 從.Tutorial 狀態轉到.Play狀態
gameState = .Play
// 移除Tutorial精靈
worldNode.enumerateChildNodesWithName("Tutorial", usingBlock: { node, stop in
node.runAction(SKAction.sequence([
SKAction.fadeOutWithDuration(0.5),
SKAction.removeFromParent()
]))
})
// 開始產生障礙物 從右向左移動
startSpawning()
// 讓Player 向上蹦跶一次...
flapPlayer()
}
~~~
點擊運行項目,請盡情享受成功的果實吧!
倘若你對游戲某一部分不太熟悉,請到[github](http://www.jianshu.com/p/1fdb34225bef)下載所有課程的代碼和課件。