系列:用Swift作個(gè)游戲
作者:pmst(1345614869)
微博:PPPPPPMST
Lecture08課程結(jié)束望几,我們已經(jīng)走過了90%,剩下的10%是對(duì)游戲體驗(yàn)的改進(jìn)罷了萤厅。就比如橄抹,剛啟動(dòng)游戲,“Player”就出現(xiàn)在屏幕中Flap一下翅膀惕味,然后還沒等用戶清楚這個(gè)游戲是什么情況的時(shí)候楼誓,“Player”已經(jīng)墜地陣亡了。這種游戲體驗(yàn)可謂是差到極致名挥,試想一個(gè)用戶下載游戲并啟動(dòng)疟羹,此時(shí)還對(duì)游戲沒有一絲認(rèn)知,渴求先看看幫助說明或者玩法介紹之類吧禀倔!
因?yàn)楸菊n程中榄融,將剔除早前的直接進(jìn)入游戲的弊端,通過添加主菜單供用戶選擇開始一次游戲亦或是查看游戲幫助說明等選項(xiàng)救湖。如下:
前文已經(jīng)給出了游戲狀態(tài)有如下幾種:
enum GameState{
case MainMenu
case Tutorial
case Play
case Falling
case ShowingScore
case GameOver
}
當(dāng)我們開始一個(gè)游戲的時(shí)候剃袍,必須制定當(dāng)前新游戲的狀態(tài),比如是MainMenu顯示主菜單呢還是直接進(jìn)入正題Play開始進(jìn)行游戲捎谨。為此我們自定義一個(gè)構(gòu)造函數(shù)init(size: CGSize, gameState: GameState)
傳入gameState設(shè)置新游戲初識(shí)狀態(tài)民效。請(qǐng)?zhí)砑尤缦聝蓚€(gè)方法到GameScene.swift中的GameScene類中憔维。
init(size: CGSize, gameState: GameState) {
self.gameState = gameState
super.init(size: size)
}
添加完畢之后,你會(huì)發(fā)現(xiàn)編譯器報(bào)錯(cuò)畏邢,這也是情理之中业扒,畢竟修改了構(gòu)造方法導(dǎo)致早前的初始化方法都不能使用了。不急舒萎,慢慢修改程储。請(qǐng)定位到switchToNewGame()
方法,要知道早前我們開始一個(gè)新游戲就是調(diào)用該函數(shù)臂寝,但是未指定新游戲的狀態(tài)章鲤,為此我們要大刀闊斧地小改一番...如下:
func switchToNewGame(gameState: GameState) { //改動(dòng)1 添加了一個(gè)傳入?yún)?shù)
runAction(popAction)
let newScene = GameScene(size: size,gameState:gameState)//修改傳入?yún)?shù)
let transition = SKTransition.fadeWithColor(SKColor.blackColor(), duration: 0.5)
view?.presentScene(newScene, transition: transition)
}
wo ca!!這下早前所有調(diào)用switchToNewGame()
方法的地方都報(bào)錯(cuò)了。請(qǐng)不要著急咆贬,凡是循序漸進(jìn)败徊,首先找到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
}
}
改動(dòng)還是蠻大的掏缎,起碼現(xiàn)在需要根據(jù)你點(diǎn)擊的位置來執(zhí)行相應(yīng)的點(diǎn)擊事件:
- 獲得第一個(gè)點(diǎn)擊皱蹦,然后得到在場(chǎng)景中的位置Position,自然就是點(diǎn)Point:包括x坐標(biāo)值和y坐標(biāo)值了。
- 這里我們只是簡(jiǎn)單判斷點(diǎn)擊位置的范圍眷蜈,比如點(diǎn)擊位置下偏下方時(shí)沪哺,就裝作點(diǎn)擊了"Learn to make this game的按鈕"。
- 倘若通過位置判斷酌儒,你點(diǎn)擊了Play按鈕辜妓,則新建一個(gè)初始游戲狀態(tài)為
.Tutorial
的新游戲,此時(shí)并不會(huì)立刻開始游戲忌怎,而是顯示一個(gè)教程畫面嫌拣,只有當(dāng)再次點(diǎn)擊時(shí)才會(huì)開始游戲。 - 此時(shí)處于游戲結(jié)束狀態(tài)呆躲,通過點(diǎn)擊OK按鈕開啟一個(gè)新游戲,但是游戲狀態(tài)為.Menu捶索。
此時(shí)還有個(gè)報(bào)錯(cuò)來自于"GameViewController.switf文件"插掂,請(qǐng)找到let scene = GameScene(size:CGSizeMake(320, 320 * aspectRatio))
這一行,改為我們定義的構(gòu)造方法let scene = GameScene(size:CGSizeMake(320, 320 * aspectRatio),gameState:.MainMenu)
即可腥例。
點(diǎn)擊運(yùn)行辅甥,我去!! 咋不靈了.....
貌似didMoveToView()
方法中 我們并沒有根據(jù)游戲初始狀態(tài)來初始化游戲場(chǎng)景...請(qǐng)轉(zhuǎn)到GameScene
類中,定位到didMoveToView()
,將其中內(nèi)容替換成如下內(nèi)容:
override func didMoveToView(view: SKView) {
physicsWorld.gravity = CGVector(dx: 0, dy: 0)
physicsWorld.contactDelegate = self
addChild(worldNode)
// 以下為替換內(nèi)容
if gameState == .MainMenu {
switchToMainMenu()
} else {
switchToTutorial()
}
}
//MARK: Game States
//添加剩余兩個(gè)場(chǎng)景切換方法
func switchToMainMenu() {
gameState = .MainMenu
setupBackground()
setupForeground()
setupPlayer()
setupSomebrero()
//TODO: 實(shí)現(xiàn)setupMainMenu()主界面布局 之后把注釋去掉
}
func switchToTutorial() {
gameState = .Tutorial
setupBackground()
setupForeground()
setupPlayer()
setupSomebrero()
setupLabel()
//TODO: 實(shí)現(xiàn)setupTutorial()教程界面布局 之后把注釋去掉
}
其中我們還未實(shí)現(xiàn)對(duì)主界面的布局燎竖,以及教程界面的布局璃弄,這也是接下來所要干的事了。
實(shí)現(xiàn)主界面的布局:
代碼貌似很長(zhǎng)构回,但內(nèi)容很熟悉不是嗎夏块,當(dāng)年你在配置ScoreCard界面的時(shí)候不也這么做過疏咐?先布局幾個(gè)button,然后執(zhí)行幾個(gè)動(dòng)畫罷了脐供,請(qǐng)邊碼邊回憶是怎么對(duì)精靈位置放置浑塞,添加動(dòng)作的。
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
])))
}
實(shí)現(xiàn)教程界面設(shè)置:
反觀這個(gè)教程界面就顯得簡(jiǎn)單多了政己,只需要添加一章玩法幫助的圖就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字樣的之后方法進(jìn)行調(diào)用卵牍。
點(diǎn)擊運(yùn)行項(xiàng)目,恩...出來了沦泌,而且再次點(diǎn)擊Play會(huì)轉(zhuǎn)到教程界面糊昙。不過再點(diǎn)擊的話,貌似沒反應(yīng)了赦肃,聰明的你肯定會(huì)轉(zhuǎn)到touchesBegan()
方法溅蛉,定位到.Tutorial狀態(tài),你會(huì)發(fā)現(xiàn)此時(shí)名下啥都沒有他宛,怎么可能開始愉快的玩耍呢船侧??厅各?
為此在下方添加一個(gè)switchToPlay()
方法并在.Tutorial下調(diào)用镜撩。
func switchToPlay() {
// 從.Tutorial 狀態(tài)轉(zhuǎn)到.Play狀態(tài)
gameState = .Play
// 移除Tutorial精靈
worldNode.enumerateChildNodesWithName("Tutorial", usingBlock: { node, stop in
node.runAction(SKAction.sequence([
SKAction.fadeOutWithDuration(0.5),
SKAction.removeFromParent()
]))
})
// 開始產(chǎn)生障礙物 從右向左移動(dòng)
startSpawning()
// 讓Player 向上蹦跶一次...
flapPlayer()
}
點(diǎn)擊運(yùn)行項(xiàng)目,請(qǐng)盡情享受成功的果實(shí)吧队塘!
倘若你對(duì)游戲某一部分不太熟悉袁梗,請(qǐng)到github下載所有課程的代碼和課件。
此教程已接近尾聲憔古,博主忙于工作遮怜,文章更新速度不快,請(qǐng)見諒鸿市! 請(qǐng)期待下文對(duì)游戲的進(jìn)一步優(yōu)化锯梁。