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: )
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 * {
//TODO: 之後添加
} else if touchLocation?.x < size.width * {
//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 * {
switchToNewGame(.MainMenu)
}
break
}
}
改動還是蠻大的,起碼現在需要根據你點選的位置來執行相應的點選事件:
- 獲得第一個點選,然後得到在場景中的位置Position,自然就是點Point:包括x坐标值和y坐标值了。
- 這裡我們隻是簡單判斷點選位置的範圍,比如點選位置下偏下方時,就裝作點選了”Learn to make this game的按鈕”。
- 倘若通過位置判斷,你點選了Play按鈕,則建立一個初始遊戲狀态為
的新遊戲,此時并不會立刻開始遊戲,而是顯示一個教程畫面,隻有當再次點選時才會開始遊戲。.Tutorial
- 此時處于遊戲結束狀态,通過點選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:, dy:)
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/, y: size.height * )
logo.zPosition = Layer.UI.rawValue
worldNode.addChild(logo)
// Play button
let playButton = SKSpriteNode(imageNamed: "Button")
playButton.position = CGPoint(x: size.width * , y: size.height * )
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 * , y: size.height * )
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 * , y: learn.size.height/ + kMargin)
learn.zPosition = Layer.UI.rawValue
worldNode.addChild(learn)
// Bounce button
let scaleUp = SKAction.scaleTo(, duration: )
scaleUp.timingMode = .EaseInEaseOut
let scaleDown = SKAction.scaleTo(, duration: )
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 * , y: playableHeight * + playableStart)
tutorial.name = "Tutorial"
tutorial.zPosition = Layer.UI.rawValue
worldNode.addChild(tutorial)
let ready = SKSpriteNode(imageNamed: "Ready")
ready.position = CGPoint(x: size.width * , y: playableHeight * + 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(),
SKAction.removeFromParent()
]))
})
// 開始産生障礙物 從右向左移動
startSpawning()
// 讓Player 向上蹦跶一次...
flapPlayer()
}
點選運作項目,請盡情享受成功的果實吧!
倘若你對遊戲某一部分不太熟悉,請到github下載下傳所有課程的代碼和課件。
此教程已接近尾聲,部落客忙于工作,文章更新速度不快,請見諒! 請期待下文對遊戲的進一步優化。